MSDN Documentation Errors and Omissions

Home
Back To Tips Page

 

This summarizes a number of errors and omissions in the MSDN documentation.  Do you know of one?  I'll add it here, and even give you credit for it.  Send your bug reports to newcomer@flounder.com.  This also mentions many methods that are missing in the MFC classes.

I am not the only one to have discovered these problems; see, for example, this note from Doug Harrison.

Index (Change Log) (Latest Additions)

(Note that leading _ and __ characters are ignored in the alphabetization.  MFC methods are indexed under their class name)

A B C D E F G H I J K L M
N O P Q R S T U V W X Y Z

A

B

C

C0..9

CA

CB

CC

CD

CE

CF

CH

CI

CL

CM

CO

CP

CR

CS

CT

CW

 

C0..9

CA

CB

CC

CD

CE

CF

CH

CI

CL

CM

CO

CP

CR

CS

CT

CW

D

E

F

G

H

I

K

L

M

N

O

P

Q

R

S

T

U

V

W

X 


AcquireSRWLockExclusive

Does not state if the Slim Reader/Writer lock can be placed in shared memory (shared segment DLL, shared segment executable, or memory-mapped file) and shared across process boundaries.

Does not state what happens if a thread that has an SRW lock terminates without releasing the lock.

Does not state if this is done as a spin lock or a thread-blocking lock.

Does not state if there is any guaranteed sequencing, or that shared accesses can indefinitely starve exclusive requests, or what.

From inspection of the code: This is a spin lock with no kernel call involved.  Therefore, it should not be used to lock a region for any significant length of time.  All waiting threads are in a tight loop spinning on the lock.  There is no guaranteed sequencing of threads when the lock is released.  It can be placed in shared memory shared between processes.

AcquireSRWLockShared

Does not state if the Slim Reader/Writer lock can be placed in shared memory (shared segment DLL, shared segment executable, or memory-mapped file) and shared across process boundaries.

Does not state what happens if a thread that has an SRW lock terminates without releasing the lock.

Does not state if this is done as a spin lock or a thread-blocking lock.

Does not state if there is any guaranteed sequencing, or that shared access can indefinitely starve exclusive requests, or what.

From inspection of the code: This is a spin lock with no kernel call involved.  Therefore, it should not be used to lock a region for any significant length of time.  All waiting threads are in a tight loop spinning on the lock.  There is no guaranteed sequencing of threads when the lock is released.  It can be placed in shared memory shared between processes.

AfxThrowInvalidArgException

This documentation confusingly states

Throws an invalid argument exception.

The correct statement is

Throws a CInvalidArgException * exception.

AllocateHeap

NOTE: DO NOT CONFUSE THIS WITH HeapAlloc, WHICH IS DIFFERENT!

The AllocateHeap API describing what should be a typedef to a function. 

The correct definition is

The AllocateHeap function is a member of the SECPKG_DLL_FUNCTIONS structure and specifies a pointer to the function to be called to allocate memory.  The memory will later be freed by the function specified by the FreeHeap member of the SECPKG_DLL_FUNCTIONS structure

typedef PVOID (NTAPI LSA_ALLOCATE_LSA_HEAP) (__in ULOMG Length);

Note that because the function calling convention is NTAPI, ordinary C functions like malloc or new could not be specified.

Note that the absolutely essential calling convention type is not specified!  Note also that the input length is a ULONG, not the expected SIZE_T.

See also FreeHeap.

AllocatePhysicalPagesNuma 

The documentation as delivered is useless.  At least the MSDN documentation online is up-to-date.  But it is deeply flawed.  For example, wouldn't it make sense to tell how to free such pages?  Perhaps a hyperlink to what might be the corresponding function, FreeUserPhysicalPages?  Would it be asking too much to have such simple See-Also references?  There is also no hyperlink to AllocateUserPhysicalPages.

Alphabetical Function Reference 

This entry claims it is an alphabetical function reference to the C library, but it is a nearly-completely-empty page, except for stating what it is not.

/ANALYZE

I put this option in some VS2005 projects.  When I moved to VS2008, I find that VS2008 doesn't have this feature, doesn't support it, issues warnings if it is present, has no interface for removing it, does not allow it to be hand-edited from the command line, and I cannot find in the text .sln or .vcproj files anything that says /ANALYZE.   So I get annoying warnings.  The means of removing this option are not documented, but it turns out that you have to look for a line referencing "PreFast" and delete it.  This is, of course, obvious.  Right.

_ATL_VER

It would have been nice to have something meaningful in the description of _ATL_VER, such as the following table.  Note that none of this is helped by the total inconsistency where some values are expressed in decimal and some in hex; EVERY symbol that has a "version" meaning shall have all its version history displayed.  No exceptions.  Ever.

_ATL_VER Product release
? Visual Studio 4.0
? Visual Studio 4.1
? Visual Studio 4.2
0x0200 ‡ Visual Studio 5.0
0x0300 Visual Studio 6.0
0x0700 § Visual Studio .NET 2002
0x0710 Visual Studio .NET 2003
0x0800 Visual Studio 2005
0x0900 § Visual Studio 2008

§ A Tip of the Flounder Fin to Giovanni Dicanio for this information

§ A Tip of the Flounder Fin to David Wilkinson for this information

A Tip of the Flounder Fin to David Scambler for this information

ASSERT

The ASSERT macro is missing an absolutely critical piece of information in its Remarks section:

The ASSERT macro will reset the value found by ::GetLastError to 0 (ERROR_SUCCESS).  Therefore, if the last error value is needed, it must be obtained before the ASSERT macro appears, because otherwise, in debug mode, this critical piece of information will be lost.  For example, note the following two incorrect examples and the correct example which follows them

BOOL result = AnyAPICall(...);
if(!result)
   { 
     ASSERT(FALSE);
     MyReportError(GetLastError());  // WILL ALWAYS REPORT ERROR_SUCCESS IN DEBUG MODE!
   }
BOOL result = AnyAPICall(...);
ASSERT(result);
if(!result)
    {
     MyReportError(GetLastError()); // WILL ALWAYS REPORT ERROR_SUCCESS IN DEBUG MODE!
    }

Correct code:

BOOL result = AnyAPICall(...);
if(!result)
    {
     DWORD error = GetLastError();
     ASSERT(FALSE);
     MyReprotError(error);
    }

The same problem exists with VERIFY.

AVIIF_KEYFRAME

If both AVIRIFF.h and vfw.h are included in a build, this symbol will produce a "duplicate macro definition" warning because the files are poorly-written, interacting, have an unfortunate sequentiality on their inclusion caused by hopelessly inadequate management of the header files, and just generally represent A Prime Example Of Worst Practice,

If both files are included, vfw.h must be included first.  For details see AVIRIFF.h,

AVIIF_LIST

If both AVIRIFF.h and  vfw.h are included in a build, this symbol will produce a "duplicate macro definition" warning because the files are poorly-written, interacting, have an unfortunate sequentiality on their inclusion caused by hopelessly inadequate management of the header files, and just generally represent A Prime Example Of Worst Practice,

If both files are included, vfw.h must be included first.  For details see AVIRIFF.h,

AVIIF_TWOCC

This symbol, which is defined for the AVIOLDINDEX structure in vfw.h, is not documented in the AVI documentation. A search of the MSDN documentation does not reveal any documentation on it. Yet it is found in existing AVI files. What are we to make of this?

AVIMakeCompressedStream

The description AVIERR_NOCOMPRESSOR might state something about what had gone wrong.  For example.

"It is not valid to assume that any particular compressor is installed.  Either a user interface should be provided that lets the user select from a list of installed compressor types, or the compressor type should be checked for existence.  The ICInfo API can be used to validate that a compressor type is valid, and it also allows for the enumeration of all the compressor types currently installed."

See Also:
ICInfo, ...


To find a list of the compressor types, I did

for(int i = 0; ; i++)
    {
     ICINFO info = { sizeof(ICINFO) };
     if(!ICInfo(0, i, &info))
         break;
     // display the info.fccType/info.fccHandler information
    }

To validate the compressor type, I did

BOOL IsValidVideoCompressor(DWORD codec)
   {
    ICINFO info = {sizeof(ICINFO) };
    if(!ICInfo(mmioFOURCC('V', 'I', 'D', 'C'), codec, &info))
       return FALSE;
    return TRUE;
   }

AVIOLDINDEXENTRY

The _avioldindex_entry.dwFlags has, in existing files, a flag value 0x00000002 which appears in real files but it is not documented.  It turns out it is the AVIIF_TWOCC flag which appears in the header file but is not documented.  Note that if a flag has been made obsolete, the documentation must state "This flag is now obsolete, as of <version number here>".  That way, we know we can ignore its existence, instead of wondering why it is not documented and whether or not we should be writing it out.

AVIPALETTEENTRY

The peNew field is described as

Specifies an array of PALETTEENTRY structures, of size bNumEntries.

The correct documentation is

This array contains the number of PALETTEENTRY structures specified as bNumEntries, or, if bNumEntries is 0, the array will contain 256 PALETTEENTRY structures.

AVIRIFF.h

This header file does not contain nor is referenced by adequate documentation.  Due to fundamental design errors in the creation of this file and the related vfw.h file, you can get serious compilation errors if you include them in the wrong order.  It needs to have this information added:

"If this file is included with vfw.h, then vfw.h must be included first.  Due to an error in the way these files were constructed, AVIRIFF.h  defines symbols which are either not defined in AVIRIFF.h or would result in "macro redefinition" errors if AVIRIFF.h is included first.  This is due to erroneous attempts to duplicate symbols instead of using a common header file that includes all necessary information."

It generates an error of duplicate macro definitions. The sad thing is that the conditionals could work both ways, and it is worth pointing out that the definitions do not need to be different:

AVIRIFF.h line 192 (VS2008)

#ifndef AVIIF_LIST
#define AVIIF_LIST 0x00000001
#define AVIIF_KEYFRAME 0x00000010
#endif

vfw.h

#define AVIIF_LIST 0x00000001L
#define AVIIF_TWOCC 0x00000002L
#define AVIIF_KEYFRAME 0x00000010L

Note also that the AVIIF_TWOCC flag is not defined in AVIRIFF.h, but is defined in vfw.h. Why do we have two conflicting files, anyway?

Note that the AVIIF_TWOCC flag is not documented in the VS2008 documentation.

AVISTREAMHEADER

The documentation for AVISTREAMHEADER appears to be missing from the VS2008 documentation.

The 'strh' data structure is said by the (non-VS2008)  AVI documentation as having its data element

"consists of of an AVISTREAMHEADER structure"

But this is not correct. The format of a strh structure is

'strh' len data

but the documentation would suggest that the data portion is the AVISTREAMHEADER. This is not true. The strh chunk is itself an AVISTREAMHEADER. It is not even a complete one, either.

For example, the AVISTREAMHEADER structure is 64 bytes long. The cb field is specified as counting the number of bytes not including the 'strh' (FOURCC fcc) and length (DWORD cb) fields, which is consistent with the layout of the RIFF chunk.

64 - (sizeof(FOURCC) + sizeof(DWORD) => 64 - 8 => 56. 

Even accounting for the first two fields, the 'fcc' and 'cb' fields, which actually overlap the AVI header information (so the data part actually starts at the fccType field), I have seen AVI files in which only 48 bytes of data are provided, that is, the rcFrame data is completely missing. This fact is not actually documented in the MSDN online AVI documentation, which is therefore misleading.

Are we at all surprised by this?

The correct documentation would add

"When the AVISTREAMHEADER structure appears in a RIFF file, parts of the structure overlap the chunk type and length information of the RIFF data chunk.  Therefore, the structure offset is slightly skewed relative to the start of the data component of the RIFF chunk, as shown in the table below."

data field offset structure offset Field Description
-8 0 FOURCC fcc; mmioFOURCC('s', 't', 'r', 'h'); overlaps with the chunk ID of the RIFF file
-4 4 DWORD cb; Count of bytes following the cb field; overlaps with the chunk length of the 'strh' record.
0 8 FOURCC fccType Type of information
4 12 FOURCC fccHandler Codec handler for the data in the stream
8 16 DWORD dwFlags Flag bits
12 20 WORD wPriority (as in existing documentation)
14 22 WORD wLanguage (as in existing documentation)
16 24 DWORD dwInitialFrames Number of frames preceding the frames in this stream.
20 28 DWORD dwScale (as in existing documentation)
24 32 DWORD dwRate (as in existing documentation)
28 36 DWORD dwScale (as in existing documentation)
32 40 DWORD dwStart (as in existing documentation)
36 44 DWORD dwLength # of frames in this stream
40 48 DWORD dwSuggestedBufferSize (as in existing documentation)  Also, why is there no guidance as to how to set this (e.g., "if 0 is provided, a buffer size appropriate for the data will be used", if that is how it works)
44 52 DWORD dwQuality One of ICQUALITY_LOW (0), ICQUALITY_HIGH (10000), or ICQUALITY_DEFAULT ((DWORD)-1)
48 56 DWORD dwSampleSize  
  struct { When this structure appears in a RIFF data 'strh' chunk, the following fields may be absent.  Therefore, you must check the cb field and not attempt to use fields which may not be present.  To attempt to use them will result in getting the RIFF file contents out-of-sync with the actual information in the file, leading to program failure.
48 56    short left; (as in existing documentation)
50 58    short top; (as in existing documentation)
52 60    short right; (as in existing documentation)
54 62    short bottom; (as in existing documentation)
  } rcFrame;  

Note that there is no documented guidance on how to fill in many of these fields.  

AVIStreamSetFormat

This is a typical example of the vague and confusing documentation which is becoming so common.

It states "The handler for writing AVI files does not accept format changes".  What are we to make of this?  That you cannot call this API for AVI files?  Then why does it exist?  There is no way to reconcile this statement with the existence of the API itself!

For example, the lpFormat parameter is said to be a "Pointer to a structure containing the new format".  Whatever that means.  A code example shows that it is a pointer to a BITMAPINFO structure, which none of the AVI documentation even suggests.  How it could possibly be evident to anyone reading the documentation that this is the correct object to point to is beyond comprehension.  Nothing I have found suggests what this parameter should point to, or what possible types these values could be.

In the Remarks section, the error code AVI_BADFORMAT should be specified as one of the return values, and one of the explanations should be that "This may indicate that the bits-per-pixel established for the stream and set in the BITMAPINFO structure are incompatible with the codec selected."

BASED_CODE

The documentation for this macro states (correctly) that it has no meaning in Win32.  However, it still appears in various examples, and its usage must be expunged in all cases.

_beginthread/_beginthreadex 

The Remarks section says that _beginthread returns 1L if there is an error.  This is incorrect, as can be readily demonstrated by trying to create a few hundred threads.  When _beginthread fails, it returns (uintptr_t)1L.

These calls are unbelievably irresponsibly documented in the latest documentation.  The erroneous documentation is

uintptr_t _beginthread( 
   void(*start_address )( void * ),
   unsigned stack_size,
   void *arglist 
);
uintptr_t _beginthreadex( 
   void *security,
   unsigned stack_size,
   unsigned (*start_address)( void * ),
   void *arglist,
   unsigned initflag,
   unsigned *thrdaddr 
);
This documentation ignores the fact that there is an explicit linkage type required.  In previous versions of the documentation, the correct documentation was given as

uintptr_t _beginthread( 
   void( __cdecl *start_address )( void * ),
   unsigned stack_size,
   void *arglist 
);
uintptr_t _beginthreadex( 
   void *security,
   unsigned stack_size,
   unsigned ( __stdcall *start_address)( void * ),
   void *arglist,
   unsigned initflag,
   unsigned *thrdaddr 
);

Note that these both explicitly state, as part of the syntax of the prototype, the fact that there is a linkage type on the function.

Due to what appears to be either terminal laziness or a complete misunderstanding of how readers use documentation, when managed code added the _clrcall linkage, the linkage type was eliminated from the specification and moved to the commentary code that follows.  The correct documentation in modern terms should  have been the form shown below.

uintptr_t _beginthread(                        // NATIVE CODE
   void( __cdecl *start_address )( void * ),
   unsigned stack_size,
   void *arglist 
);
uintptr_t _beginthread(                        // MANAGED CODE
   void( __clrcall *start_address )( void * ),
   unsigned stack_size,
   void *arglist 
);

uintptr_t _beginthreadex(                      // NATIVE CODE
   void *security,
   unsigned stack_size,
   unsigned ( __stdcall *start_address)( void * ),
   void *arglist,
   unsigned initflag,
   unsigned *thrdaddr 
);
uintptr_t _beginthreadex(                      // MANAGED CODE
   void *security,
   unsigned stack_size,
   unsigned ( __clrcall *start_address)( void * ),
   void *arglist,
   unsigned initflag,
   unsigned *thrdaddr 
);

This is the simple and obvious way to document it correctly.  There is NO EXCUSE for the current misleading, erroneous, confusing, and ultimately incorrect form of the prototypes.  I have witnessed several failures caused by people expecting the prototypes to accurately reflect the actual prototypes, not be some kind of lazy documenter's excuse for writing poor documentation.  Saving a few lines of text and/or a few tens of bytes to produce the incredibly poor documentation we now have for these functions is inexcusable.

_beginthread example code

This illustrates one of the singular poorest styles of thread coding that can possibly exist!  It calls _endthread to terminate the thread.  This is a practice so egregiously poor that it must never, ever be encouraged; in fact, the documentation of ExitThread and _endthread and _endthreadex and AfxEndThread should carry large warning labels:

WARNING: THIS FUNCTION SHOULD NEVER BE USED IN ANY REAL PROGRAM!  TO CALL IT OPENS THE POSSIBILITY OF SERIOUS PROGRAM MALFUNCTION, LOST STORAGE, AND GENERALLY THE POTENTIAL FOR CATASTROPHIC FAILURE!  THE ONLY WAY TO EXIT A THREAD IS TO RETURN FROM THE TOP-LEVEL THREAD FUNCTION!

Even the simplest cases fail catastrophically.  Consider

UINT ThreadFunc(LPVOID p)
   {
     std::vector<int> values;
      .... thread code, does lots of values.push_back calls
     _endthreadex(0);
     return 0;
    }

When _endthreadex is called (and this presumes that the thread was created with _beginthreadex, and this should not actually be of any concern to the thread!), the thread terminates.  Did you see the destructor for the std::vector getting called?  Of course not!  So you have a storage leak.

All other scenarios are much worse!  The use of _endthread, _endthreadex, AfxEndThread, and ExitThread should be actively discouraged!  They should never be used in examples anywhere for any reason whatsoever!

Upon further study, there are so many things wrong with the code that it is unacceptable.  Here is a side-by-side comparison of the existing code with the rewrite.

Original code download.gif (1234 bytes)Revised code
It is worth pointing out that these will normally be found in the stdafx.h file stdafx.h
 
#include <windows.h>
#include <process.h>    /* _beginthread, _beginthreadex */
#include <stddef.h>
#include <stdlib.h>     // _threadid
#include <conio.h>      // _getch 
// crt_BEGTHRD.C
// compile with: /MT /D "_X86_" /c
#include <windows.h>
#include <process.h>    /* _beginthread, _endthread */
#include <stddef.h>
#include <stdlib.h>     // _threadid
#include <conio.h>      // _getch 
#include <stdafx.h>
void Bounce( void *ch );
void CheckKey( void *dummy );
 
It is well-known that for small intervals this does not produce a good random number selection.  The low-order bits are not particularly "random".  In fact, the lowest-order bit usually alternates from 0 to 1
/* GetRandom returns a random integer between min and max. */
#define GetRandom( min, max ) ((rand() % (int)(((max) + 1) - (min))) + (min))
/* GetRandom returns a random integer between min and max. */
#define GetRandom( min, max ) (( (rand() >> 8) % (int)(((max) + 1) - (min))) + (min))
This should, strictly speaking, be volatile because there is actually no requirement that it be treated consistently with multithreading.  In addition, the comment is nonsense; it has nothing to do with video.  The name is very non-mnemonic, so I chose to rename it to something meaningful.
BOOL repeat = TRUE;     /* Global repeat flag and video variable */
BOOL running = TRUE;     
There is no particular reason that this variable should be global I eliminated the variable because it has no reason to be global
HANDLE hStdOut;         /* Handle for console window */
 
  We will need an event object that tells us if all the running threads have terminated.  This handle represents that event.
HANDLE Complete;
It is also not clear why this variable needs to be global, but I left it as global
CONSOLE_SCREEN_BUFFER_INFO csbi;    /* Console information structure */
CONSOLE_SCREEN_BUFFER_INFO csbi;    /* Console information structure */
It may come as a surprise, but main takes two arguments.  In addition, the use of main is very retro, and not Unicode-aware.  Time to upgrade it.
int main()
{
int _tmain(int argc, _TCHAR* argv[])
{ 
 UNREFERENCED_PARAMETER(argc)
 UNREFERENCED_PARAMETER(argv)
There is no need for this variable here because of the restructuring.
  CHAR    ch = 'A';
 
  This can be a local variable; anyone who needs it can ask for it again.
   hStdOut = GetStdHandle( STD_OUTPUT_HANDLE );
    HANDLE hStdOut = GetStdHandle( STD_OUTPUT_HANDLE );
   /* Get display screen's text row and column information. */
   GetConsoleScreenBufferInfo( hStdOut, &csbi );
   /* Get display screen's text row and column information. */
   GetConsoleScreenBufferInfo( hStdOut, &csbi );
This is nonsense.  The main thread should be the controlling thread.  In particular, this establishes a bad pattern because it suggests that in a GUI app that there should be a secondary thread which has a dialog that has a "Cancel" button, which is of course a completely flaming disaster.  Therefore, good patterns should be established by not removing the main thread of control from the main thread.  So the main thread will wait for the input from the keyboard. The key scanning is left in the main thread, and therefore the thread launch is eliminated.
    /* Launch CheckKey thread to check for terminating keystroke. */
    _beginthread( CheckKey, 0, NULL );
 
The loop that creates Bounce threads is moved to a secondary thread
    /* Loop until CheckKey terminates program. */
    while( repeat )
    {
        /* On first loops, launch character threads. */
        _beginthread( Bounce, 0, (void *) (ch++)  );

        /* Wait one second between loops. */
        Sleep( 1000L );
    }
   UINT id;
   HANDLE launch = (HANDLE)_beginthreadex(NULL, 0, Launcher, NULL, 0, &id);
The main thread now has responsibility for waiting for the indication that a key has been struck, and if so, it sets the running flag to FALSE
   _getch();
   running = FALSE;
Note that we will wait for this controlling thread to terminate.  We cannot simply exit, because the ExitProcess will force thread termination.  In a real application, the threads might have responsibility for closing files, updating databases, or taking other overt actions that must run to completion before the thread terminates, and in the original example, these threads would be killed, very likely before they completed their actions, leaving data in some inconsistent and potentially corrupt state.
   WaitForSingleObject(launch, INFINITE);
   CloseHandle(launch);
Note that although the return type of main is int, the original code has no return statement, meaning that it will not compile error-free.
 
   return 0;
} // main
} // _tmain
The comment about _endthread implied is nonsensical; it suggests that _endthread would ever have made sense. The CheckKey function is omitted entirely.
/* CheckKey - Thread to wait for a keystroke, then clear repeat flag. */
void CheckKey( void *dummy )
{
    _getch();
    repeat = 0;    /* _endthread implied */

}
 
  It is not clear why the retro char data type is being used here.  Also, it is worth documenting what is really happening
/* Bounce - Thread to create and and control a colored letter that moves
 * around on the screen.
 *
 * Params: ch - the letter to be moved
 */
/* Bounce - Thread to create and and control a colored letter that moves
 * around on the screen.
 *
 * Params: (LPVOID)(TCHAR)ch - the letter to be moved
 */
  Get rid of the retro char type.  There is no sane reason to express a space as a hex constant, so a space literal is used.  We need the stdout handle here, so instead of using a global variable, we just obtain it as needed.
void Bounce( void *ch )
{
    /* Generate letter and color attribute from thread argument. */
    char    blankcell = 0x20;
    char    blockcell = (char) ch;
    BOOL    first = TRUE;
void Bounce( void *ch )
{
    /* Generate letter and color attribute from thread argument. */
    _TCHAR    blankcell = _T(' ');
    _TCHAR    blockcell = (_TCHAR) ch;
    BOOL      first = TRUE;
    HANDLE hStdOut = GetStdHandle( STD_OUTPUT_HANDLE );
  It is considered poor style to use commas in declaration lists.
   COORD   oldcoord, newcoord;
  COORD   oldcoord;
  COORD   newcoord;
  Change the name of the repeat variable to something that makes sense
   DWORD   result;

    /* Seed random number generator and get initial location. */
    srand( _threadid );
    newcoord.X = GetRandom( 0, csbi.dwSize.X - 1 );
    newcoord.Y = GetRandom( 0, csbi.dwSize.Y - 1 );
    while( repeat )
    { 
       /* Pause between loops. */
       Sleep( 100L );
   DWORD   result;

    /* Seed random number generator and get initial location. */
    srand( _threadid );
    newcoord.X = GetRandom( 0, csbi.dwSize.X - 1 );
    newcoord.Y = GetRandom( 0, csbi.dwSize.Y - 1 );
    while( running )
       {
        /* Pause between loops. */
        Sleep( 100L );
  No change in the loop.  However, the indentation and vertical spacing before WriteConsoleOutputCharacter of the blockcell is changed to make it obvious that it is not part of the else statement.
        /* Blank out our old position on the screen, and draw new letter. */
        if( first )
            first = FALSE;
        else
         WriteConsoleOutputCharacter( hStdOut, &blankcell, 1, oldcoord, &result );
         WriteConsoleOutputCharacter( hStdOut, &blockcell, 1, newcoord, &result );

        /* Increment the coordinate for next placement of the block. */
        oldcoord.X = newcoord.X;
        oldcoord.Y = newcoord.Y;
        newcoord.X += GetRandom( -1, 1 );
        newcoord.Y += GetRandom( -1, 1 );

        /* Correct placement (and beep) if about to go off the screen. */
        if( newcoord.X < 0 )
            newcoord.X = 1;
        else if( newcoord.X == csbi.dwSize.X )
            newcoord.X = csbi.dwSize.X - 2;
        else if( newcoord.Y < 0 )
            newcoord.Y = 1;
        else if( newcoord.Y == csbi.dwSize.Y )
            newcoord.Y = csbi.dwSize.Y - 2;

        /* If not at a screen border, continue, otherwise beep. */
        else
            continue;
        /* Blank out our old position on the screen, and draw new letter. */
        if( first )
           first = FALSE;
        else
           WriteConsoleOutputCharacter( hStdOut, &blankcell, 1, oldcoord, &result );
         
        WriteConsoleOutputCharacter( hStdOut, &blockcell, 1, newcoord, &result );

        /* Increment the coordinate for next placement of the block. */
        oldcoord.X = newcoord.X;
        oldcoord.Y = newcoord.Y;
        newcoord.X += GetRandom( -1, 1 );
        newcoord.Y += GetRandom( -1, 1 );

        /* Correct placement (and beep) if about to go off the screen. */
        if( newcoord.X < 0 )
            newcoord.X = 1;
        else if( newcoord.X == csbi.dwSize.X )
            newcoord.X = csbi.dwSize.X - 2;
        else if( newcoord.Y < 0 )
            newcoord.Y = 1;
        else if( newcoord.Y == csbi.dwSize.Y )
            newcoord.Y = csbi.dwSize.Y - 2;

        /* If not at a screen border, continue, otherwise beep. */
        else
            continue;
Given that the character is already in blockcell, there is no reason to re-cast the input parameter again
        Beep( ((char) ch - 'A') * 100, 175 );
    }
       Beep( (blockcell - 'A') * 100, 175 );
      }   
Using _endthread is unnecessary because it suggests that it could ever possibly make sense.  It is not "redundant" because "it is only going to _endthread when it returns".  This presumes that we know that this thread was created by _beginthread, and if we change its creation to _beginthreadex, this code is wrong.  See the discussion earlier about C++ and destructors.  It is essential for correct and robust behavior that _endthread never be called. The problem is that we have to notify the caller that the thread has terminated.  While we could have done an array of HANDLE values, it would not work correctly because we would be limited to 64 threads, and we don't want to impose that kind of limitation.  Therefore, we keep a count of the number of active threads.  When the count goes to 0, there are no more threads running, and we can call SetEvent to allow the spawning thread to resume execution.  This thread will exit, and the main thread is waiting for the launcher to terminate so it can terminate the process.

I decided it also looked nicer if the thread erased its character when it completed.

    /* _endthread given to terminate */
    _endthread();
}
    if(!first)
       WriteConsoleOutputCharacter( hStdOut, &blankcell, 1, oldcoord, &result );
    LONG count = InterlockedDecrement(&threadcount);
    if(count == 0)
       SetEvent(Complete);
}
Instead of launching the bounce threads in the main application thread, a secondary thread is created that launches them. 

We get rid of the retro char and go for _TCHAR

UINT __stdcall Launcher(LPVOID dummy)
    {
     _TCHAR    ch = _T('A');
It keeps count of the number of threads it launches.  As the threads terminate, they decrement this count; when the count goes to 0, the Complete event will be set.  We need to create that event
     Complete = CreateEvent(NULL, TRUE, FALSE, NULL);
Before we attempt to create a thread, we increment the count of running threads.  If the creation fails, we decrement that count.  In this case, future attempts to create threads are unlikely to succeed, so we exit the creation loop and then wait for the threads to complete.  Otherwise, we keep creating threads until the user chooses to stop thread creation.

Note that this also compensates for the error in the documentation that erroneously states that the return value of _beginthread is 1L if there is an error.

The original code was erroneous in that in simply kept incremented the ch variable without testing to see if it went out of range.  After creating 63 threads, the variable no longer has a valid printable representation.  This code has been modified to allow it to remain valid no matter how many threads are created.  Note that in one experiment, after slightly over ½ hour, the program had created 1961 threads before hitting the error condition.

     while(running)
         { /* start another thread */
          InterlockedIncrement(&threadcount);
          uintptr_t result =_beginthread(Bounce, 0, (LPVOID)ch);
          if(result == (uintptr_t)-1)
             { /* can't create */                
              InterlockedDecrement(&threadcount);
              break;                             
             } /* can't create */                
          ch++;
          if(!isprint(ch)) 
              ch = _T('A');
          Sleep(1000);
         } /* start another thread */
If we find that running is FALSE, we exit the loop.  At this point, we have some number, possibly hundreds, of threads running.  We must wait for them all to complete.  Note that as each thread terminates, it decrements the count of active threads, threadcount, and when this goes to 0, we know all the threads have terminated, and the Complete event is set.  The launcher thread will resume execution, and return, thus terminating this thread.  The main thread, having initiated the shutdown, is waiting for this thread to terminate, so it can finally resume execution and exit the program.
     WaitForSingleObject(Complete, INFINITE);
     return 0;

BITMAP

The bmType field is specified as "This member must be zero".  Under what conditions? Certainly it shouldn't suggest that this member be initialized to 0 before calling, say, GetObject.  Perhaps it should say "When creating a bitmap object with CreateBitmapIndirect, this value must be 0.  It will be set to 0 by the GetObject call". 

Similarly, the description of bmBits is incorrect.  It says "pointer the location of the bit values of the bitmap".  This is not quite true.  The correct documentation is, :"On a GetObject call, this field is set to NULL.  When the BITMAP object is being used to create a bitmap using CreateBitmapIndirect, this is a pointer to the values which will become the bits of the bitmap."

The documentation explains that the width should be multiples of 2 bytes by giving the bizarre explanation

"In other words, bmWidthBytes * 8 must be the next multiple of 16 greater than or equal to the value obtained when the bmWidth member is multiplied by the bmBitsPixel member."

Why is this considered simpler than saying

"If an odd number of bytes is all that is required to hold the bits of a single horizontal scan line, an additional byte will be added so the next scan line starts on a WORD boundary.

I find the stuff in terms of multiples of 16 to be vastly harder to understand and explain than the simple, obvious statement that every scan line starts on a WORD boundary.

The documentation is really out-of-date.  It says "The bmBits member must be a long pointer to an array of character (1-byte) values".  I don't even know what this means.  What is a "long" pointer?  We are no longer in 16-bit Windows where we had "near" pointers and "far" (long) pointers, so the notation is nonsensical today.  It should be dropped (along with all instances of the keywords FAR and PASCAL, the latter replaced with whatever represents __stdcall).  And it very clearly states that the type is an LPVOID, so why it has to be a pointer to 1-byte values doesn't even make the slightest sense.  If the bits are 1-bit color, 16-bit color, or 24-bit color, they would not be "an array of bytes", they would be "an array of image data".

It says "each scan is a multiple of 32 bits".  What's a "scan"?  It should say "The pixels are interpreted as a sequence of horizontal rows of image data, and each row starts on a DWORD-aligned boundary".  Note there is a fundamental error here.  The bmWidthBytes parameter says that the value must be a multiple of 2 because the bit values of a bitmap form an array that is word aligned" [my emphasis] but the description in the Remarks section says that each scan is a multiple of 32 bits, which means it is DWORD aligned!  Which is correct?  I have no idea.

In my VS2008 documentation, this structure is omitted from the index.  In addition, once found (via "search"), there is no hyperlink to GetObject.

My documentation states

"The pixels on a monochrome device are either black or white. If the corresponding bit in the bitmap is 1, the pixel is turned on (white). If the corresponding bit in the bitmap is 0, the pixel is turned off (black)."

Should say

"A monochrome bitmap is represented by 1-bit pixels. A pixel that has the value 0 is displayed using the first color of the selected color palette and a pixel that has the value 1 is displayed using the second color of the selected color palette. The default palette used has black for color 0 and white for color 1.  In 24-bit or 32-bit color mode, a 0 is represented by a black pixel and a 1 is represented by a white pixel.  If a paletted DIB is converted to a DDB, the colors used for 0 and 1 are as with rendering into a palleted DC: the 0 bit is displayed as the first color in the palette and the 1 bit is displayed as the second color in the palette."

(4-Aug-08) I was sent the following note, which is quite interesting:  This email is reproduced with the permission of the author

This is a real issue. I don't know how much of the history you know, so I'll go back to the beginning.

The concept of the BITMAP goes back to Windows 1.0. At that time, all bitmaps were created by a driver: either the display driver or the printer driver. There were two formats: monochrome, and a color format that matched current display surface. The format of the bits in a color bitmap was supposed to be opaque to applications: the BITMAP format was whatever was convenient for the display or printer driver. Most used something sensible, but some were very strange indeed.

Because the driver interface required it, scanlines were aligned to a word (2-byte) boundary, a scan line was not allowed to cross a 64k boundary, and the top scanline was first. The NT BITMAP code never enforced the 64k requirement, although Windows 9X kept that limitation until it died.

OS/2 did things a little differently. They created the concept of the DIB (Device Independent Bitmap) -- a bitmap whose format was defined by contract. Scan lines in a DIB were padded to a DWORD (4-byte) boundary and were, in OS/2's inscrutable fashion, defined with the bottom scan line first.

Windows 3.0 absorbed the OS/2 DIB concept (as the BITMAPINFOHEADER), and the existing bitmap model was renamed Device-Dependent Bitmap. DDBs were top-down, WORD-aligned, and DIBs were bottom-up, DWORD-aligned. The code that copied DIBs to and from the screen was contained separately within each and every display driver, and the code almost universally sucked. Because of that, and because 99% of the graphics cards in the world used 8-bit-per-pixel palettized surfaces, application writers shunned DIBs and used DDBs, making the dangerous assumption that all DDBs were 8-bit.

As time went on, Microsoft provided the DIB engine that let app writers start using DIBs in real apps without losing performance. Today, it is very rare to find an app that uses a DDB.

At some point, Microsoft realized that monochrome DDBs and monochrome DIBs served the same purpose and duplicated code stupidly. They quietly changed the monochrome DDB definition to DWORD padding, so that a monochrome DDB and a monochrome DIB could use the same code.

So, you have a very strange melange today. A BITMAP is a DDB. The scan lines are top-down. A color DDB has word-aligned scans, and a mono DDB has DWORD-aligned scans. A BITMAPINFOHEADER is a DIB; the scan lines are bottom-up, and are DWORD-aligned. Making things even more confusing, an HBITMAP can refer to either one.

No wonder we all want to be web programmers instead of GUI programmers.

--
Tim Roberts, timr@probo.com
Providenza & Boekelheide, Inc.
 

Bitmap::FromHBITMAP

This contains a very strange statement: "Do not pass to the FromHBITMAP method a GDI bitmap or a GDI palette that is currently (or was previously) selected into a device context"

There is no justification given for this statement, and it raises the question "How do I get the bitmap data filled in?" since the normal way to create a bitmap is to select it into a DC, draw to it, deselect it, and then have a bitmap that has data in it.  How, exactly, is the data supposed to be created in the bitmap in the first place?  Presumably there is some secret known to the folks at Microsoft as to how we get the data into the bitmap, but it escapes me what it might be.

Like most of the GDI+ documentation, this documentation is incomplete, confusing, and essentially useless.

BITMAPINFO

This does not contain a hyperlink, or a See Also, to BITMAPINFOHEADER or RGBQUAD.  In VS2008, the structure is not even indexed!  The See Also references are present but totally useless, probably having been pasted in from some vaguely-related API.  For example, instead of going someplace useful, the reference to "Structures, Styles, Callbacks, and Message Maps" is utterly meaningless! Under "Structures used by MFC" it lists BITMAPINFO, even though this is a structure of the raw API and has nothing to do with MFC itself. What fool wrote this? At least in previous documentation, there were useful descriptions of these structures written by adults.

The description

The bitmap has a maximum of 216 colors. The biCompression member of the BITMAPINFOHEADER must be BI_BITFIELDS. The bmiColors member contains 3 DWORD color masks which specify the red, green, and blue components, respectively, of each pixel. Bits set in the DWORD mask must be contiguous and should not overlap the bits of another mask. All the bits in the pixel do not have to be used. Each WORD in the array represents a single pixel.

should say

"If the bitmap has a maximum of 216 colors..."

and then something useful should be stated.  The rest of the description is incoherent, and I can't figure out what it is talking about. For example, what does it mean by the phrase "all the bits in the pixel do not have to be used"? What does it mean by "each WORD in the array represents a single pixel"? What in the world is this talking about?  Could it have been intended to say

"If the bitmap has a maximum of 216 colors..."

Note that 216 is a whole lot more than 216!

I think it is saying is following:

For color depth of 16 bits or fewer, colors may be represented in a compressed format by specifying the biCompression member with the value BI_BITFIELDS. This compression format indicates that the bitmap will be represented by an array of values, where the bits of each WORD represent red, green and blue components of the pixel. For color depth of more than 16 bits, the BI_BITFIELDS indicates that the bitmap will be represented by an array of values, where the bits of each DWORD represent the red, green, and blue components.

The bit fields can be of any width, and the width of the red, green and blue fields are specified by three bit masks in the bmiColors array. bmiColors[0] is a bitmask that indicates the red value, bmiColors[1] is a bitmask that indicates the green value, and bmiColors[2] is a bitmask that indicates the blue value. The fields do not need to be the same size, or in a specific order; the only requirement is that the bit masks specify contiguous, non-overlapping fields. Note that fewer bits than a WORD could be specified.

There should be an example of BI_BITFIELDS somewhere.  Note also that there does not seem to be any restriction on the order of the RGB components in the fields, that is, I could use the bitmasks

[0] 0x0000000F
[1] 0x000000F0
[2] 0x00000F00

to indicate that the low-order four bits (0xnn0..0xnnF) represent red, the next four bits (0xn0n..0xnFn) represent green, and the next four bits (0x0nn..0xFnn) represent green.  But could I also say

[0] 0x00000F00
[1] 0x000000F0
[2] 0x0000000F

or even

[0] 0x000000F0
[1] 0x00000F00
[2] 0x0000000F

as valid representations?

The description

The bitmap has a maximum of 224 colors, and the bmiColors member is NULL. Each 3-byte triplet in the bitmap array represents the relative intensities of blue, green, and red, respectively, of a pixel.

is completely incoherent! The bmiColors member is an ARRAY, and CANNOT POSSIBLY BE NULL! This is like saying

int n[4]; 

and saying "The value of n is NULL". It is complete and utter nonsense! Didn't anyone actually proofread this documentation before it was released?  But why does it say "the bitmap has a maximum of 224 colors"? This sounds like a description of 24-bit color, so in that case, the bitmap has a maximum of 16,777,216 colors!  Did it perhaps mean to say 224?  Did the writer have a clue that 224 is not the same as 224?

Similarly, by 232 colors did it mean 232?  Certainly an earlier section that talks about 256 colors doesn't mean 256

The description

The biClrUsed member of the BITMAPINFOHEADER structure specifies the number of color indices in the color table that are actually used by the bitmap. If the biClrUsed member is set to zero, the bitmap uses the maximum number of colors corresponding to the value of the biBitCount member.

is completely incoherent. If the biClrUsed member is 0, how does it know what colors to use? It sounds like if the biBitCount is 8, there are a maximum of 256 colors, but what are they? How do we know? What nonsense is this? I have no idea how to read this description or use it!

The discussions that start out "The bitmap is" are nonsensical.  They must all start by saying "If the bitmap..." Perhaps a table would encompass it better.  But in any case, the current writing is completely incoherent and should be rewritten.

The documentation suggests that the biCompression must be BI_BITFIELDS, which is complete nonsense. Having a technical writer capable of forming a coherent sentence would be a great help in this documentation.  This is not a technical problem, it is a problem of having someone who somewhere in their career learned to read and write in coherent sentences, mastered concepts such as the subjunctive mode, and generally knows how to speak and write a language (I do not exclude those for whom English is a second language; often they are better at this than native English speakers who have only one language, because their education forced them to understand fundamental language structures.  This documentation would have been incoherent in any language!)

 

BITMAPINFOHEADER

In the VS2008 documentation, this term is not even indexed!  It can only be found by "search"

There is a table which states

16 The bitmap has a maximum of 216 colors. If the biCompression member of the BITMAPINFOHEADER is BI_RGB, the bmiColors member of BITMAPINFO is NULL.
24 The bitmap has a maximum of 224 colors, and the bmiColors member of BITMAPINFO is NULL.
32 The bitmap has a maximum of 232 colors. If the biCompression member of the BITMAPINFOHEADER is BI_RGB, the bmiColors member of BITMAPINFO is NULL.

 This is completely nonsensical; the bmiColors member is an array, and thus cannot be NULL.  The correct statement in all cases is "The bmiColors member of the BITMAPINFO structure is not used"
 

The documentation of this suggests that the biCompression field is one of a limited value of small integers.  However, I have discovered empirically that when the BITMAPINFOHEADER is used as a stream specifier in an AVI file, in the

RIFF('AVI ').LIST('hdrl').LIST('strl').strf

record, the biCompression can be a FOURCC code.  I have discovered at least one case where it says 'CRAM' (mmioFOURCC('C', 'R', 'A', 'M') ) as the compression mode.  This is undocumented even in the AVI documentation.

BroadcastSystemMessage, BroadcastSystemMessageEx

Note that messages in the Registered Window Message range cannot be broadcast across desktops, because RegisterWindowMessage returns a unique message code that can be different in every desktop.

BROWSEINFO 

The documentation does not state that the pszDisplayName and lpszTitle pointers may be NULL.  The description of iImage is most politely defined as "incoherent", since it is not "a variable" but in fact is a member of the structure. The correct documentation is

pszDisplayName

Address of a buffer to receive the display name of the folder selected by the user.  The size of this buffer must be at least MAX_PATH characters.  If this pointer is NULL, the display name of the folder will not be stored.

lpszTitle

Address of a null-terminated string that is displayed above the tree control view in the dialog box.  This string can be used to specify instructions to the user.  If this pointer is NULL, no instructions will be displayed.  Note that it would be very useful if this bothered to explain if there is a length, or a multiline, restriction, on this string!

iImage

This is set to indicate the image associated with the selected folder.  The image is specified as an index to the system image list.  And where is the hyperlink so we can learn about what the system image list is, and how we can use it?  In the See Also section? 

_bstr_t::Attach 

This function can accept a NULL parameter, but the documentation does not specify what happens if NULL is used as an argument.  The implementation files frequently use Attach(0) but we cannot guess what this is doing.

_bstr_t::Assign 

The Remarks states that Assign "does a binary copy, which means the entire length of the BSTR is copied, regardless of content".  There is absolutely nothing that explains anything about what this gibberish means, nor is there a hyperlink to a description of what a BSTR actually is (for example, this implies that a BSTR, analogous to a CString, has both a "maximum buffer size" and a "current characters used".  So there is no reason to expect the reader has a clue as to what this actually means.

There are problems with the examples that are not clear and seem to contradict other documentation.

For example, near the end of the example, the statement

_snwprintf_s(bstrWrapper.GetBSTR(), 100, bstrWrapper.length(), L"changing BSTR");

It is not at all clear why this works.  For example, how was the magical value "100" chosen for the length?  Why is it meaningful?  What is the possible range of values permitted?  How do we know the buffer that is returned by GetBSTR points to a memory area that is guaranteed to have 100 characters of space? 

It is worth pointing out that this is one of those "theoretical" examples, written by someone who never actually tested the code to see if it works!  It doesn't.  This doesn't even meet Doug Harrison's "fanciful" criterion; fanciful code may be incomplete and confusing, but I believe it more-or-less works.  This never worked.

In the example, the code reads:

// new value into BSTR
_snwprintf_s(bstrWrapper.GetBSTR(), 100, bstrWrapper.length(), L"changing BSTR");
wprintf_s(L"bstrWrapper = %s\n", static_cast<wchar_t*>(bstrWrapper));
wprintf_s(L"bstrWrapper2 = %s\n", static_cast<wchar_t*>(bstrWrapper2));

Now, before the _snwprintf, the values are (from the watch windows:)

Watch Window
bstrWrapper
{"Yet another string" (1)}
_bstr_t
    m_Data
0x00a26f78 {m_wstr=0x0033742c "Yet another string" m_str=0x00000000 <Bad Ptr> m_RefCount=1 }
_bstr_t::Data_t *
    m_wstr
0x0033742c "Yet another string"
wchar_t *
    m_str
0x00000000 <Bad Ptr>
char *
    m_RefCount
1
unsigned long
bstrWrapper2
{"some text" (1)}
_bstr_t
    m_Data
0x00a26f30 {m_wstr=0x00337474 "some text" m_str=0x00000000 <Bad Ptr> m_RefCount=1 }
_bstr_t::Data_t *
    m_wstr
0x00337474 "some text"
wchar_t *
    m_str
0x00000000 <Bad Ptr>
char *
    m_RefCount
1
unsigned long

But after the _snwprintf, the values are

Watch Window
bstrWrapper
{"changing BSTR" (1)}
_bstr_t
    m_Data
0x00a26f78 {m_wstr=0x0033742c "changing BSTR" m_str=0x00000000 <Bad Ptr> m_RefCount=1 }
_bstr_t::Data_t *
    m_wstr
0x0033742c "changing BSTR"
wchar_t *
    m_str
0x00000000 <Bad Ptr>
char *
    m_RefCount
1
unsigned long
bstrWrapper2
{"□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□ (1)
_bstr_t
    m_Data
0x00a26f30 {m_wstr=0x00337474 "□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□ﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮ m
_bstr_t::Data_t *
    m_wstr
0x00337474 "□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□ﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮﻮ
wchar_t *
    m_str
0x00000000 <Bad Ptr>
char *
    m_RefCount
1
unsigned long

If we look at the memory dump, the characters shown are as illustrated below (it has decoded the bytes as ANSI characters; apparently there is not a way to tell a memory dump to interpret the characters as Unicode).

Memory Window
0x00337470             fe fe fe fe fe fe fe fe fe fe fe fe     þþþþþþþþþþþþ 
0x00337480 fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe þþþþþþþþþþþþþþþþ 
0x00337490 fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe þþþþþþþþþþþþþþþþ 
0x003374A0 fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe þþþþþþþþþþþþþþþþ 
0x003374B0 fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe þþþþþþþþþþþþþþþþ 
0x003374C0 fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe þþþþþþþþþþþþþþþþ 
0x003374D0 fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe þþþþþþþþþþþþþþþþ 
0x003374E0 fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe þþþþþþþþþþþþþþþþ 
0x003374F0 fe fe fe fe ee fe ee fe ee fe ee fe ee fe ee fe þþþþîþîþîþîþîþîþ 
0x00337500 ee fe ee fe ee fe ee fe ee fe ee fe ee fe ee fe îþîþîþîþîþîþîþîþ 
0x00337510 ee fe ee fe ee fe ee fe ee fe ee fe ee fe ee fe îþîþîþîþîþîþîþîþ 
0x00337520 ee fe ee fe ee fe ee fe ee fe ee fe ee fe ee fe îþîþîþîþîþîþîþîþ 
0x00337530 ee fe ee fe ee fe ee fe ee fe ee fe ee fe ee fe îþîþîþîþîþîþîþîþ 
0x00337540 ee fe ee fe ee fe ee fe ee fe ee fe ee fe ee fe îþîþîþîþîþîþîþîþ 
0x00337550 ee fe ee fe ee fe ee fe ee fe ee fe ee fe ee fe îþîþîþîþîþîþîþîþ 
0x00337560 ee fe ee fe ee fe ee fe îþîþîþîþîþîþîþîþ

It displays 64 characters of UFEFE and 64 characters of UFEEE.

Doesn't this look like free storage?  So the code as written does not work!  I therefore have no idea what the point of this dysfunctional example is.

The various statements about GetBSTR says that it "affects all _bstr_t objects that share a BSTR".  So let's look at those last few lines, commented "new value into BSTR"

_bstr_t bstrWrapper contains "Yet another string"
_bstr_t bstrWrapper2 contains "some text"

These are clearly unique strings.  Then there is the odd _snwprintf_s call that clearly changes one of them.  The resulting state of the strings is

_bstr_t bstrWrapper contains "changing BSTR"
_bstr_t bstrWrapper2 contains "some text"

Well, whoopee.  Nothing was assigned to bstrWrapper2, so why is interesting that it didn't change?  A more informative example would have assigned the same BSTR to two _bstr_t variables, and illustrated that changing one of them changed the other!

Here's a working example of the effects of sharing:

    // show non-sharing
    BSTR bstr = ::SysAllocString(OLESTR("New Shared String"));
    wprintf_s(L"Using GetAddress to show non-sharing\n");
    bstrWrapper = "Shared string";
    bstrWrapper2 = bstrWrapper;
    wprintf_s(L"bstrWrapper = %s\n", 
              static_cast(bstrWrapper));
    wprintf_s(L"bstrWrapper2 = %s\n", 
              static_cast(bstrWrapper2));
Output window
Before assignment:
    bstrWrapper = Shared string
    bstrWrapper2 = Shared string

 

Watch Window
bstrWrapper
{"New Shared String" (2)}
_bstr_t
    m_Data
0x00a56f78 {m_wstr=0x0032742c "New Shared String" m_str=0x00000000 <
_bstr_t::Data_t *
    m_wstr
0x0032742c "New Shared String"
wchar_t *
    m_str
0x00000000 %lt;Bad Ptr>
char *
    m_RefCount
2
unsigned long
bstrWrapper2
{"New Shared String" (2)}
_bstr_t
    m_Data
0x00a56f78 {m_wstr=0x0032742c "New Shared String" m_str=0x00000000 <Bad Ptr> m_RefCount=2 }
_bstr_t::Data_t *
    m_wstr
0x0032742c "New Shared String"
wchar_t *
    m_str
0x00000000 %lt;Bad Ptr%gt;
char *
    m_RefCount
2
unsigned long

Note that both variables have the same m_Data and m_wstr values, and the m_RefCount is 2.

    *bstrWrapper2.GetAddress() = bstr;  
    wprintf_s(L"bstrWrapper = %s\n", 
              static_cast(bstrWrapper));
    wprintf_s(L"bstrWrapper2 = %s\n", 
              static_cast(bstrWrapper2));
Output window
After assignment
    bstrWrapper = Shared string
    bstrWrapper2 = New Shared String

Note that the assignment created a new copy in bstWrapper2 but had no effect on the contents of bstrWrapper1

Watch Window
bstrWrapper
{"Shared string" (1)}
_bstr_t
    m_Data
0x001b6f78 {m_wstr=0x001f6f64 "Shared string" m_str=0x00000000 <Bad Ptr> m_RefCount=1 }
_bstr_t::Data_t *
    m_wstr
0x001f6f64 "Shared string"
wchar_t *
    m_str
0x00000000 <Bad Ptr>
char *
    m_RefCount
1
unsigned long
bstrWrapper2
{"New Shared String" (1)}
_bstr_t
    m_Data
0x001b6f30 {m_wstr=0x0132c84c "New Shared String" m_str=0x00000000 <Bad Ptr> m_RefCount=1 }
_bstr_t::Data_t *
    m_wstr
0x0132c84c "New Shared String"
wchar_t *
    m_str
0x00000000 <Bad Ptr>
char *
    m_RefCount
1
unsigned long

 

    // show sharing
    bstrWrapper = "Shared string";
    bstrWrapper2 = bstrWrapper;
    wprintf_s(L"bstrWrapper = %s\n",
              static_cast(bstrWrapper));
    wprintf_s(L"bstrWrapper2 = %s\n",
              static_cast(bstrWrapper2));
 
Output Window
Before assignment
    bstrWrapper = Shared string
    bstrWrapper2 = Shared string
Watch Window
bstrWrapper
{"Shared string" (2)}
_bstr_t
       m_Data
0x00bc6f78 {m_wstr=0x00087474 "Shared string" m_str=0x00000000 <Bad Ptr> m_RefCount=2 }
_bstr_t::Data_t *
       m_wstr
0x00087474 "Shared string"
wchar_t *
       m_str
0x00000000 <Bad Ptr>
char *
    m_RefCount
2
unsigned long
bstrWrapper2
{"Shared string" (2)}
_bstr_t
    m_Data
0x00bc6f78 {m_wstr=0x00087474 "Shared string" m_str=0x00000000 <Bad Ptr> m_RefCount=2 }
_bstr_t::Data_t *
    m_wstr
0x00087474 "Shared string"
wchar_t *
    m_str
0x00000000 <Bad Ptr>
char *
    m_RefCount
2
unsigned long
    bstrWrapper2.GetBSTR() = ::SysAllocString(L"New Shared String");
    wprintf_s(L"bstrWrapper = %s\n", 
              static_cast(bstrWrapper));
    wprintf_s(L"bstrWrapper2 = %s\n", 
              static_cast(bstrWrapper2));
Output Window
After assignment
    bstrWrapper = New Shared String
    bstrWrapper2 = New Shared String
Watch Window
bstrWrapper
{"New Shared String" (2)}
_bstr_t
    m_Data
0x00886f78 {m_wstr=0x0028742c "New Shared String" m_str=0x00000000  m_RefCount=2 }
_bstr_t::Data_t *
    m_wstr
0x0028742c "New Shared String"
wchar_t *
    m_str
0x00000000 <Bad Ptr>
char *
    m_RefCount
2
unsigned long
bstrWrapper2
{"New Shared String" (2)}
_bstr_t
    m_Data
0x00886f78 {m_wstr=0x0028742c "New Shared String" m_str=0x00000000 <Bad Ptr> m_RefCount=2 }
_bstr_t::Data_t *
    m_wstr
0x0028742c "New Shared String"
wchar_t *
    m_str
0x00000000 <Bad Ptr>
char *
    m_RefCount
2
unsigned long

Note that in this case, the assignment to one variable, via the GetBSTR function, affects all the variables that share that BSTR.

_bstr_t::GetAddress 

The one-line summary under "_bstr_t Class" says

GetAddress Points to the BSTR wrapped by a _bstr_t

This is confusing and misleading. The correct documentation is

GetAddress Creates an out reference to a BSTR and sets the length of the BSTR to zero; most commonly used on the left-hand side of an assignment statement.

The first definition, the one that actually is given, makes it confusing to distinguish why GetAddress differs from GetBSTR.

The same confusion occurs at the actual function definition; the Remarks section conspicuously fails to specify that the BSTR is set to have a 0-length. The remarks about the fact that it "affects all _bstr_t objects that share a BSTR" is confusing because there is no example of what this means and there is no hyperlink to any discussion of this sharing.

The badly-broken example showing how GetAddress is used, which is part of the _bstr_t::Assign discussion, shows the method is used on the left-hand-side of an assignment statement, which is not at all an obvious usage from the descriptions. Furthermore, as my example demonstrates, the use of GetAddress does not affect all the BSTRs involved!

_bstr_t::GetBSTR 

The one-line summary under "_bstr_t Class" says

GetBSTR Points to the beginning of the BSTR wrapped by the _bstr_t

It is not clear why anyone cares about the concept of "beginning of the BSTR". The correct documentation would be

GetBSTR Points to the BSTR wrapped by the _bstr_t; used when a BSTR input argument is required.

The same confusion applies in the actual function description. It contains the same gibberish about affecting all _bstr_t objects; there is no hyperlink to a discussion of this nor is there an example that illustrates why this might be a problem. The hyperlink to _bstr_t::Assign seems to contradict this. But my example, which is an extension of the badly-broken _bstr_t::Assign example, demonstrates how this actually works.

Button Styles

The documentation fails to mention how the style bits are grouped and which are mutually exclusive with each other.

The following should at least be stated:

Button styles

The button styles can be zero or more of the following styles, in combinations. Note that within each category, certain styles are mutually exclusive and at most one of the styles can be used.

Style Description
Button type Defines the appearance of the button. Select zero or one of these. If one of these is not specified, BS_PUSHBUTTON is assumed.
BS_PUSHBUTTON A classic pushbutton.
BS_DEFPUSHBUTTON The same as BS_PUSHBUTTON, but it is highlighted as if it were a default pushbutton. Note that in a dialog, changing the style does not establish the default property of the button; a DM_SETDEFID message specifying the handle of the button must also be sent to the dialog.
BS_CHECKBOX A checkbox style button with two states. These are BST_CHECKED and BST_UNCHECKED. The value BST_CHECKED shows a check-mark; the value BST_UNCHECKED does not show a check mark. Clicking on the button sends a button notification, but does not change the state of the button.
BS_AUTOCHECKBOX A checkbox style button with two states. These are BST_CHECKED and BST_UNCHECKED. The value BST_CHECKED shows a check-mark; the value BST_UNCHECKED does not show a check mark. Clicking on the button cycles between these two states.
BS_3STATE A checkbox style button, but one which has three states. The three states are BST_CHECKED, BST_UNCHECKED and BST_INDETERMINATE. The value BST_CHECKED shows a check-mark; the value BST_UNCHECKED does not show a check mark; the value BST_INDETERMINATE shows a dimmed check mark. Clicking on the button sends a button notification, but does not change the state of the button.

Note the documentation that says "the dimmed state is typically used to show that a check box has been disabled is nonsense.

BS_AUTO3STATE A checkbox style button, but one which has three states. The three states are BST_CHECKED, BST_UNCHECKED and BST_INDETERMINATE. The value BST_CHECKED shows a check-mark; the value BST_UNCHECKED does not show a check mark; the value BST_INDETERMINATE shows a dimmed check mark. Clicking on the button cycles the button state between BST_UNCHECKED, BST_CHECKED, and BST_INDETERMINATE, in that sequence.
BST_RADIOBUTTON Defines a radiobutton style control. A radiobutton has two states, BST_CHECKED and BST_UNCHECKED. The value BST_CHECKED shows a circle in the radio button indicating that it is in the checked state; the value BST_UNCHECKED shows a radio button without a circle inside it, indicating it is the unchecked state. Clicking a radio button sends a button notification to the parent, but does not change the state of the button or any other radio buttons in the radio button group.
BS_AUTORADIOBUTTON Defines a radiobutton stle control. A radiobutton has two states, BST_CHECKED and BST_UNCHECKED. The value BST_CHECKED shows a circle in the radio button indicating that it is in the checked state; the value BST_UNCHECKED shows a radio button without a circle inside it, indicating it is the unchecked state. Clicking an auto-radiobutton will send a button notification to the parent, even if the button is already in BST_CHECKED state. If the button is in BST_UNCHECKED state, the checkmark will be removed from the button in the radio button group and it will be set to BST_UNCHECKED.
BS_OWNERDRAW The button will be owner-drawn. Drawing will be handled by a WM_DRAWITEM message handler.
BS_PUSHBOX Obsolete. Existed only in 16-bit Windows code.
BS_USERBUTTON Obsolete. Existed only in 16-bit Windows code.
BS_GROUPBOX The button is a group box, a rectangle in which other buttons are displayed. Text associated with this control will always be displayed in the top border of the button. The BS_BOTTOM, BS_CENTER, BS_TOP, BS_LEFT, BS_RIGHT and BS_CENTER styles will determine its alignment.
Text alignment relative to button These styles apply to check boxes and radio buttons, and determine the relationship of the text to the control.
BS_LEFTTEXT
BS_RIGHTBUTTON
The text appears to the left of the button, This applies to all button styles except BS_GROUPBOX. Both symbols have the same value. Note that both of these symbols represent two different ways to specify exactly the same style bit.
Vertical text alignment within button Establishes the vertical text alignment within the button. If not specified, a default suitable for the button is chosen. Choose zero or one of the styles below.
BS_BOTTOM Text is vertically aligned with the bottom of the button. Cannot be combined with BS_VCENTER or BS_TOP
BS_VCENTER Text is vertically centered between the top and bottom of the button. Cannot be combined with BS_BOTTOM or BS_TOP
BS_TOP Text is vertically aligned with the top of the button. Cannot be combined with BS_BOTTOM or BS_VCENTER
Horizontal text alignment within button Establishes the text alignment within the button. If not specified, a default suitable for the button is chosen. Choose zero or one of the styles below.
BS_LEFT Text is horizontally aligned to the left of the button rectangle. Cannot be combined with BS_CENTER or BS_RIGHT
BS_CENTER Text is horizontally centered in the button rectangle. Cannot be combined with BS_LEFT or BS_RIGHT
BS_RIGHT Text is horizontally aligned to the right of the button rectangle. Cannot be combined with BS_LEFT or BS_CENTER.
Button content Defines content display. For some button styles, text is always used, and the content selection value is ignored. Choose zero or one of the styles below.
BS_TEXT The button will display text. Incompatible with BS_ICON and BS_BITMAP
BS_ICON The button will display an icon. Incompatible with BS_TEXT and BS_BITMAP
BS_BITMAP The button will display a bitmap. Incompatible with BS_ICON and BS_TEXT
Button appearance Defines border and content display. Choose zero or more of the styles below.
BS_MULTILINE Wraps the button text to multiple lines if the text is too long to fit on a single line within the button rectangle. Note that the button rectangle must be vertically tall enough to hold the text.
BS_FLAT Specifies the button is two-dimensional, and does not use any shading techniques to give a 3-D appearance
BS_PUSHLIKE Modifies a radio button, autoradio button, check box, or autocheckbox to look like a regular button. When the button is in the BST_CHECKED state, it appears to be pressed, and when it is in the BST_UNCHECKED state, it appears as an unpressed button
Button behavior Choose zero or one of the options below.
BS_NOTIFY Enables the sending of BN_DBLCLK, BN_KILLFOCUS and BN_SETFOCUS notifications tot he parent.

BYTE (Assembly directive)

Does not contain a hyperlink to the SBYTE directive. Does not contain a crosslink to the DB directive.

Does not specify that the initializer values can be in the range -128..255. Does not give the syntax for a string, or mention that a string is not implicitly NUL-terminated.

C2248: CObject::CObject

VS2005 will diagnose an error which passed earlier compilers, and issue a message of the form

c:\program files\microsoft visual studio 8\vc\atlmfc\include\afxwin.h(931) : error C2248: 'CObject::CObject' : cannot access private member declared in class 'CObject'
    c:\program files\microsoft visual studio 8\vc\atlmfc\include\afx.h(558) : see declaration of 'CObject::CObject'
    c:\program files\microsoft visual studio 8\vc\atlmfc\include\afx.h(529) : see declaration of 'CObject'
    This diagnostic occurred in the compiler generated function 'CDC::CDC(const CDC &)'

Notice that at no point does this actually refer to your code. Therefore, there is no effective procedure to determine where the error occurred. The error is indicated at the closing brace of class CDC. Shall we say that this error message is less than informative?

To add to the confusion, the "task list" only shows the first line of this file, and does not indicate what module was being compiled at the time the error occurred. This is a grotesque design error in Visual Studio.

So first, you have to go back to the Output window to get the above several lines, and to see what module is being compiled.

If you look at CObject::CObject, you will see the following lines:

	// Disable the copy constructor and assignment by default so you will get
	//   compiler errors instead of unexpected behaviour if you pass objects
	//   by value or assign objects.
protected:
	CObject(); 

Now, while the idea of having the compiler detect serious errors is a Good Idea, having it issue the particular error it does, without any way of finding the source line that caused it, is a Really, Really, Bad Idea.

So how do you find the problem?

I did "binary cut". I went to the end of my source program that was failing and added

#endif // CObject

Then I went approximately halfway back into the file, right before a procedure started, and added

#if 0 // CObject

and rebuilt it. The error still existed. So I knew it happened before that. So I went back to about 1/4 into the file, moved the #if 0 there, and rebuilt The error still appeared. I went back to about 1/8 into the file, moved the #if 0 there, and the error disappeared. So I knew it was somewhere between 1/8 and 1/4 of the distance into the file.

So I moved the #endif to the 1/4 point and compiled again, and no, the error did not occur. So I looked at the functions between the two, and only one used a CDC in any way. So I then moved the #if 0 and #endif to just inside the { } of what was the OnPaint handler, tried again. It compiled. Now, play binary cut with the body of the function. I got to one point where there was a suspicious construct and indeed it seemed to be passing the wrong parameter, which is what triggered the error. I changed it to &dc and the problem went away. The problem was caused by overload resolution if I just specified dc.

Not a fun experience, and more so because the error message is so misleading and provides nothing useful.

MFC Collections as a cause (14-Feb-09)

There's another cause, for example, I declare

class MyClass {
   public:
      CStringArray a;
};

I then add either of two declarations:

CArray<MyClass> MFCCollection;
std::vector<MyClass> STLCollection;

any attempt to use these classes, such as

STLCollection.clear();
MFCCollection.Add(MyClass());

will generate the following error message

1>c:\program files\microsoft visual studio 8\vc\atlmfc\include\afxcoll.h(593) : error C2248: 'CObject::operator =' : cannot access private member declared in class 'CObject'
1> c:\program files\microsoft visual studio 8\vc\atlmfc\include\afx.h(559) : see declaration of 'CObject::operator ='
1> c:\program files\microsoft visual studio 8\vc\atlmfc\include\afx.h(529) : see declaration of 'CObject'
1> This diagnostic occurred in the compiler generated function 'CStringArray &CStringArray::operator =(const CStringArray &)'

Now, in any intelligently-designed language and compiler, this error message would be associated with the actual cause, which is the use of the class (such as the line that says STLCollection.clear() or MFCCollection.Add(BadClass()) but that would not be any fun. So you are presented with this obscure game.

The real cause is that CStringArray does not have a public assignment operator (it is not clear why CObject has a protected assignment operator!)

The reason is that the operations are trying to assign an object of type BadClass, which means that in order to do a member assignment, it must assign a CStringArray, which is impossible because MFC collections do not have assignment operators.

The solution is to not use a CStringArray, but instead declare the class as

class MyClass {
   public:
     std::vector<CString> a;
};

Then it will work, because std::vector has a defined assignment operator.

C2440: OnNcHitTest 

If you encounter this error on a project in VS2008, it is because you have converted this project from earlier versions of VS. There is an undocumented, incompatible change in the specification of the ON_WM_NCHITTEST handler. See CWnd::OnNcHitTest for the details.

CArchive::Write

There are many reasons I detest the CArchive class, and every time I touch it I find another. The latest is that CArchive::Write wants a void * argument, where in a sane world it would take a const void * argument.

CAsyncSocket

The article KB192570 on asynchronous sockets in multiple threads is a disaster. See my rewrite.

CB_ADDSTRING 

For lParam, it should state that for a non-owner-draw control, or an owner-draw control with CBS_HASSTRINGS, a copy of the string referenced by the LPARAM is stored in the control.

CB_INSERTSTRING 

For lParam, it should state that for a non-owner-draw control, or an owner-draw control with CBS_HASSTRINGS, a copy of teh string referenced by the LPARAM is stored in the control.

CB_SETITEMDATA

The return type is specified as CB_ERR if an error occurs, but is not specified for success. Is it CB_OKAY or is it some other random value?

CBitmap::GetBitmapBits

Why does this refer to CGDIObject::GetObject to find out the number of bits when it should reference CBitmap::GetBitmap?

CButton::GetState 

The documentation sucks. It enumerates some hex constants without actually using the symbolic values that should be there. The mask 0x0003, unfortunately, is required, because there is no symbol with a reasonable name like BST_STATE_MASK, but there is no excuse for using 0x0004 in place of BST_PUSHED or 0x0008 in place of BST_FOCUS. In addition, it states that

A 0 indicates the button is unchecked. A 1 indicates the button is checked....A 2 indicates the check state is indeterminate.

This is correct, but slovenly in that it encourages the use of literal values and does not encourage the use of symbolic names. To be quality documentation, it should state

The value after masking is equal to BST_UNCHECKED if the button is unchecked, BST_CHECKED if the button is checked, and BST_INDETERMINATE if the check state is indeterminate (applies only to 3-state check boxes).

CCheckListBox::GetCheck

This states that values returned are 0, 1 or 2. The correct documentation is

Return value
BST_UNCHECKED
if not checked, BST_CHECKED if checked, and BST_INDETERMINATE if it is in the indeterminate state.

CCheckListBox::SetCheck

This states that values used should be 0, 1 or 2; the correct documentation is

nCheck
State of the check box: BST_UNCHECKED to clear it, BST_CHECKED to set it, and BST_INDETERMINATE if it is in the indeterminate state.

CCriticalSection

Like most MFC locking primitives, there is a fundamental simple principle to guide the usage of this type:

NEVER, UNDER ANY CIRCUMSTANCES IMAGINABLE, EVER, USE A SYNCHRONIZATION PRIMITIVE DEFINED BY MFC!!!!!!!!!

It's that simple.

The correction to the documentation is to label these classes as "broken, and broken by design, not to be used".

But here's the proof:

A critical section, like a mutex, has "recursive acquisition semantics", that is, if acquired more than once by the same thread, the acquisition merely increases a reference count, and there must be as many releases as acquisitions. The proof is that you can write

CRITICAL_SECTION lock;
::InitializeCriticalSection(&lock);
::EnterCriticalSection(&lock);
::EnterCriticalSection(&lock);
::LeaveCriticalSection(&lock);
::LeaveCriticalSection(&lock);
::DeleteCriticalSection(&lock);

(You may ask why you would lock twice in a row; the above code shows a logical presentation of what can be a complicated execution sequence spanning many functions). But the above code works. Now let's look at CCriticalSection in the same context:

CCriticalSection crit;
CSingleLock lock(&crit);
lock.Lock();
lock.Lock();
lock.Unlock();
lock.Unlock();

You would think this would work, but it does not and cannot be made to work. Let's look at the lock and unlock code in MFC:

BOOL CSingleLock::Lock(DWORD dwTimeOut /* = INFINITE */)
{
	ASSERT(m_pObject != NULL || m_hObject != NULL);
	ASSERT(!m_bAcquired);

	m_bAcquired = m_pObject->Lock(dwTimeOut);
	return m_bAcquired;
}

BOOL CSingleLock::Unlock()
{ ASSERT(m_pObject != NULL);
	if (m_bAcquired)
		m_bAcquired = !m_pObject->Unlock();

	// successfully unlocking means it isn't acquired
	return !m_bAcquired;
}

How many fundamental design errors can you spot in this code? Let's see, we can't pass a pointer to it to another thread because the m_bAcquired is not using any form of synchronized access. It assumes that recursive acquisition can never exist, because it assumes on a Lock call that m_bAcquired is FALSE. It is set if the lock is acquired. If it is not set, the Unlock code cannot be executed.

How, exactly, this could ever possibly make sense for a mutex, semaphore, CRITICAL_SECTION or event escapes me. It cannot make sense. For example, and I discovered this years ago, if the Event has a name and is shared between processes, each process will have its own CSingleLock object and the correlation of the m_bAcquired state and the actual state is impossible to maintain.

Had the m_bAcquired variable been eliminated, this code could be made to work, but as it stands, this code is complete crap, and must never be used (unless, of course, you have no interest in the correctness of your code).

CDC::DrawIconEx

Does not exist. Why not?

CDC::GetCurrentBitmap

Does not have a See Also to CDC::GetCurrentBrush, CDC::GetCurrentFont, CDC::GetCurrentPalette, CDC::GetCurrentPen, or CDC::GetCurrentObject.

CDC::GetCurrentBrush

Does not have a See Also to CDC::GetCurrentBitmap, CDC::GetCurrentFont, CDC::GetCurrentPalette, CDC::GetCurrentPen, or CDC::GetCurrentObject.

CDC::GetCurrentFont

Does not have a See Also to CDC::GetCurrentBitmap, CDC::GetCurrentBrush, CDC::GetCurrentPalette, CDC::GetCurrentPen or CDC::GetCurrentObject.

CDC::GetCurrentObject

Does not have a See Also to CDC::GetCurrentBitmap, CDC::GetCurrentBrush, CDC::GetCurrentFont, CDC::GetCurrentPalette, or CDC::GetCurrentPen.

CDC::GetCurrentPalette

Does not have a See Also to CDC::GetCurrentBitmap, CDC::GetCurrentBrush, CDC::GetCurrentFont, CDC::GetCurrentPen or CDC::GetCurrentObject.

CDC::GetCurrentPen

Does not have a See Also to CDC::GetCurrentBitmap, CDC::GetCurrentBrush, CDC::GetCurrentFont, CDC::GetCurrentPalette or CDC::GetCurrentObject.

CDC::GetTextExtent

This fails if executed in a DC where the SetWorldTransform has applied a transformation where the values have gone infinite (e.g., a zero-height shear of infinite width). This would not be so bad, except the code has two serious implementation problems:

The fix would certainly involve initializing the SIZE value in the function to (0, 0), and removing the VERIFY. The user should be expected to be able to respond properly to such an error. My workaround was

 BOOL ok = ::GetTextExtentPoint32(dc->m_hAttribDC, s, (int)s.GetLength(), &sz);

CDC::DPtoLP

Does not work correctly in GM_ADVANCED with scaling. See DPtoLP

CDC::ModifyWorldTransform

This does not exist, and should. It can be written as

XFORM M;
...set M
::ModifyWorldTransform(dc, &M, mode); // for CDC

or

XFORM M;
...set M
::ModifyWorldTransform(*dc, &M, mode); // for CDC*

because of the implicit CDC::operator(HDC) which will do the correct casting.

CDC::SelectClipRgn

The documentation is misleading and confusing. It says

The function assumes that the coordinates for the given region are specified in device units.

But this is not correct. The phrase "device units" suggests that the distance units are in units of MM_TEXT. But it also means the origin and x,y distances will use the coordinate system of MM_TEXT.  The correct statement is

The function uses a region that is specified in device units, in the device coordinate space; that is, any transformations applied via SetWorldTransform or other transform-matrix operations, or which use mapping modes other than MM_TEXT, are ignored.

CDC::SetGraphicsMode

This does not exist, and should. It can be written as

::SetGraphicsMode(dc, GM_ADVANCED); // for CDC

or

::SetGraphicsMode(*dc, GM_ADVANCED); // for CDC*

because of the implicit CDC::operator(HDC) which will do the correct casting.

See also SetGraphicsMode

CDC::SetTextCharExtra

The documentation is not as thorough as the raw API SetTextCharExtra in that it does not specify that if there is an error, the return value is 0x80000000, or perhaps INT_MIN. It does not specify if nCharExtra can be negative.

CDC::SetWorldTransform

This does not exist, and should. It can be written as

XFORM M;
...set M
::SetWorldTransform(dc, &M); // for CDC

or

XFORM M;
...set M
::SetWorldTransform(*dc, &M); // for CDC*

because of the implicit CDC::operator(HDC) which will do the correct casting.

CDialog::Create

This gives misleading information about how to create a dialog using a dialog ID. For example, it says

Two forms of the Create member are provided for access to the dialog-box resource by either template name or template ID number (for example, IDD_DIALOG1).

This is misleading, because it suggests that using the IDD_ number is actually the correct way to do this. It is not. The example is equally poor, showing

BOOL ret = pDialog->Create(IDD_MYDIALOG, this);

only reinforces this bad idea. The correct documentation would be

Two forms of the Create member are provided for access to the dialog-box resource by either template name or template ID number (for example, CMyDialog::IDD)

and the corrected example should be

BOOL ret = pDialog->Create(CMyDialog::IDD);

Note also that the specification says that the prototype is

virtual BOOL CDialog::Create(LPCTSTR lpszTemplateName, CWnd * pParentWnd = NULL);
virtual BOOL CDIalog::Create(UINT nIDTemplate, CWnd * pParentWnd = NULL);

It suggests that the parent window will be used. This is erroneous. The API (see CreateDialog) ignores this parameter and sets the parent to be the main window of the application. This is not documented anywhere. Note that this is not an MFC failure, but a fundamental failure of the underlying API to work as documented.

The documentation also does not have a hyperlink to CDialog::CreateIndirect.

CDialog::CreateIndirect

The documentation gives erroneous information. It specifies the methods as

virtual BOOL CreateIndirect(LPCDLGTEMPLATE lpDialogTemplate, CWnd * pParentWnd = NULL, void * lpDialogInit = NULL);
virtual BOOL CreateIndirect(HGLOBAL hDialogTemplate, CWnd * pParentWnd = NULL);

The documentation erroneously states that the window specified as the pParentWnd will be the parent of the dialog. This is not true. The parameter is ignored completely, even if it is not NULL, and the parent is always set to the main application window. The documentation lies. Note that this is not an MFC issue, but a fundamental failure of the underlying API, which is erroneously documented.

CDocument::AddView

This example is exceptionally poor, and in fact will not compile as written. The reason is that it uses a new operator on a view, and the constructor of a CView-derived class is protected. No mention of this is made. It is entirely possible this article has not been reviewed since the original 16-bit MFC release.

In addition, this covers the esoteric situation of creating a set of multiple views in an SDI app, and not the common situation of creating a second view in and MDI app. It is poorly conceived and badly written.

The more common case is adding a view to an existing document. My code for doing this will be shown below.

First, create a string to define the view. Suppose I created a Red class for my original application, and therefore had CRedView and CRedDocument. Now I wish to add a Green view. The steps are

  1. Create new CGreenView class derived from CView.
  2. Duplicate the string IDR_REDTYPE in the STRINGTABLE to create a new string as shown below

  3. IDR_RedViewTYPE  \nRed\nRed\n\n\nRed.Document\nRed Document
    IDR_GreenViewTYPE \nRed\nRed\n\n\nRed.Document\nRed Document

    Note that the contents of the string are identical because the document type is still a Red document, we are just getting a Green view of it!

  4. Create a new icon to represent your green view. Create both 32x32 and 16x16 icons, in as many color schemes as you want. Give the icon the ID IDR_GreenViewTYPE
  5. In the Window menu, change the 'New' to 'New &Red View'. Add a new menu item
  6. ID_WINDOW_NEWGREENVIEW  'New &Green View'
  7. Add a protected member variable to your CMainFrame class:

  8. CMultiDocTemplate * GreenTemplate;
  9.  In the constructor for CMainFrame, set it to NULL:

  10. GreenTemplate = NULL;
  11. In the destructor CMainFrame::~CMainFrame add

  12. delete GreenTemplate
  13. To the CMainFrame class, add a protected method

  14. CDocument * GetCurrentDocument();
  15. Examine the existing code in your CWinApp-derived OnInitInstance (you will end up doing some copy-paste of this)

  16.         pDocTemplate = new CMultiDocTemplate(IDR_RedViewTYPE,
                    RUNTIME_CLASS(CMultiViewDoc),
                    RUNTIME_CLASS(CChildFrame), // custom MDI child frame
                    RUNTIME_CLASS(CRedView));
            if (!pDocTemplate)
                    return FALSE;
            AddDocTemplate(pDocTemplate);
  17. Add a handler in your CMainFrame class for the menu item:

  18. void CMainFrame::OnWindowGreenview()
        {
         if(GreenTemplate == NULL)
            GreenTemplate = new CMultiDocTemplate(IDR_GreenViewTYPE,
                                                  RUNTIME_CLASS(CMultiViewDoc),
                                                  RUNTIME_CLASS(CChildFrame),
                                                  RUNTIME_CLASS(CGreenView));
         ASSERT(GreenTemplate != NULL);
         if(GreenTemplate == NULL)
            return; // internal error should not occur
         CMultiViewDoc * doc = (CMultiViewDoc *)GetCurrentDocument();
         ASSERT(doc != NULL);
         if(doc == NULL)
            return; // should not be possible
         CFrameWnd * frame = GreenTemplate->CreateNewFrame(doc, NULL);
         ASSERT(frame != NULL);
         if(frame != NULL)
            { /* frame created */
             frame->InitialUpdateFrame(doc, TRUE);
            } /* frame created */
        }
  19. Implement the GetCurrentDocument method

  20. CDocument * CMainFrame::GetCurrentDocument()
        {
         CMDIFrameWnd * frame = (CMDIFrameWnd*)AfxGetApp()->m_pMainWnd;
         if(frame == NULL)
            return NULL;
         CMDIChildWnd * child = (CMDIChildWnd *)frame->GetActiveFrame();
         if(child == NULL)
            return NULL;
         CView * view = child->GetActiveView();
         if(view == NULL)
            return NULL;
         CDocument * doc = view->GetDocument();
         return doc;
        } // CMainFrame::GetCurrentDocument

CEdit::GetCueBanner

The documentation misses an important description. The documentation describes only

BOOL GetCueBanner(
    LPWSTR lpwText,
    int cchText);

but it omits the critical description

CString GetCueBanner();

which returns a CString. See also EM_GETCUEBANNER.

CEvent

This class cannot be realistically used. There are so many things wrong with the implementation that it is unsalvageable.

For example, you cannot use a CSingleLock across process boundaries, or in a loop. If you leave the scope where you have locked the event, the destructor will call ::SetEvent, even though your intention would be to release the event in some other context. Essentially, this class cannot be used in any real program.

CException::GetErrorMessage

There is an error in the description of the nMaxError parameter; it should read

nMaxError

The maximum number of characters the buffer can hold, including the NUL terminator.

There is no NULL character; NULL is a pointer. The name of the character whose value is 0 is NUL. One L. All that is required to know this is fundamental literacy, since every legitimate book that talks about character values states this explicitly. For example, the character table in The Unicode Standard 3.0, page 336.

The Remarks section also talks about adding "a trailing null". No, it might say, "a trailing null character" but the correct documentation would say "a trailing NUL"

It also states that if "the buffer is too small, the error message may be truncated" (Emphasis added). This means there is a probability the message could be truncated, or perhaps it won't be. So which is it? And why can't I find out how big a buffer I should allocate? If the error message is truncated, will the function return FALSE? Does it call ::SetLastError to indicate ERROR_INSUFFICIENT_BUFFER? This method looks like it was written by summer intern. Perhaps the documentation should state

Note: GetErrorMessage will not copy more than nMaxError - 1 characters to the buffer, and will always create a NUL-terminated string value in the buffer. If the value of nMaxError is shorter than the actual error message text, the error message will be truncated, and there will be no indication that this has occurred, other than _tcslen(lpszError) == nMaxError - 1. If this is true, you should consider allocating a larger buffer and trying again.

Has anyone ever figured out why there is not a GetErrorMessageLength call to tell how large a buffer is needed, or better still, a method

virtual BOOL GetErrorMessage(CString & s, PUINT pnHelpContext = NULL)

so we don't have to create bogus fixed-size buffers and program as if we are writing K&R C code for the PDP-11?

The sad thing is that would actually have been easier to implement, in most of the handlers, than the code that is there! (Note that this could easily be done as an improvement, and the current version could create a temporary CString and call the new CString-based version, then copy the result to the LPTSTR).

CFile::CFile

(See also the documentation about why the set of flags documentation is poorly-written, misleading, and confusing)

The correct documentation of these is

CFile();
CFile(HANDLE hFile)
CFile(LPCTSTR lpszFileName, UINT nOpenFlags) throw(...)

The Remarks section starts out by making a truly absurd statement:

Because this constructor does not throw an exception, it does not make sense to use TRY/CATCH logic. Use the Open member function, then test directly for exception conditions.

This is an out-and-out lie, and therefore must be corrected. Furthermore, it is contradicted by later documentation; the third paragraph in the Remarks section says

The constructor with two arguments creates a CFile object and opens the corresponding operating-system file with the given path. This constructor combines the functions of the first constructor and the Open member function. It throws an exception if there is an error while opening the file.

Unfortunately, it fails to mention that it is a CFileException *. The correct documentation would correct this by rewriting the first paragraph to say

The constructor without parameters and the constructor that takes a HANDLE do not throw exceptions.

And changing the wording in the third paragraph to say

The constructor with two arguments creates a CFile object and opens the corresponding operating-system file with the given path. This constructor combines the functions of the first constructor and the Open member function. It throws a CFileException * if there is an error while opening the file.

The example is erroneous, and the reference to the obsolete CATCH macro should be eliminated. The example uses the obsolete char data type, and does not properly handle the fact that CFile::Write can throw an exception.

The Remarks section, and this is verified by reading the code, says explicitly

The constructor with one argument creates a CFile object that corresponds to an existing operating-system file identified by hFile. No check is made on the access mode or file type. When the CFile object is destroyed, the operating-system file will not be closed. You must close the file yourself.

However, the comments in the existing example, which creates a CFile from a pre-existing handle, state

      // We can call Close() explicitly, but the destructor would have
      // also closed the file for us. Note that there's no need to
      // call the CloseHandle() on the handle returned by the API because
      // MFC will close it for us.

which is complete nonsense. It is too bad the person who wrote these comments had not actually read the documentation!

The correct example should be

HANDLE hFile = CreateFile(_T("C:\\MyFile.DAT"),
         GENERIC_WRITE, FILE_SHARE_READ,
         NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

   if (hFile == INVALID_HANDLE_VALUE)
      AfxMessageBox(_T("Couldn't create the file!"));
   else
   {
      // Attach a CFile object to the handle we have.
      CFile myFile(hFile);

      static const TCHAR sz[] = _T("Hockey is best!");

      // write string, without null-terminator
      try {
           myFile.Write(sz, lstrlen(sz) * sizeof(TCHAR));
          }
      catch(CFileException * e)
          {
           CString msg;
           msg.Format(_T("Write failure: %d"), e->m_lOserror);
           AfxMessageBox(msg);
          }

      // Note that if we call CFile::Close, we do not need to call ::CloseHandle
      // on hFile.  But we simply leave scope, and CFile::~CFile is called, 
      // it will not close the handle, and we will have to explicitly call
      // ::CloseHandle at some later time.
      myFile.Close();
   }

CFile::modeRead, CFile::modeWrite and CFile::modeReadWrite

In an intelligently-designed world, CFile::modeReadWrite would be equal to CFile::modeRead | CFile::modeWrite. For reasons that boggle the imagination, unlike the basic ::CreateFile call where GENERIC_READ | GENERIC_WRITE can (and must) be or-ed together to allow a file to be read/write, in MFC there is a totally separate value which is used to enable for reading and writing. Of course, the documentation makes no effort to point this obvious piece of poor design out to the reader. These errors are in the discussion of CFile::CFile.

Therefore, the documentation should read

Better still, additional information should be presented as follows

In the following table, the flags are grouped according to their compatibility

Grouping Selection Options Meaning
Creation These flags are optional; none or all can be selected. CFile::modeCreate If present, the file is created; an existing file is truncated to zero length
CFile::modeNoTruncate If present, will not truncate an existing file
CFile::modeNoInherit Creates a non-inheritable file handle. If this is not specified, the file handle is inheritable
Access Choose exactly one of these CFile::modeRead Opens file for reading only. May not be combined with CFile::modeWrite
CFile::modeWrite Opens file for writing only. May not be combined with CFile::modeRead
CFile::modeReadWrite Opens file for reading and writing
Share access Choose exactly one of these CFile::shareDenyNone Allows subsequent read and write opens
CFile::shareDenyRead Allows only read opens
CFile::shareDenyWrite Allows only write opens
CFile::shareExclusive If not specified, this is the default; no subsequent opens can be made
Data mode Choose at most one of these CFile::typeText Sets text mode (used only for derived classes that use FILE *)
CFile::typeBinary Sets binary mode (used only for derived classes that use FILE *)
Low-level options Choose any of these, or all of them CFile::osNoBuffer Sets no file system caching: FILE_FLAG_NO_BUFFERING
CFile::osWriteThrough Sets the write-through option: FILE_FLAG_WRITE_THROUGH
File access hint Choose at most one of these CFile::osRandomAccess Suggests random access: FILE_FLAG_RANDOM_ACCESS
CFile::osSequentialScan Suggests sequential access: FILE_FLAG_SEQUENTIAL_SCAN

CFile::Seek

This erroneously states that it throws a CFileException if an error occurs. This is completely incorrect. It throws a CFileException * if an error occurs. Note that it is the responsibility of the frame that catches this exception to call the Delete method of the CException class to delete the exception object.

CFileDialog

This class contains in its remarks a comment that has to rank among the most embarrassing Microsoft could put into any documentation:

When the user allocates their own buffer to accommodate OFN_ALLOWMULTISELECT, the buffer cannot be larger than 2048 or else everything gets corrupted (2048 is the maximum size).

It certainly causes one to wonder what insanity could have (a) created a situation in which a user allocated buffer length of the wrong size can "corrupt everything" (whatever that means!), (b) meant that such a fundamental bug is documented, instead of having fixed it immediately upon discovery, sending out a hotfix or service pack if necessary, and (c) originally resulted in choosing such a trivial size for such an important feature!

However, Giovanni Dicanio submits:

"The buffer can be larger than 2048, as several of us noted recently"

So apparently the bug has been fixed, but now the documentation is out of date! But is there now a limit? Or is there none? Is the bug fixed, or does it simply re-occur at a larger value?

CFileDialog::CFileDialog

This is another embarrassing example of poor documentation. It shows the declaration

static char BASED_CODE filter[] = "...";

which has numerous problems. First, it uses the qualifier BASED_CODE which is not hyperlinked to any documentation explaining what it is or what it does. The lack of the hyperlink saves trying to explain why, if you do go to the documentation, it says "Under Win32, this macro expands to nothing and is provided for backward compatibility". That is to say, this proves the documentation has not been reviewed by anyone competent since the 16-bit Windows version was written.

It allows the interpretation that the following declaration would be permitted:

void CMyView::OnDoSomething()
   {
     static char BASED_CODE filter[] = "...|...|...|";
     CFileDialog dlg(TRUE, ..., filter);
     ....
     if(dlg.DoModal() != IDOK)
        return;

(Note that due to terminal brain damage on the part of the Web designers, it was impossible to copy the text, and since I read these pages on a different machine that I work on, it was too painful to keep switching back and forth to try to copy the text out, so I just used "...." to indicate the stuff I didn't want to try to retype. Even stranger, when I right-click on the example, I get a capability of "saving it to Excel", which seems remarkably stupid, but if I try it, it tells me that a Web query could not return an answer. Has anyone actually tested these Web pages to see if they make sense?)

What is even worse is that this is not a Unicode-aware example; it should have been at the very least a non-static TCHAR, and have _T() around the quoted string, but in fact it is even worse than this: it suggests that it might even make sense to declare such a variable and initialize it to a literal string! This is so remarkably against modern practice that I wonder how anyone could have allowed an example this bad into the documentation. The correct approach is to make this a STRINGTABLE entry, and do

CString filter;
filter.LoadString(IDS_EXCEL_FILTER);

because such terms as "Chart files" and "All Files" are clearly English, and to put literal English strings (or any language-of-your-choice) into a program today represents poor practice, since the string cannot be localized.

CFindReplaceDialog overview

In the overview it says

In order for the parent window to be notified of find/replace requests, you must use the Windows RegisterWindowMessage function and use the ON_REGISTERED_MESSAGE message-map macro in your frame window that handles this registered message.

Hello? WHAT MESSAGE? The text should say

In order for the parent window to be notified of find/replace requests, you must use the Windows RegisterWindowMessage function to register the message which is defined by the name FINDMSGSTRING and use the ON_REGISTERED_MESSAGE message-map macro in your frame window that handles this registered message.

Otherwise, I have to discover that this critical information is part of the Create description (I just looked in the header file to find it...)

The message will be sent only for Find Next and Cancel actions.

To detect actions other than Find Next and Cancel, you will need to subclass the CFindReplaceDialog dialog and create your own message handlers; because the wizards do not have knowledge of these conditions, you will need to hand-edit the message map to add these.

For example, to detect that the Find Whole Word option has changed, or the search text has changed, you might create a subclass CMyFindReplace You would add to the class definition

protected:
    afx_msg void OnWholeWordClicked(); 
    afx_msg void OnSearchStringChanged();
    CButton c_WholeWord;

and add to the DoDataExchange function the line

DDX_Control(pDX, chx1, c_WholeWord);

and add to the message map

ON_BN_CLICKED(chx1, OnWholeWordClicked)
ON_EN_CHANGE(edt1, OnSearchStringChanged)

(Do note that these entries do not end with a semicolon!). The symbols edt1 and chx1 are defined in the Platform SDK header file dlgs.h.

The handler for the Find Whole Word change event would then be

void CMyFindReplace::OnWholeWordClicked()
   {
    if(c_WholeWord.GetCheck() == BST_CHECKED)
       {
        ...
       }
   else
       {
        ...
       }
   }

 

CFindReplaceDialog::CFindReplaceDialog

The Remarks section is erroneous and confusing. It states

CFindReplaceDialog objects are constructed on the heap with the new operator. For more information on the construction of CFindReplaceDialog objects, see the CFindReplaceDialog overview. Use the Create member function to display the dialog box.

The correct document would say

CFindReplaceDialog objects must be constructed on the heap with the new operator. For more information on the construction of CFindReplaceDialog objects, see the CFindReplaceDialog overview. Use the Create member function to display the dialog box. Note that the PostNcDestroy handler of the CFindReplaceDialog will do a delete this, and if the object is not allocated on the heap, serious errors will result.

CFindReplaceDialog::Create

The description ends with

Within your OnFindReplace function, you interpret the intentions of the user and create the code for the find/replace operations.

Huh? How do I do this? Examine goat entrails? The correct documentation would say

Within your OnFindReplace function, you can use the FindNext and IsTerminating methods to determine which actions are being taken. The function is invoked for the Find Next and Cancel actions of the dialog. To handle other events, you would create a subclass of CFindReplaceDialog, as described in the CFindReplaceDialog overview.

The specification of the parameters is unclear. The correct specification would be

lpszFindWhat

Specifies the string for which to search. If this value is non-NULL, this string will be set as the search string when the Find/Replace dialog comes up. If this value is NULL, there will be no initial search string.

lpszReplaceWith

Specifies the string which will be used for the replacement. If this value is non-NULL, the string specified will be set as the replacement string when the Find/Replace dialog comes up. If this value is NULL (or the parameter is defaulted) there will be no initial replacement string.

CFindReplaceDialog::FindNext

It is not clear if this is complementary with IsTerminating or there is a condition under which the callback could take place where neither condition were true.

There is no discussion of how the user might implement this using a menu/toolbar command; for example, I used

static const UINT UWM_FIND_REPLACE = ::RegisterWindowMessage(FINDREPLACEMSG);
void CMyView::OnFindNext()
   {
    if(finder == NULL)
       return;   // no active find/replace dialog
    PostMessage(UWM_FIND_REPLACE, 0, (LPARAM)finder);
   }

CFindReplaceDialog::IsTerminating

The documentation of this is confusing. It assumes an unfortunate antecedent of 'the current'. It says

If this function returns nonzero, you should call the DestroyWindow member function of the current dialog box and set any dialog box pointer variable to NULL. Optionally, you can also store the find/replace text last entered and use it to initialize the next find/replace dialog box.

The "current" dialog box may mean something completely different to the user, such as the dialog box which is the application. A less confusing description would be

If this function returns a nonzero value, you should call the DestroyWindow member function of the current Find/Replace dialog and set any pointer variables which reference it to NULL. Optionally, you can also store the find/replace text last entered and use it to initialize the next find/replace dialog box. The CFindReplaceDialog::PostNcDestroy handler will call delete this to ensure the dialog box class instance for the Find/Replace dialog will be freed.

In addition, the example that is referenced under GetFindString has virtually nothing to do with the GetFindString example, and would be a perfect example for CFindReplaceDialog::IsTerminating, which is where it should appear!

Why is an example of termination used as an example of GetFindString? A better example for GetFindString would be an example that shows how to do whole-word detection including the boundary conditions at the beginning and ending of the buffer.

Note that it would be helpful if the example included how to load the strings and set them when a new dialog is created...it may be obvious to me, but not necessarily to someone else.

Also, it is not clear if IsTerminating and FindNext are complementary conditions, or there is a condition under which the callback could be called and neither of these predicates would be true?

CFont::CreateFont, CFont::CreateFontIndirect

These should be declared "obsolete" and maintained only for backward compatibility with older MFC code. The definitions should be

  • BOOL CFont::Create(....the long list of parameters here...);
    BOOL CFont::Create(const LOGFONT & lf)
  • There is no need to try to use the old Win16 API names any longer. Since this change is upward compatible (it is just adding to the existing class), it would simplify the documentation a lot.

    CFont::CreatePointFont, CFont::CreatePointFontIndirect

    These produce different results in VS .NET 2003 and VS 2005-and-beyond.

    In VS .NET 2003, the font height was computed as

    	POINT pt;
    	pt.y = ::GetDeviceCaps(hDC, LOGPIXELSY) * logFont.lfHeight;
    	pt.y /= 720;    // 72 points/inch, 10 decipoints/point

    In VS 2005, the font height is computed as

    	POINT pt;
    	// 72 points/inch, 10 decipoints/point
    	pt.y = ::MulDiv(::GetDeviceCaps(hDC, LOGPIXELSY), logFont.lfHeight, 720);

    Note that MulDiv rounds a fractional value up to the nearest integer, but the / operator truncates the value. So, for example, if the font height were specified as 80 units, that is, an 8point font, and the display had 96dpi, the computed height should be 96 * 80 / 720 = 10.67 pixels. But under the computation

    	pt.y = 96 * 11; // == 7680
    	pt.y /= 720;    // == 10

    but under MulDiv, the computation ::MulDiv(96, 80, 720) generates the result 11 because 10.67 is rounded to the nearest integer, 11.

    CHOOSECOLOR 

    The third member of the CHOOSECOLOR structure is erroneously defined and nonsensically explained.

    It is defined as

    HWND hInstance;

    which of course makes no sense whatsoever because an HINSTANCE is not an HWND. Then it is further confused by the definition. The documentation in Flags is even more confusing.

    hInstance

    If the CC_ENABLETEMPLATEHANDLE flag is set in the Flags member, hInstanceis a handle to a memory object containing a dialog box template...

    I have no idea what a "handle to a memory object" might be. I'm not even sure what a "memory object" is, because the way to reference a block of memory is typically by using a pointer. Perhaps it is meant to be an HGLOBAL to memory allocated by GlobalAlloc. Perhaps it is an HLOCAL to memory allocated by LocalAlloc. If either of these are true, then that is what should be stated. If both are true, that is what should be stated. I know it is confusing to actually tell the truth, but it would be useful here. For example

    hInstance

    If the CC_ENABLETEMPLATEHANDLE flag is set in the Flags member, hInstance holds a handle to a block of storage obtained via GlobalAlloc and which has been initialized to a dialog template. See Remarks. ...

    Then, in the nonexistent Remarks section (which must be added), it would explain

    When using CC_ENABLETEMPLATEHANDLE, the hInstance member must contain a handle to storage allocated by GlobalAlloc and initialized to hold a dialog template. While this is commonly obtained by FindResource/LoadResource, in that case it is more common to use the CC_ENABLETEMPLATE flag and set hInstance to the module instance required. If the template has been created by other means, it must either be created in storage allocated by GlobalAlloc or, once created, a suitably-sized allocation must be made using GlobalAlloc, then GlobalLock used to obtain a pointer to this storage and the template copied to this storage. The handle must eventually be unlocked via GlobalUnlock and the storage freed by GlobalFree.

    Back in the description of hInstance, it goes on

    ...If the CC_ENABLETEMPLATE flag is set, hInstance is a handle to a module that contains a dialog box template named by the lpTemplateName member.

    So it might be an HGLOBAL or an HLOCAL or an HINSTANCE. How could anyone have been stupid enough to declare it as an HWND? An HINSTANCE makes sense, or even the generic HANDLE type, but an HWND? Didn't anyone review these structures for sanity?

    The documentation of Flags makes even less sense:

    CC_ENABLETEMPLATEHANDLE

    Indicates that the hInstance member identifies a data block that contains a preloaded dialog box template.

    What is a "data block"? If I have one, how would I "identify" it? The usual way to indicate a reference to a block of memory is via a pointer. This is even less clear than "a handle to a memory object"! If it is supposed to be a handle of storage allocated by GlobalAlloc or LocalAlloc it should say so!

    CFontDialog::DoModal

    This will occasionally issue a completely erroneous message

    Like most messages designed by amateurs, this is worse than content-free; it is out-and-out wrong. It is particularly annoying because this can occur on a machine with several hundred fonts installed, so it is obvious that the message is nonsense! The correct message would say

    There are no fonts installed that match the selection criteria.
    textual explanation of selection criteria here
    Either change the selection criteria or install fonts that match the criteria

    This is an example of a lazy programmer failing to analyze the reason for failure and using a simple, hardwired error message when an explanatory error message describing the exact reason would be the correct solution.

    The printer DC provided via the CHOOSEFONT structure (or supplied as the third parameter to the CFontDialog constructor for CFontDialog) appears to have no effect on the font size returned. You would expect that if a DC for a 600dpi printer were supplied (which I do), then a 10point font should return an lfHeight of -83 (83/600 of an inch * 72ppi = 9.96 points), but instead it returns -19, which would be the correct value to return for a screen font. (The font selected was Arial).

    CFontDialog::GetCurrentFont

    In the overview of members, this is said to retrieve the name of the font. This is absurd. It retrieves a LOGFONT structure representing the selected font.

    ChooseFont

    There appears to be a problem with the use of a printer DC to provide the reference DC for font selection. See CFontDialog::DoModal

    CHOOSEFONT

    The lpszStyle member is described as

    Pointer to a buffer that contains style data. if the CF_USESTYLE flag is specified, ChooseFont uses the data in this buffer to initialize the font style combo box. When the user closes the dialog box, ChooseFont copies the string in the font style combo box into this buffer.

    Well, this is not particularly useful information. What is "style information"? What format is it in? How large is the buffer? Could a short buffer have a buffer-overrun condition that would result in unsafe code? (There is no apparent maximum length specification of the size of this buffer).

    The correct documentation should be at least

    Pointer to a buffer that contains a style selection string. If the CF_USESTYLE flag is specifed, ChooseFont matches this string to a string in the font style combo box, and selects that style as the initial style displayed. The match is case-independent, but the match must otherwise be an exact string; a partial match will not be made (FindStringExact is apparently used to do the match). When the user closes the dialog box, ChooseFont copies the string in the font style combo box into this buffer. This means the parameter used cannot be a pointer to a literal string, which is stored in write-protected memory. <<What is missing here is a specification of the buffer size that should be provided>>.

    CHtmlEditCtrlBase::QueryStatus

    Refers to the cmdID parameter as being selected from the CGID_MSHTML command group. There is no hyperlink to what this means. It does not mention that to use this API, to get those symbols, you must include the header file mshtmcid.h. There should be a hyperlink to the page named "MSHTML Command Identifiers".

    To refer to this documentation as "hopelessly inadequate" is to overstate its quality.

    CIEXYZ

    There is no hyperlink to the type FXPT2DOT30 data type, which appears to be not documented at all. While it is clear that this is going to be represented as 11.111111111111111111111111111111 in a fixed-point representation, the representation is not explained; presumably the high-order bit is the sign bit, the next bit is either 0 or 1, and the remaining bits are the mantissa. But documentation would be useful.

    CIEXYZTRIPLE

    This contains the obsolete keyword FAR. It is amazing that this was ever used to define a 32-bit-only concept, and that it would ever appear in a 32-bit structure description. FAR is dead, and should be deleted from every place it is used.

    CImage::BitBlt

    There are several forms of this method, and they are not explained; in the usual slovenly fashion, all parameters are explained as the union of all parameters in all overloaded methods, and no explanation is given. Frankly, I'm sick and tired of trying to reverse-engineer the methods from the union-of-every-possible-parameter descriptions, which I think is just a lazy documenter's way of avoiding doing the job right. Note that a throw statement is supposed to list the types of exceptions thrown, and if it doesn't, the notes should explain what exceptions are thrown! In spite of the nonsense the documentation people or implementors state, it is not possible to throw every conceivable exception ever defined. The number of exceptions is finite and enumerable.

    I.
    BOOL BitBlt( HDC hDestDC,
                 int xDest, 
                 int yDest, 
                 DWORD dwROP = SRCCOPY 
    ) const throw( ); 
    II.
    BOOL BitBlt(HDC hDestDC,
                const POINT& pointDest,
                DWORD dwROP = SRCCOPY 
    ) const throw( );
    
    III.
    BOOL BitBlt(HDC hDestDC,
                const RECT& rectDest,
                const POINT& pointSrc,
                DWORD dwROP = SRCCOPY 
    ) const throw( );
    
    IV.
    BOOL BitBlt(HDC hDestDC,
                int xDest,
                int yDest,
                int nDestWidth,
                int nDestHeight,
                int xSrc,
                int ySrc,
                DWORD dwROP = SRCCOPY 
    ) const throw( );
    

    Notes:

    In forms I. and II., the entire bitmap is transferred to the destination DC at the coordinates specified, starting at 0,0 in the source bitmap. In form III, the bits are transferred starting at the specified coordinates in the source, and only as many pixels as specified by the destination rectangle are transferred to the coordinates specified by the destination rectangle.

    Form IV. provides full generality. Forms I..III are shortcuts for Form IV.

    The exceptions that can be thrown are CMemoryException *...

    CImage::Load

    This vaguely states that it loads an image file. It does not state that this image can be a BMP, JPG, GIF or PNG file. (You have to read a completely different part of the document to figure this part out). It also does not state that it can be a TIFF file.

    CImage::LoadFromResource

    This does not specify what type of resource is used to load the image; it vaguely says "contains the image to be loaded". Unfortunately, the only type of image it can load is a BITMAP resource, which should be explicitly stated. In fact, the correct documentation should say

    Remarks

    This uses the ::LoadImage API to load an IMAGE_BITMAP resource.

    CImageList::Create

    This talks about a "color mask" but contains no hyperlinks to suggest what this is or how it is used. A graphics wizard might find this obvious but it will not be obvious to the average reader who is trying to use a CImageList for the first time and may never have done any graphics programming. For example, it should link directly to the page that talks about "Drawing an image list" (ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.en/dv_vclib/html/2f6063fb-1c28-45f8-a333-008c064db11c.htm in the VS2008 documentation).

    cl /QIPF 

    This documentation presumes the reader has a clue as to what "IPF" means. Does it refer to the largely-dead "Itanium" processor? Who knows? There is no hyperlink on "IPF" to suggest what it means or explain it.

    One of the apparent joys of this seems to be that the compiler has to cope with errors in the hardware. It is not clear if you want to produce something that can run on any version of whatever an IPF is what options should be selected.

    The description /QIPF_noPIC says "Generates image with position independent code (IPF only)". Wouldn't the part that says "no" suggest that it means specifying this option creates an image with no position independent code? Duh?

    Class View

    This has always struck me as one of the more profoundly useless accretions in Visual Studio. I have never wanted to use it, but in VS2003+ it has become an essential part of the interface, even though there should be ways to browse what I want without ever seeing this piece of trash. For example, if I am editing dialogs, I should be able to have a dropdown that has the list of all the dialogs, and I just choose a class from that dropdown. I shouldn't need anything this elaborate.

    But it is also delicate and temperamental. This is a feature I do not need in a programming environment. Suddenly, without warning, I get a presentation of the form

    Why should I care about how many files it has to parse? There is apparently no attempt underway to actually parse them, and until this now-essential feature get these files parsed, I have lost the ability to add methods, handlers, etc. to my classes. I'm getting very good at doing this "by hand". I feel like I've fallen through a time warp to 1990 or so when we programmed in Microsoft C 3.0, used nmake, hand-built our MakeFile, etc. Have we learned nothing in nearly 20 years?

    One thing that was clearly not learned was the value of documentation. Note that there is no "Help" button under these conditions that will take to your Happy Place where there is actual documentation explaining what this message means and what you can do to fix the problem. A search of the MSDN documentation reveals nothing; the Class View documentation naively expects that everything actually works all the time and therefore presumes that there is never a need to document the failure modes. So what's to do? Shutting down the project and deleting the .ncb file, followed by a full rebuild, does nothing. And, as it happens, I'm literally Out In the Middle Of Nowhere in Pontypŵl, Wales, UK, with no Internet service so no way of seeing if anyone else had this problem or how it was fixed. Right-click gives no useful information. I'd expect to see a menu that had the option "So parse them already, you are waiting perhaps for a sign from God?" to which I could issue some suitably scathing reply, such as "Oh, don't worry about me, I'll just sit here in the dark and type my program in by hand." (The fact is: I can. But if you teach, you discover that more and more students use Intellisense as a replacement for thinking, and simply can't recover if it doesn't work!)

    I cannot make this go away. So I'm stuck. Key question: WHY IS THIS NOT DOCUMENTED? WHY IS THERE NO ACTIVE HELP LINK TO AN EXPLANATION WHEN IT OCCURS? ARE THERE ANY ADULTS PAYING ATTENTION TO THESE INTERFACES?

    Without the Class View, I cannot add control handlers or menu handlers to my project using the tools.

    Right before this failure, I got a message (which I now cannot reproduce) that said something about how an Add/Remove of an event was not possible because the code entity was read only. Well, I can imagine that if I had a read-only file and did not have the rights to modify that file, I should get such an error. But my file is read/write. I can read/write it from VS, from a secondary editor, and from NotePad if I have to. Therefore, the message is completely meaningless as an error report. But I get it anyway. On a file that mere minutes before I had successfully added a handler to. Now, the first thing we note about this message is that it is lying in it teeth. The file is clearly not write-protected, so any message to that effect is highly erroneous. What is going on? Well, obviously, I want to go to the documentation about this message. Right. And if you believe Microsoft has actually documented a message this important, then we really need to get together to help get my dead Nigerian uncle's funds from his bank...I'll give you 10% of the $30,000,000 he has stashed away. At which point I will retire and never have to write another line of code. You, on the other hand, will need to go into personal bankruptcy. That actually captures the Microsoft attitude pretty well.

    The first solution is that every MessageBox that is produced by Visual Studio suggests a problem has a hyperlink (via its Help button, for example) to the MSDN page that explains what has gone wrong and what you have to do to fix it. Failing that, each such message shall have a code number such as RTE3002 that we can look up just like compiler error messages (alas, with probably as coherent an explanation. So it can be language-independent as compiler and linker messages are. Just look up the code. (Of course, having the Help button do it for us would be even better, but I'll settle for value of information over a fancy interface to it; I should be able to type RTE3002 into the search box and get the reference to it virtually instantaneously. (In my code, every MessageBox is unique in that there are absolutely, positively no two places that can issue the same text in a given MessageBox. Even if it issued by a common file-open handler, one of the parameters to that handler is information to be displayed that says who invoked the common handler code. I have no qualms about passing in __FILE__ and __LINE__ to an error message indicating a serious and unexpected failure. See my essay on creating useful text in message boxes.

    Guiding principle: EVERY MessageBox that can be issued must uniquely identify the problem, give all essential details, and suggest a recovery technique or give a hyperlink to a local page that contains the information (do not link me to Microsoft Live! pages!!! I do not have internet access out here in South Wales (I barely have cell phone service!))

    ClientCallback Function Prototype Callback Function 

    The documentation erroneously omits the NTAPI calling convention!

    Clipboard, Using 

    There are some serious errors in the essay on "Using the Clipboard". It is unfortunate these examples were not written by someone with a basic understanding of reality.

    For example, in the specific example I looked at, the window function is declared as APIENTRY, when this violates the documented interface, which is CALLBACK. True, it just happens that in the current implementation APIENTRY and CALLBACK are both defined as synonyms for __stdcall, but that does not have to be true, and the use of APIENTRY for the CALLBACK function is erroneous.

    The example is syntactically erroneous because it was written for Windows 1.0, before there was an ANSI C compiler. The quaint usage of not specifying the types in the parameter list, but after the parameter list, has been dead for 20 years! At least. Come on, isn't anyone actually reviewing these examples to see if they work at all?

    The function uses a static variable inside the scope, hwndNextViewer, sets it in the WM_CREATE handler, and assumes it will be correct for all time. This is a deeply erroneous assumption; if there is ever a second instance of this window, this hack will fail utterly. This is per-window state and can only be valid for one window. It only works in this trivial example because there is only one instance of this window, but it does not generalize to MDI or any other faintly realistic scenario. To use a hack this easily broken in an example is unforgivable. It even encourages the idea that this technique might ever make sense. At the very least, the value should be stored as a property, and retrieved; the correct code would be

    HWND hwndNextViewer; // note: this is NOT static!
    #define PROP_HWND_VIEWER _T("HWND NextViewer") _T("guid-here")
    case WM_CREATE:
        hwndNextViewer = SetClipboardViewer(hwnd);
        SetProp(hwnd, PROP_HWND_VIEWER, (HANDLE)hwndNextViewer);
        break;

    Then, when it is required, retrieve it:

    case WM_DESTROY:
        hwndNextViewer = (HWND)GetProp(hwnd, PROP_HWND_VIEWER);
        ChangeClipboardChain(hwnd, hwndNextViewer);
        PostQuitMessage(0);
        break;
    
    case WM_DRAWCLIPBOARD:
        SetAutoView(hwnd);
        hwndNextViewer = (HWND)GetProp(hwnd, PROP_HWND_VIEWER);
        SendMessage(hwndNextViewer, uMsg, wParam, lParam);
        break;

    Any solution which uses a static as illustrated is an example of amateur programming at its worst. I was writing code like this, using SetProp/GetProp, in 1990, in my first Windows program, so it is not Rocket Science to get it right!

    As an alternative, the state can be stored in a struct pointed to by the GWLP_USERDATA field. This is considered Best Practice in modern standards if not programming in MFC, where the state is kept in the CWnd-derived class.

    typedef struct {
         HWND hwndNextViewer;
         ... other per-window state kept here
       } UserData;
    
    case WM_CREATE:
         {
          UserData * context = (UserData *)malloc(sizeof(UserData)); // or new UserData if using C++ without MFC
          SetWindowLongPtr(hwnd, GWLP_USERDATA, context);
          context->hwndNextViewer = SetClipboardViewer(hwnd);
          break;
         }      
    case WM_DRAWCLIPBOARD:
        {
         UserData * context = (UserData *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
         SendMessage(context->hwndNextViewer, uMsg, wParam, lParam);
       }

    It is also rather amateurish to put all the variables at the start of the block; good practice would place them local to the blocks in which they were used. That is, instead of a variable hwndNextViewer declared at the head of the function, the variables would be declared as needed:

    case WM_DESTROY:
        {
         HWND hwndNextViewer = (HWND)GetProp(hwnd, PROP_HWND_VIEWER);
         ChangeClipboardChange(hwnd, hwndNextViewer);
         PostQuitMessage(0);
         break;
        }

    Better still, the windowsx.h macros should be used, so it should be written as

    LRESULT CALLBACK MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
       {
        switch(uMsg)
           {
            HANDLE_MSG(WM_PAINT, main_OnPaint);
            HANDLE_MSG(WM_SIZE, main_OnSize);
            HANDLE_MSG(WM_CREATE, main_OnCreate);
            HANDLE_MSG(WM_CHANGECBCHAIN, main_OnChangeCBChain);
            HANDLE_MSG(WM_DESTROY, main_OnDestroy);
            HANDLE_MSG(WM_INITMENUPOPUP, main_OnInitMenuPopup);
            HANDLE_MSG(WM_COMMAND, main_OnCommand);
            default:
               return DefWindowProc(hwnd, uMsg, wParam, lParam);
          }
        return 0;
       }

    The above represents Best Practice in writing a Windows app. Anything else is just amateur scribbling.

    ClipCursor

    This appears to be broken under Vista. However, it is not clear why. For example, there is a qualification in the description of ClipCursor that "the calling process must have WINSTA_WRITEATTRIBUTES access to the window station", but there is no hyperlink to the concepts of "Window station" or "WINSTA_WRITEATTRIBUTES". Any attempt to figure out how to use GetSecurityInfo wanders off into obscure security features that make it hard to even attempt to write code to see if this attribute is set, or figure out how to set it if it is not set.

    CListBox::GetCurSel 

    This section fails to contain any explanation of the form

    "For multiple-selection list boxes, this will not work. Instead, using GetSelItems to get an array of currently-selected items"

    nor does the See Also section contain a reference to GetSelItems.

    CListBox::GetSel 

    Does not mention that to get the current selection of a single-selection listbox, GetCurSel should be used, nor does it contain a hyperlink to GetCurSel in the Remarks section nor in the See Also section.

    CListBox::GetSelItems 

    This does not contain a hyperlink to GetSelCount nor does GetSelCount appear in the See Also section.

    Also, the examples that include declarations like

    extern CListBox * pmyListBox;

    are confusing and misleading to most readers. It is rare that a pointer to a listbox is involved; more likely, a CListBox control variable appears in the class definition. The examples should all be of the form

    // The declaration in the class, created by the Add Variable tool, would be
    CListBox myListBox;
    
    // This code would appear in a handler for some event in the dialog, property page, or form view
    int nCount = myListBox.GetSelCount();
    CArray<int> aryListBoxSel;
    
    aryListBoxSel.SetSize(nCount);
    myListBox.GetSelItems(nCount, aryListBoxSel.GetData());
    
    // Dump the selection array
    #ifdef _DEBUG
       afxDump << aryListBoxSel;
    #endif

    This would save the insane code I've seen where somebody declares a global variable, assigns to it the address of the control variable, and uses it in the style shown in all too many of these examples. Note there is no need to specify the second template parameter to CArray

    Note that the rgIndex parameter is specified as "a long pointer". Whatever that is. Who knows what a "long pointer" is? Quickly, everyone! (Those who wrote programs for Win16 are not eligible to answer). Goes along with the stupidity of including "FAR" in declarations, header files, and documentation. This concept has been obsolete in Win32 since its creation, so why does the documentation still suggest that such a concept exists?

    Does anyone know what the "rg" prefix means in Hungarian Notation? And shouldn't this simply be called "nCount" which would make more sense?

    CListBox::InitStorage

    The code example shows

    int n = pMyListBox->InitStorage(256, 10);
    ASSERT (n != LB_ERRSPACE);
    CString str;
    for(int i = 0; i < 256; i++)
       {
        str.Format(_T("item string %d"));
        pMyListBox->AddString(str);
       }

    The problem with this is that the second parameter is the number of bytes to allocate for strings. "item string 256" takes 16 bytes (including the terminal NUL) in 8-bit apps and 32 bytes in Unicode. So why was "10" chosen as the example string length?

    (Thanks to Robert Zimmerman for pointing this out)

    The question also arises: what about dealing with Item Data? If I'm going to have item data, and want to preallocate 256 items, should I add 256 * sizeof(MyItemDataStruct)? If it is a regular ListBox, or owner-draw with LBS_HASSTRINGS, should I allocate 256 * (sizeof(MyItemDataStruct) + STRING_ALLOWANCE) where STRING_ALLOWANCE is the number of bytes I plan to allocate for the strings? Inquiring Minds Want To Know!

    CListBox::SetCurSel 

    This states that it cannot be used to set or remove a selection in a multiple-selection list box, but then fails to give a hyperlink to CListBox::SetSel, nor does CListBox::SetSel appear in the See Also section.

    CListBox::SetSel 

    Does not contain a hyperlink that says "To set the selection for a single-selection ListBox, use SetCurSel", nor does it include SetCurSel in the See Also section.

    CListCtrl::SortItems

    The function CListCtrl::SortItems takes a user-defined parameter of type DWORD_PTR but the callback function takes this as a type LPARAM. How is this? Yes, we know that the Deep Truth is that both DWORD_PTR and LPARAM are types that are either 32-bit or 64-bit, but in fact a DWORD_PTR is unsigned and an LPARAM is signed. How is it that someone could write an interface spec that used two different types to mean the same thing? Which is it?

    CloseHandle

    When given an invalid handle this will call RaiseException to throw a Structured Exception. The exception code is 9. This is undocumented, and also incompatible with C++.

    CMutex

    Like most MFC locking primitives, there is a fundamental simple principle to guide the usage of this type:

    NEVER, UNDER ANY CIRCUMSTANCES IMAGINABLE, EVER, USE A SYNCHRONIZATION PRIMITIVE DEFINED BY MFC!!!!!!!!!

    It's that simple. But here's the proof:

    The correction to the documentation is to label these classes as "broken, and broken by design, not to be used".

    In the superclass code, the call to CSyncObject::Lock is as shown below

    BOOL CSyncObject::Lock(DWORD dwTimeout)
    {
    	DWORD dwRet = ::WaitForSingleObject(m_hObject, dwTimeout);
    	if (dwRet == WAIT_OBJECT_0 || dwRet == WAIT_ABANDONED)
    		return TRUE;
    	else
    		return FALSE;
    }

    One can only look upon the author of this code with pity and scorn. Note what this code says: "If someone is in the middle of modifying a protected area of data, and crashes before completing the modifications, you may now proceed to use that data as if it has been correctly modified, even though you know it is probably corrupted and will cause you to crash unpleasantly, or corrupt the data, or lose information, In addition, every thread that was blocked on the mutex will receive the WAIT_ABANDONED notification, so now all these threads think they have successfully acquired the mutex, and they all happily proceed to try to use the data, concurrently.

    But hey, do you want us to do the job right? That would take work, because we'd have to return an actual error code, and we only want to return a BOOL, and besides, it doesn't matter that you now have several threads concurrently accessing your allegedly protected data."

    Such code is beneath contempt. It is a prime example of how to guarantee catastrophic failure of your program.

    But it's worse than you can imagine. A mutex, like a critical section, has "recursive acquisition semantics", that is, if acquired more than once by the same thread, the acquisition merely increases a reference count, and there must be as many releases as acquisitions. The proof is that you can write

    HANDLE lock = ::CreateMutex(NULL, FALSE, NULL);
    ::WaitForSingleObject(lock, INFINITE);
    ::WaitForSingleObject(lock, INFINITE);
    ::ReleaseMutex(lock);
    ::ReleaseMutex(lock);
    ::CloseHandle(lock);

    (You may ask why you would lock twice in a row; the above code shows a logical presentation of what can be a complicated execution sequence spanning many functions). But the above code works. Now let's look at CMutex in the same context:

    CMutex crit;
    CSingleLock lock(&crit);
    lock.Lock();
    lock.Lock();
    lock.Unlock();
    lock.Unlock();

    You would think this would work, but it does not and cannot be made to work. Let's look at the lock and unlock code in MFC:

    BOOL CSingleLock::Lock(DWORD dwTimeOut /* = INFINITE */)
    {
    	ASSERT(m_pObject != NULL || m_hObject != NULL);
    	ASSERT(!m_bAcquired);
    
    	m_bAcquired = m_pObject->Lock(dwTimeOut);
    	return m_bAcquired;
    }
    
    BOOL CSingleLock::Unlock()
    { ASSERT(m_pObject != NULL);
    	if (m_bAcquired)
    		m_bAcquired = !m_pObject->Unlock();
    
    	// successfully unlocking means it isn't acquired
    	return !m_bAcquired;
    }

    How many fundamental design errors can you spot in this code? Let's see, we can't pass a pointer to it to another thread because the m_bAcquired is not using any form of synchronized access. It assumes that recursive acquisition can never exist, because it assumes on a Lock call that m_bAcquired is FALSE. It is set if the lock is acquired. If it is not set, the Unlock code cannot be executed.

    How, exactly, this could ever possibly make sense for a mutex, semaphore, CRITICAL_SECTION or event escapes me. It cannot make sense. For example, and I discovered this years ago, if the Mutex has a name and is shared between processes, each process will have its own CSingleLock object and the correlation of the m_bAcquired state and the actual state is impossible to maintain.

    Had the m_bAcquired variable been eliminated, this code could be made to work, but as it stands, this code is complete crap, and must never be used (unless, of course, you have no interest in the correctness of your code). Combine this with the failure to properly handle WAIT_ABANDONED and you have a class that is guaranteed to make your code incorrect.

    .CODE (MASM)

    The documentation says

    When used with .MODEL, starts a code segment (with segment name CODE)

    .CODE

    Unfortunately, there is nothing in the description of .MODEL that suggests in any way whatsoever that .CODE has any meaning. I can only infer that this documentation is gibberish.

    The same error appears for .CONST and .DATA.

    COleDispatchDriver::~COleDispatchDriver

    This is a superclass which has a very serious bug. It is not transparent to the ::GetLastError value. Due to erroneous design in the OLE functions, some low-level OLE handler (called eventually by a Release call from the destructor) incorrectly sets the ::GetLastError value to 0 (S_OK). Unfortunately, this means that if you try to write intelligently-designed code, such as

    if(some_error_condition)
       { /* operation failed */
        DWORD err = ::GetLastError();
        ... process error
        ::SetLastError(err);
        return FALSE;
       } /* operation failed */

    and you have any OLE automation object in the scope of your function, the error value is destroyed, so the caller, when it gets the FALSE return and calls ::GetLastError to see why, gets S_OK (0) instead of the error value you set! In my case, it was the CPresentation type involved with a PowerPoint Automation Interface call.

    .CONST (MASM)

    The documentation says

    When used with .MODEL, starts a constant data segment (with segment name CONST)

    .CONST

    Unfortunately, there is nothing in the description of .MODEL that suggests in any way whatsoever that .CONST has any meaning. I can only infer that this documentation is gibberish.

    The same error appears for .CODE and .DATA.

    COS (Excel)

    (Well, this is actually a Excel documentation bug, but why not add it here...)

    The Excel documentation says

    Remark

    If the angle is in degrees, multiply it by PI()/180 or use the COS function to convert it to radians.

    The correct documentation is

    Remark

    If the angle is in degrees, multiply it by PI()/180 or use the RADIANS function to convert it to radians.

    __COUNTER__

    This is an exceptionally-poorly-written example. It does not, for example, show that there is a variable called my_unique_prefix1, nor does it show how you might use this code realistically in some context! The example is contrived to the point of uselessness. It uses a previously-undefined term, "compiland", to describe its use. No example is shown by which I might do something like

    #define DoSomething(x) \
          int FUNC(MyVariable); \
          MyVariablen = ...; \
          CallSomeFunction(MyVariablen);

    and therefore illustrate how to get references to the actual declaration of MyVariablen. Instead, it presumes you know that the current value of __COUNTER__ was 0, and there is no reason to expect this!!!! A useful example should be provided.

    The documentation also does not state that this value is defined only if _MSC_VER >= 1300.

    CPathT::IsDirectory

    The documentation erroneously claims the function is BOOL and returns TRUE if the path is a directory, FALSE otherwise. A correspondent [thanks to Mikel Luri Fernandez] points out that this documentation is incorrect; it returns a non-zero value if the path is a directory, in particular, it returns the value 16, which is the value for FILE_ATTRIBUTE_DIRECTORY.

    _CPPLIB_VER

    The documentation of this is nonsensical. First, who is Dinkumware and why should we care? Second, what is "reports" relative to a symbol? It might say "is defined as", but the verb "reports" is meaningless. Third, what is the format of the value? Inquiring Minds Want To Know! Note that as soon as this takes on more than one value, the complete history of values shall be displayed, and preferentially with an explanation (e.g., "TR1", with a hyperlink to what TR1 actually means).

    It is defined in yvals.h, which is not even part of the C++ Standard Library.

    Empirically, I have discovered the following

    VS Version _CPPLIB_VER
    VS6 (not defined)
    VS2003 313
    VS2005 405
    VS2008 505

    Note: I happen to know that Dinkumware created the Standard Template Library (STL), now known as the Standard C++ Library, and most of it was written by P.J. Plauger. But how is anyone else going to know that? Only C++/STL geeks would know that information!

    @CPU (MASM)

    The documentation says

    A bit mask specifying the processor mode (numeric equate).

    I'm sure this actually has real values, but apparently they are top-secret, and cannot be determined unless you know someone at Microsoft.

    Documentation like this is an insult to intelligent readers.

    It is not clear why this simple and obvious table does not appear in the documentation: And it would be helpful if the "clever" encoding were actually explained; I've made a few guesses at the end as to what the bits might mean.

    Some days, Microsoft is their own worst enemy when the issue of "open source" arises! Slovenly excuses for documentation are a major factor that make them look really bad!

    Directive@CPU value
    HexBinaryDecimal
    .80860101h0001 0000 0001257
    .80870101h0001 0000 0001257
    .NO870001h0000 0000 00011
    .1860103h0001 0000 0011 259
    .2860507h0101 0000 01111287
    .286P0587h0101 1000 01111415
    .2870501h0101 0000 00011281
    .3860D0Fh1101 0000 11113343
    .386P0D8Fh1101 1000 11113471
    .3870D01h1101 0000 00013329
    .4860D1Fh1101 0001 11113359
    .486P0D9Fh1101 1001 11113487
    .5860D3Fh1101 0011 11113391
    .586P0DBFh1101 1011 11113519
    .6860D5Fh1101 0101 11113423
    .686P0DDFh1101 1101 11113551
    .K3D0D01h1101 0000 00013329
    The following bits are inferred by inspection of the above patterns
    protected mode instructions0080h0000 1000 0000128
    floating point0100h0001 0000 0000256
    ?0400h0100 0000 00001024
    has virtual hardware mode0800h1000 0000 00002048

    CreateDC

    The API documentation is a bit confusing, and also has a tendency to lie.

    For example, it says

    If the function fails, the return value is NULL. The function will return NULL for a DEVMODE structure other than the current DEVMODE....To get extended error information, call GetLastError.

    Nobody can figure out what a "current DEVMODE" is, and the entire sentence about returning NULL in such a case appears to be nonsensical. It suggests that either the sentence should be removed, or a substantial amount of explanatory material should be added.

    It has been reported several times that CreateDC will return NULL but GetLastError returns ERROR_SUCCESS. This appears to be a function of the printer driver, and suggests that there are bugs in some drivers. Therefore, the advice that you can get additional information from GetLastError could be a lie, but no discussion of the conditions under which this can occur is given.

    CreateDialog, CreateDialogIndirect, CreateDialogParam, CreateDialogIndirectParam

    CreateDialog and CreateDialogIndirect are specified as

    HWND CreateDialog(HINSTANCE hInstance, LPCTSTR lpTemplate, HWND hWndParent, DLGPROC lpDialogFunc);
    HWND CreateDialogIndirect(HINSTANCE hInstance, LPCDLGTEMPLATE lpTemplate, HWND hWndParent, DLGPROC lpDialogFunc);
    HWND CreateDialogParam(HINSTANCE hInstance, LPCTSTR lpTemplate, HWND hWNdParent, DLGPROC lpDialogFunc, LPARAM lParam);
    HWND CreateDialogIndirectParam(HINSTANCE hInstance, LPCDLGTEMPLATE lpTemplate, HWND hWndParent, DLGPROC lpDialogFunc, LPARAM lParam);

    All of these erroneously claim that the hWndParent is used as the parent window for the dialog. However, this is erroneous; experiment demonstrates that the parent is always the main application window.

    CreateDIBSection

    The documentation is confusing and misleading. For example, it states that

    ppvBits

    Pointer to a variable that receives a pointer to the DIB bit values

    [This seems pretty obvious. But then in the description of hSection we find bizarre and confusing information]

    hSection

    ...

    If hSection is NULL, the system allocates memory for the DIB.

    [so far, this seems clear]

    In this case, the CreateDIBSection ignores the dwOffset parameter.

    [so far, this seems clear]

    An application cannot later obtain a handle to this memory.

    [This is out of left field. Why in the world would an application want a handle to this memory? What possible sense could this make? It isn't even clear what purpose a handle would serve, given we have explicitly said we are not supplying a handle, so why are we bothered with information that has no value, and only serves to be confusing?]

    The dshSection member of the DIBSECTION structure filled in by calling the GetObject function will be NULL.

    [And so what? Who cares? If I specify it as NULL when I create the DIB section, I should expect to see that it is, to no one's surprise, NULL if I ask for it later! So why throw in nonsense about memory handles that has nothing to do with anything imaginable?]

    CreateEnhMetaFile

    When a parameter may be NULL, an API description should say so at the point where the parameter is specified, not buried in the remarks. Thus, this should say

    hdcRef
    [in] Handle to a reference device for the enhanced metafile. May be NULL, see Remarks

    There is no discussion of how to close an enhanced metafile! There is no hyperlink to ::CloseEnhMetaFile.

    CreateFont

    This does not have a hyperlink to LOGFONT for every instance of LOGFONT. There is an erroneous myth about Web page design that says you should not hyperlink a term more than once; this rule was created by idiots. EVERY instance of a term that is hyperlinked should be hyperlinked, especially if the nearest adjacent use of the term is not visible on the same screen. A rule like this was designed by people who never actually have to use Web pages, but once read some book about Web page design and therefore think they have some say in the design, and propagate this piece of idiocy from generation to generation of document designers. In addition, there is no instance of LOGFONT in the See Also section! Every field name should have a hyperlink to the exact paragraph in the LOGFONT description! Also, EnumFontFamilies is not hyperlinked, but does at least appear in the See Also section.

    What is not discussed here is the matching algorithm. In an intelligently-designed world, the facename would dominate; that is, whatever else was specified would be thought of as the "decorations" of the face name. But in Windows, a deeply-flawed model is used; the facename is the least important parameter to the name. So if you want a Wingdings font that is the same size as the standard font you are using, you can't just ::GetObject/CFont::GetLogFont, change the face name, and create a font from that information. Instead, you must magically infer that you need to change several other parameters to get it to work right.

    lf.lfPitchAndFamily = FF_DECORATIVE;
    lf.lfCharSet = SYMBOL_CHARSET;

    Yet these should have nothing to do with the problem! I already said what font I want! Everything else is irrelevant in the font choice!

    See also CFont::CreateFont.

    CreateNamedPipe

    There is a discussion about how there are "byte pipes" and "message pipes" but there is not, and never has been, any description of what a "message pipe" is, or how it is different from a "byte pipe".

    In a separate article, titled "Named PipeType, Read, and Wait Modes", there is a semi-coherent explanation. It might be interpretable as follows, but this is my deduced summary of what it says:

    A pipe can be opened in "byte mode" or "message mode". In "byte mode", a pipe is simply a stream of bytes; in "message mode", a pipe is a stream of "messages", where a "message" is a single ReadFile or WriteFile operation. The behavior is as follows:

    Synchronous pipes: WriteFile will write a sequence of bytes. For a byte-mode pipe, this represents an arbitrary number of bytes, nothing more. In a message-mode pipe, this represents a single "message".

    Asynchronous pipes: In byte mode, WriteFile will write as many bytes as it can, and return the number of bytes actually written. The number of bytes it can write is limited by the available internal buffer space; this is more likely to have a major impact when the client and server are on separate machines. When writing to a named pipe, a WriteFile operation can return TRUE (success) even if it did not write all the bytes, and it will return in the bytesWritten parameter the number of bytes actually written. In message mode, there is no attempt to write a partial buffer.

    Synchronous pipes: In byte mode, ReadFile will read the number of bytes available, up to the maximum buffer size. "Message" boundaries based on how the sender issued the WriteFile have no meaning and are ignored, whether the sender wrote the bytes in byte mode or message mode. In message mode, ReadFile will read an entire message (up to the maximum buffer length) if the bytes were written in message mode, but the number of bytes read will not exceed the number written by a message-mode WriteFile. If the buffer size is too small, the bytes are read, but ReadFile returns FALSE and GetLastError returns ERROR_MORE_DATA. subsequent ReadFile operations will read remaining pieces of the message, but not exceeding an individual message.

    CreateService 

    The documentation is missing several key pieces of information.

    lpDisplayName

    The display name to be used by user interface programs to idnetify the service. This string has a maximum length of 256 characters. The name is case-preserved by the Service Control Manager, but comparisons will always be performed case-insensitive.
     
    The lpDisplayName parameter may be of the form "@pathname,-stringid", where the path name can include expansion strings such as %systemroot%, and the path is the path to an executable image that contains the STRINGTABLE that has the service name, and the stringid parameter is the index into that STRINGTABLE. This can either be the executable, or a localized DLL. Although this is stored as a REG_SZ, when the @-form is used, the pathname component can use expandable strings, most commonly "%systemroot%".

    lpBinaryPathName

    The fully-qualified path to the service binary file. If the path contains a space, it must be quoted by double-quotes so that it is correctly interpreted. For example, if the string were "d:\\my share\\myservice.exe" it would have to be written as the literal string as "\"d:\\my share\\myservice.exe\"".
     
    If the path refers to %systemroot%, such as "%systemroot%\\myservice.exe", this should be specified as "\\systemroot\\myservice.exe" because the string is normally stored as a REG_SZ, and there is a special test to allow the specification of the non-expansion component "\\systemroot".
     
    The path can also include arguments for an auto-start service. For example, "\\d:\\myshare\\myservice.exe arg1 arg2". Note that if the argument strings contain spaces, they must be enclosed in quotes. These arguments are passed to the service entry point, the handler function established by the StartServiceCtrlDispatcher as the "ServiceMain" function of the SERVICE_TABLE_ENTRY when the service is registered.  Note that this erroneously states that the arguments are passed to the main function.
     
    To specify a path on another computer, it must be specified as a UNC name, that is, "\\machinename\\sharename\\path" and must not use logical device mappings, because logical names will not be available to the Service Control Manager. The path must be accessible by the computer account of the local computer because this is the security context used to access the executable on the remote computer. However, from a security viewpoint, it is not recommended to keep the executable of a system service on another machine because it could allow any potential vulnerabilities of the remote computer to be propagated to all the computers using the compromised executable.
     
    Note the description uses the phrase "the computer account" without having any hyperlink that goes to any place that describes what a "computer account" is.

    lpLoadOrderGroup

    The names of the load ordering group of which this service is a member. Specify NULL or an empty string if this service does not belong to a group. What is missing here is the syntax for multiple names. The key, upon empirical examination of the Registry, is a REG_MULTI_SZ type, which would suggest that this parameter is a multi-string value. Or perhaps it is a different format that is converted to a multi-string value. It would help a lot to have correct documentation.

    lpdwTagId

    A pointer to a variable that receives a tag value that is unique in the group specified by the lpLoadOrderGroup parameter. Specify NULL if you are not changing the existing tag.
     
    You can use a tag for ordering service startup within a load ordering group by specifying a tag order vector in the following Registry value
     
    \HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\GroupOrderList
     
    Tags are only evaluated for driver services that have SERVICE_BOOT_START or SERVICE_SYSTEM_START start types.
     
    The lpdwTagId is specified as an __out parameter, but the underlined italic text which is in the current documentation suggests that in is an __inout parameter. Note that this could produce seriously erroneous programs if the value is not initialized to some known value. There are several possible alternative interpretations. For example, it is not at all clear what is meant by the phrase "changing the existing tag". This is CreateService, which is creating a previously-non-existent service, so it is not at all clear how it could be "changed" by CreateService. So correct documentation would be of the form:

    If this value is non-NULL, the tag will be recomputed based on the load order vector and an updated tag will be returned in the DWORD which is referenced. If it is NULL, description needed.

    At the end of the description, there is a a poorly-written table which suffers from many omissions. It does not state the type of a key, and it does not explain the keys properly.

    It also fails to note that the description of the DisplayName is a REG_SZ, but can still contain expandable values if it starts with an @ (the natural inclination would be to think this must be a REG_EXPAND_SZ value to allow that). It also fails to mention the RequiredPrivileges key, which must be a REG_MULTI_SZ, and there is would have to be a link or an enumeration explaining what the values could be. Thus, the table should be of the form shown below. Note that the appearances of ChangeServiceConfig2 must be active hyperlinks.

    Key Type Description
    DependOnGroup REG_MULTI_SZ Load-ordering groups on which this service depends, as specified by lpDependencies.
    DependOnService REG_MULTI_SZ Services on which this service depends as specified by lpDependencies
    Description REG_SZ The description as specified by ChangeServiceConfig2. This may be of the form "@pathname,-stringid".
    DisplayName REG_SZ The display name as specified by lpDisplayName. This may be of the form "@pathname,-stringid".
    ErrorControl REG_DWORD The error control value as specified by dwErrorControl.
    FailureActions REG_BINARY Failure actions specified by ChangeServiceConfig2
    Group REG_SZ Load ordering group specified by lpLoadOrderGroup. Note that setting this value can override the setting of the DependOnService value.
    ImagePath REG_EXPAND_SZ A string specifying the location of the executable image, as specified by lpBinaryPathName
    ObjectName REG_SZ Account name specified by lpServiceStartName
    RequiredPrivileges REG_MULTI_SZ ? who knows ?
    ServiceSidType REG_DWORD ? who knows ?
    Start REG_DWORD When to start the service, as specified by dwStartType.
    Tag REG_DWORD Tag identifier which was returned in lpdwTagId. If lpdwTagId is not specified, ? who knows ?
    Type REG_DWORD Service type specified by dwServiceType

    It is not clear why the Group attribute says it "can" override the setting; either it does or it doesn't. If there are conditions that are implied by "can", then they must be explained. There is no explanation.

    In examining a FailureActions element, the following structure was found:

    FF FF FF FF    DWORD dwResetPeriod
    00 00 00 00    LPTSTR lpRebootMessage
    00 00 00 00    LPTSTR lpCommand
    03 00 00 00    DWORD  cActions
    14 00 00 00    ?
    00 00 00 00    SC_ACTION_TYPE  
    00 00 00 00    DWORD delay
    00 00 00 00    SC_ACTION_TYPE
    00 00 00 00    DWORD delay
    00 00 00 00    SC_ACTION_TYPE
    00 00 00 00    DWORD delay  

    It is not clear what the 0x14 value is. It is too short to be the length of the rest of the structure (which is 0x18). So it is not clear how to interpret these values.

    CreateThread 

    Does not state that this must not be called in DllMain or deadlock will result.

    CreateThreadPool 

    The parameter is stated to be "reserved", but correct documentation would state "The parameter is reserved, and must be specified as NULL".

    CreateWindow

    It does not mention that the lpWindowName parameter can be NULL.

    If the window style is given as WS_POPUP, the hwndParent parameter is ignored, and the parent returned by ::GetParent is not the parent of the window, but the top-level window of the application. This can be verified by using Spy++ to inspect the parent link, which is indeed the handle to the top-level window, not the handle that was passed to ::CreateWindow.

    If the parent is HWND_MESSAGE, the documentation is not quite correct. See the discussion of HWND_MESSAGE.

    CreateWindowEx

    It does not mention that the lpWindowName parameter can be NULL.

    If the window style is given as WS_POPUP, the hwndParent parameter is ignored, and the parent returned by ::GetParent is not the parent of the window, but the top-level window of the application. This can be verified by using Spy++ to inspect the parent link, which is indeed the handle to the top-level window, not the handle that was passed to ::CreateWindowEx.

    If the parent is HWND_MESSAGE, the documentation is not quite correct. See the discussion of HWND_MESSAGE.

    Creating a Multithreaded Service

    Compared to the other articles, Writing a Service Program's main function, Writing a ServiceMain function, Writing a Control Handler Function, this is actually a well-written example. It is Unicode-aware, for example.

    However, it is not clear why the first SERVICE_TABLE_ENTRY name is an empty string. It has to be pointed out that this only applies to a SERVICE_WIN32_OWN_PROCESS.

    However, it does use the dangerous and obsolete wsprintf, instead of something safe. In VS2005, it should use _stprintf_s which is both safe and Unicode-aware.

    It shows multithreading, but it hardwires the value 3 instead of doing something sensible like doing

    #define MAX_THREADS 3

    or

    const int MAX_THREADS = 3;

    which would make things much clearer.

    It should not be calling CreateThread, but should call _beginthread, _beginthreadex or AfxBeginThread to start the threads, otherwise the threads may not correctly work in the environment.

    It is not clear why the WaitForSingleObject is timing out at 1000ms, or why there is something that says "Main loop for service" at all. It would make more sense to have

    WaitForSingleObject(hStopEvent, INFINITE);

    since there is essentially nothing to do until the event is signaled. The example implies that there is actually something useful that might be done here, and there rarely is.

    The correct code for the loop is

    for(t=1; ; t++)

    since the TRUE is redundant in the code shown.

    It should be pointed out that the purpose of the loop is to allow a new checkpoint value to be computed each time the timeout happens, and, for example, it has nothing to do with the number of threads.

    The test for WAIT_ABANDONED is pointless because there is no mutex involved in the wait, and WAIT_ABANDONED is therefore impossible. There should be an explanation that the reason there is a timeout in the WaitForMultipleObjects is so the SERVICE_STOP_PENDING can be executed once a second.

    The use of the same string TEXT("CloseHandle") for three completely different error conditions is poor style; it should say something like

    if(!CloseHandle(hStopEvent))
        ErrorStopService(TEXT("CloseHandle(hStopEvent)"));

    There is a perfectly fine switch statement to discriminate the control events. Having a separate if-test that then re-decodes the dwCtrlCode a second time is silly. If the excuse is that "there are several lines of code there", then the correct solution is to call a subroutine to do all that. But since the switch statement does exactly the same thing in both cases, why not just combine the cases as well?

    case SERVICE_CONTROL_SHUTDOWN:
    case SERVICE_CONTROL_STOP:
         dwState = SERVICE_STOP_PENDING;
         DoShutdown();
         break;

    The use of the SetTheServiceStatus function to prepare the structure and call SetServiceStatus represents best practice and therefore should be moved to the other really bad examples already mentioned above.

    However, this does not allow for service-specifc errors. It should have an additional parameter, dwServiceExitCode.  dwServiceExitCode is nonzero, the dwWin32ExitCode should be set to ERROR_SERVICE_SPECIFIC_ERROR, e.g.,

    ss.dwServiceSpecificExitCode = dwServiceExitCode;
    ss.dwWin32ExitCode = (dwServiceExitCode != 0 ? ERROR_SERVICE_SPECIFIC_ERROR : dwWin32ExitCode

    It is extremely unclear why programmers feel an overwhelming compulsion to zero out buffers they are about to completely overwrite; the ZeroMemory calls should be eliminated, because the wsprintf call completely overwrites the significant errors. And, of course, the obsolete and dangerous wsprintf should be replaced by _stprintf_s.

    The complex for-loop that is used to shut down the threads should not be duplicated here! A single function that implements that logic should be called from both sites.

    This code is not an example of best practice, but it is orders of magnitude better than the incredibly poor examples used for other threading.

    _CrtMemCheckpoint

    The _CrtMemCheckpoint function does not work as documented. I have a complete essay on this topic.

    _CrtSetAllocHook

    The documentation of this function is erroneous and misleading. It suggests that the size parameter of the hook function specifies the size of the memory block, in bytes, but for _HOOK_FREE, this value is always 0! It is not clear why the function neglects to send this critical information across the boundary. In addition, the filename and lineNumber parameters say "if available" but do not state what is meant by "available". "Unavailable" means they will be NULL and 0.

    If there were any attempt at doing respectable documentation, it would say

    allocType _HOOK_ALLOC _HOOK_FREE _HOOK_REALLOC
    userData NULL Pointer to user block NULL
    size Size of allocation 0 Size of reallocation
    blockType Block Type Block Type Block Type
    requestNumber Request number 0 Request number
    filename File name, or NULL NULL File name, or NULL
    lineNumber Line number, or 0 0 Line number, or 0

    CSemaphore

    This class cannot be used with CSingleLock because the destructor of CSingleLock will call ::ReleaseSemaphore, which would result in erroneous behavior. Furthermore, it cannot be used across processes with a named semaphore, because an attempt to acquire it sequentially in one process will result in erroneous behavior. In addition, CSingleLock::Unlock will not allow a semaphore to be released more than once, which means it cannot be used in a receiver loop that needs to release the semaphore many times; the CSingleLock::Unlock goes out of its way to get this wrong.

    CSettingsStore::CSettingsStore

    This constructor is erroneously documented. The documentation says


    CSettingsStore(
       BOOL bAdmin,
       BOOL bReadOnly 
    );

    Parameters

    [in] bAdmin
    Boolean parameter that specifies whether the CSettingsStore object is acting in administrator mode.

    Remarks

    If bAdmin is set to false, the m_hKey member variable is set to HKEY_LOCAL_MACHINE. If you set bAdmin to true, m_hKey is set to HKEY_CURRENT_USER.


    Note that the documentation of bAdmin is correct in the Parameters section, but erroneous in the Remarks section. The code is very clear about the behavior

    CSettingsStore::CSettingsStore(BOOL bAdmin, BOOL bReadOnly) :
    m_bReadOnly(bReadOnly), m_bAdmin(bAdmin), m_dwUserData(0)
       {
         m_reg.m_hKey = bAdmin ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
       }

    So the correct documentation would be

    If bAdmin is set to true, the m_hKey member variable is set to HKEY_LOCAL_MACHINE. If you set bAdmin to false, m_hKey is set to HKEY_CURRENT_USER.

    CSimpleStringT::GetBuffer

    This erroneously states "ReleaseBuffer will perform a strlen on the buffer to determine its length". This is erroneous. The correct documentation is "ReleaseBuffer will perform a tcslen on the buffer to determine its length". A CSimpleStringT could be a Unicode string.

    The specification of the parameter nMinBufferLength makes no sense. It says

    The minimum size of the character buffer in characters. This value does not include space for a null terminator.

    I have no idea what this means. It might mean

    The minimum size of the character buffer, expressed as a character count. The buffer will hold this many characters, and sufficient space will be allocated to allow for nMinBufferLength characters followed by a null character.

    but if that's what it means, it should say so. It also says

    If there is insufficient memory to satisfy the GetBuffer request, it throws an exception.

    Really? What exception? Perhaps the correct documentation would have said

    If there is insufficient memory to satisfy the GetBuffer request, it throws a CMemoryException *.

    CSimpleStringT::ReleaseBuffer

    From reader Jim Beveridge:

    This additional information should be added to the documentation: Do not ever call ReleaseBuffer without first calling GetBuffer because ReleaseBuffer ignores the current reference count and so does not perform “copy on write” semantics. If you need to truncate a CString, use Truncate().
     

    CSingleLock Class

    The discussion of this class is largely nonsensical, except in the places where it is grossly incorrect.

    First, and most important, it fails to ever mention that CSingleLock does not allow recursive acquisition of any synchronization object, meaning you cannot correctly acquire a CMutex or CCriticalSection recursively, cannot use CSemaphore, CMutex or CEvent across process boundaries, cannot use a CSemaphore or CEvent in a loop, and generally can't do anything right. It contains no link to CSingleLock::Lock. The description

    To use a CSingleLock object, call its construct inside a member function of the controlled resource's class. Then call IsLocked member function to determine if the resource is available; if it is, continue with the remainder of the member function. If the resource is unavailable, either wait for a specified amount of time for the resource to be released, or return failure.

    It does not mention that the destructor CSingleLock::~CSingleLock will call the CSingleLock::Unlock method, making this completely unusable for a CSemaphore class! In fact. the destructor is undocumented.

    What part of "lock" did the twit who wrote this code miss? Why is there no reference to CSingleLock::Lock? How is it that CSingleLock::IsLocked can return TRUE if there is no attempt made to lock it? How is it that the suggested approach is to poll instead of block the thread instead of waiting for the lock? What child wrote this, and where was the adult supervision? I have never seen a poorer explanation of synchronization than this.

    CSingleLock::CSingleLock

    This does not mention that the initial acquisition might fail and the constructor will block, which means it would be a very poor idea to specify bInitialLock=TRUE in any real object. It does not mention that the destructor will call CSingleLock::Unlock, which means it is unusable with CSemaphore. It replicates the poor example given in all other methods, where CSingleLock::Lock is called but the value is not tested, but IsLocked is called as if there could be no race condition. In this trivial example that is true, but there is no discussion about the fact that a CSingleLock object cannot be shared with other threads.

    CSingleLock::~CSingleLock

    This method is completely undocumented, even though it can cause massive malfunction. For example, an attempt to apply CSingleLock to a CSemaphore will result in an erroneous program.

    CSingleLock::Lock

    There are so many things wrong with this documentation that it is unusable in its current form. For example, it specifies a timeout, but in the Remarks section it does not mention that if it is applied to a CCriticalSection class that the timeout must always be INFINITE? Or that for CCriticalSection, no mater what timeout is used, it will be treated as INFINITE after the ASSERT failure. At no point, does it mention that recursive acquisition is impossible, or that it will not work if the CSingleLock object is shared across threads, or if you are locking across processes. It does not mention this cannot be made to work correctly. It does not mention that for CMutex, if the error is WAIT_ABANDONED, it treats it as a successful return. The example is nonsense. If the CSingleLock fails, it is important to know why, but this information is not made available; testing IsLocked might mean that the object is locked, or it might mean that object has been locked in a second thread. Since there is no documentation that the CSingleLock object cannot be shared across threads, it is therefore possible to create programs that cannot correctly work, but for some brief period of time will give the illusion of working.

    If a CSemaphore is locked using CSingleLock::Lock, when the variable goes out of scope, the destructor CSingleLock::~CSingleLock calls CSingleLock::Unlock which will call ReleaseSemaphore. It is hard to imagine how this could ever be right.

    The basic rule is simple: NEVER USE THIS CLASS OR ITS RELATED CLASSES IN ANY REAL PROGRAM THAT MATTERS!

    CSingleLock::Unlock

    Does not contain a cross-reference to CSingleLock::Lock or CSingleLock::IsLocked. Does not mention that Unlock is incorrectly implemented and will not allow recursive acquisition of CMutex or CCriticalSection or sequential acquisition of CSemaphore or waiting for CEvent in a loop.

    The example is complete trash. It shows a lock acquisition which does not check to see if the object has been locked, and then it shows a test on a CSingleLock object, which, if shared, would mean that the code is grossly incorrect, since there is a potential race condition. The example was written by someone who never understood synchronization, and is unrecoverable. Because CSingleLock::Lock is actually implemented incorrectly, there is no way it is even possible to write this code correctly!

    CSpinButtonCtrl

    In the article titled "Using CSpinButtonCtrl", no mention is made of the fact that a CSpinButtonCtrl will send up/down notifications via WM_HSCROLL or WM_VSCROLL messages (depending on the orientation ofo the control)

    CStdioFile::CStdioFile

    While the documentation of the constructor is slightly better than the documentation of the methods, there are still severe defects.

    The proper prototypes are

    CStdioFile();
    CStdioFile(FILE * pOpenStream) throw(...);
    CStdioFile(LPCTSTR lpszFileName, UINT nOpenFlags) throw(...)

    And the documentation should state that "CStdioFile will throw a CInvalidArgException * if the pOpenStream parameter is NULL, or if the lpszFileName parameter is NULL." and "CStdioFile will throw a CFileException * if a file name is given and there is an error opening the file"

    It erroneously states that an open or creation file will throw a CFileException, which is a complete lie. It throws a CFileException *.

    It fails to state that if the stream is provided from an existing FILE *, the stream will not be closed by CStdioFile::~CStdioFile. It also fails to state that CStdioFile::~CStdioFile will close a stream opened by CStdioFile::Open or CStdioFile(LPCTSTR,  UINT) if the stream has not already been closed.

    There is a possible implementation error; the code uses ^= to clear a bit when it should use &= ~. I find it a common programming error to use XOR when &~ is what is required; apparently nobody has taught these programmers that clearing a bit and complementing a bit are different concepts.

    There is a hyperlink for stdio, but not for stdout or stderr. Why not? Even if they are all on the same page, there is no way the reader can a priori know that; or, alternatively, the hyperlink should be on the phrase "predefined input/output file pointers" (which should probably say "predefined standard handles" and indicate that these apply only to console applications).

    It erroneously states the create and noInherit modes are optional; this is impossible because these modes do not exist. The correct documentation is that the modeCreate and modeNoInherit modes are optional. Or perhaps, explicitly, the CFile::modeCreate and CFile::modeNoInherit modes are optional, since that is how they would have to be written by the programmer.

    The code example uses the now-obsolete TRY/CATCH macros instead of the modern C++ try/catch, and the obsolete char data type. It uses exit(1), a call that should never be made in any well-written program under any conditions whatsoever. It is a poor example of programming under any conditions. It gives a reason for the error only in debug mode, which is stupid, because in release mode it means that it fails silently, and worse still, merely exits the program without any indication to the user as to what went wrong. This is mind-bogglingly stupid. My most common description of these examples is "Where is the adult supervision?"

    // examples for CStdioFile::CStdioFile
    
    // This first example shows how to open a file explicitly using CFile::Open
    
    CString FileName = _T("test.dat");
    CStdioFile f1;
    if( !f1.Open( FileName, CFile::modeCreate
           | CFile::modeWrite | CFile::typeText ) ) 
         {
          // ...report error 
          // ...return control to caller or otherwise terminate flow of control
         }
    // We get here only if there was no open failure, so the above code must not just 
    // fall through on error
    
    //====================================================================================
    // This second example shows how to create a file from an existing stream
    // This would be used in a console app, but not in a Windows program, where 
    // there is no stdout stream.  It could be used for other FILE * streams in any program
    
    CStdioFile f2( stdout );
    
    //====================================================================================
    // This third example shows how to create and open a file using the constructor
    
    CString FileName = _T("test.dat");
    try
      {
       CStdioFile f3( FileName,
            CFile::modeCreate | CFile::modeWrite | CFile::typeText );
      }
    catch( CFileException * e )
    {
     // ... report error
     e->Delete(); // unless exception is re-thrown
     // ... return control to caller or otherwise terminate flow of control
    }

    CStdioFile::~CStdioFile

    This is not even documented, yet there is a very critical feature which must be documented!

    The documentation should state "If the object was created with the CStdioFile(FILE *) constructor, the file will not be closed by the destructor. In all other cases, if there is an open file, it will be closed by the destructor.

    CStdioFile::GetLength

    This is undocumented. It should be documented, because it overrides the CFile::GetLength method but in fact this implementation is erroneous.

    This is improperly implemented. It suffers from several bugs. Although it returns a ULONGLONG, in fact, it erroneously uses the FILE* stream method ftell, which only returns a LONG, and therefore will malfunction if the file is > 4.2GB. But in addition, it calls ftell, which has a serious implementation error. The consequence of this is that if it is applied to a Unix-formatted file (where \n is the only newline character, not \r\n as in Windows) it erroneously computes the current file position, then when "restoring" the file position, it restores it to the erroneously-computed value. This results in a disaster.

    The correct documentation is


    CStdioFile::GetLength

    Obtains the current logical length of the file, in bytes.

    virtual ULONGLONG GetLength( ) const;

    Return Value

    The length of the file, in bytes.

    Remarks

    This function will not work for text files whose length exceeds 232 bytes. It will return erroneous data.

    This function will erroneously reposition the file pointer if the file in question is a Unix-formatted text file which only uses \n for line termination, instead of \r\n. It must not be used for such files.


    CStdioFile::GetPosition

    This is undocumented. It should be documented, because it overrides CFile::GetPosition, but in fact this implementation is erroneous.

    The correct documentation is


    CStdioFile::GetPosition

    Obtains the current value of the file pointer, which can be used in subsequent calls to Seek.

    virtual ULONGLONG GetPosition ( ) const;

    Return Value

    The file pointer

    Remarks

    This function will not work for text files whose length exceeds 232 bytes. It will return erroneous data.

    This function will erroneously compute the position if the file in question is a Unix-formatted text file which only uses \n for line termination, instead of \r\n. It must not be used for such files. Using the value returned for a Seek operation will position the file in the wrong position.


    CStdioFile::ReadString

    The method will throw exceptions when an error occurs, but this is documented only in the Remarks section. It also vaguely says "Will throw an exception", when the real documentation should say "Throws a CFileException *" with a hyperlink to CFileException. There is no statement that it will throw a CInvalidArgException * if the lpsz  value is NULL. There is no See Also reference to CFileException or CInvalidArgException. There is a See Also reference to CArchive::ReadString but no See Also to CStdioFile::WriteString.

    The function prototypes should be written as

    virtual LPTSTR ReadString(LPTSTR lpsz, UINT max) throw(...);
    virtual BOOL ReadString(CString & s) throw(...);

    CStdioFile::WriteString

    The method will throw exceptions when an error occurs, but this is documented only in the Remarks section. It also vaguely says "throws an exception in response to several conditions, including the disk-full condition.", when the real documentation should say "Throws a CFileException * in response to several file system errors" with a hyperlink to CFileException. It does not state that it will throw a CInvalidArgException * if the lpsz value is NULL. There is no See Also reference to CFileException. There is a See Also reference to CArchive::ReadString but no See Also to CStdioFile::ReadString. There is no See Also link to either CFileException or CInvalidArgException.

    The correct function prototype should be written

    virtual void WriteString(LPCTSTR lpsz) throw(...)

    CStringT class members

    Why does this not contain a hyperlink to CSimpleStringT in the See Also section?

    CStringT::CStringT

    This is generic to all CString constructors. If the initializing string is a MAKEINTRESOURCE of an integer value in the range of 1..65535, the constructor does a LoadString of that resource ID. Thus

    CString t;
    t.LoadString(IDS_WHATEVER);

    is the same as

    CString t(MAKEINTRESOURCE(IDS_WHATEVER));

    This is documented in my essay on CStrings.

    CTime

    While the documentation states explicitly that the upper bound of CTime is January 18, 2038 it does not state either the time on that date, or that the lower bound is 1-January-1970 00:00 (UCT)

    CToolBar::GetItemID

    This is specified as returning the ID for a button at a specified index. Unfortunately, if the index is out-of-range, it takes an ASSERT failure in the debug build and returns a meaningless value in both Debug and Release builds. In an intelligently-designed world, it would return a designated value if the item index were out-of-range. There is no mechanism I've found that actually reveals the number of items in a CToolBar, a grotesque oversight. The mechanism I had to use was:

    BOOL CMyToolBar::IsValidIndex(UINT index)
       {
        TBBUTTON tb;
        return (BOOL)SendMessage(TB_GETBUTTON, index, (LPARAM)&tb);
       }
    int CMyToolBar::GetItemCount(UINT index)
      {
       for(int i = 0; ; i++)
         if(!IsValidIndex(i))
            return i;
       return 0;
      }

    Note that if the function _GetButton were to return a designated value instead of just blindly doing an ASSERT there would be no problem in having the user write an iterator across all buttons. The correct implementation of _GetButton (in MFC's bartool.cpp) should be

    BOOL CToolBar::_GetButton(int nIndex, TBBUTTON* pButton) const
    {
    	CToolBar* pBar = (CToolBar*)this;
    	if(!pBar->DefWindowProc(TB_GETBUTTON, nIndex, (LPARAM)pButton))
                return FALSE;
    	// TBSTATE_ENABLED == TBBS_DISABLED so invert it
    	pButton->fsState ^= TBSTATE_ENABLED;
            return TRUE;
    }

    CTreeCtrl

    In the topic labeled Tree Control Styles, the documentation erroneously states that

    You can retrieve and change the styles after creating the tree control by using the GetWindowLong and SetWindowLong Windows functions, specifying GWL_STYLE for the nIndex parameter.

    This is complete nonsense. The correct statement is

    You can retrieve the styles of a control by using the CWnd::GetStyle method, or modify the styles by using CWnd::ModifyStyle.

    CWinApp::ExitInstance 

    The documentation for this function is incorrect, incomplete, and misleading.

    It states

    Return Value

    The application's exit code; 0 means no errors, and values greater than 0 indicate an error. This value is used as the return value from WinMain.

    Remarks

    Do not call this member function from anywhere but within the Run member function.

    The default implementation of this function writes framework options to the applications .INI file. Override this function to clean up when your application terminates.

    There are many problems with this documentation; for example, it does not differentiate between applications and DLLs, and while it talks about "return values greater than 0", the type is int and it does not state what negative values mean.

    The correct documentation is

    There are two contents in which CWinApp::ExitInstance will be called: applications and DLLs.

    Return Value:

    For applications: the applications' exit code; 0 means no errors, and values other than 0 indicate an error. The value returned is the value which becomes the return value from WinMain.

    For DLLs: the return value is ignored.

    Remarks

    For applications, this is called from the CWinApp::Run method. You must not call it explicitly at any time for any reason with one exception: If you override the Run method in your derived class, you must call this method before the Run method returns.

    For DLLs: this is called in response to the DLL_PROCESS_DETACH event from DllMain. As such, what you do here is subject to the restrictions of DllMain.

    The default implementation of this function writes framework options to the applications profile. This is stored in the Registry under the key HKEY_CURRENT_USER\Software\profilename where profilename is the name derived from the m_pszProfileName string.

    Note that this behavior should not be confused with the behavior of CWinThread::ExitInstance for threads.

    Note that this documentation erroneously talks as if .INI files still work, and are used, instead of taking into account the fact that Win32 redirects private profile string output to a Registry key. This documentation should be brought up to standards of behavior for Win32, and not talk as if it is describing the first MFC implementation done for 16-bit Windows!

    CWinApp::InitInstance

    This documentation is meaningless gibberish, left over from 16-bit Windows. It is completely irrelevant to modern Win32 programming practice.

    For example, it says

    Application initialization is conceptually divided into two sections: one-time application initialization that is done the first time the program runs, and instance initialization that runs each time a copy of the program runs, including the first time. The framework's implementation of WinMain calls this function.

    This is complete nonsense. It has never made sense in Win32, and in fact it erroneously is a direct copy of the dead Win16 documentation. The correct documentation is

    Application initialization is done by the framework calling InitInstance. Any initialization required is performed in this method.

    There are two situations in which a derived class from CWinApp is used, and the meaning of InitInstance is different in each case.

    See also CWinApp::ExitInstance.

    Note that this should not be confused with the behavior of CWinThread::InitInstance in the case of UI threads.

    Note that this documentation erroneously talks as if .INI files still work, and are used, instead of taking into account the fact that Win32 redirects private profile string output to a Registry key. This documentation should be brought up to standards of behavior for Win32, and not talk as if it is describing the first MFC implementation for 16-bit Windows!

    CWinThread class

    This documentation is another screamingly bad example of a collection of confusing, mis-stated, and erroneous facts, failures to explicitly state best practice, and failures to clarify serious issues.

    The correct documentation is

    The main thread of execution of an application in MFC is contained in the CWinApp class, and for any real application, from an application-specific derived class of CWinApp. In addition to the CWinThread functionality, CWinApp introduces other features such as reading and storing program options. A multithreaded program will have additional instances of CWinThread, one for each thread.

    There are two general types of threads that CWinThread supports: worker threads, and what are called "user-interface" threads, which is misleading designation; what it really means is "a thread with a message pump". It is generally considered poor practice to put visible user-interface objects into any thread other than the main UI thread (the one that is running from the CWinApp-derived class) and to attempt to do so without understanding the extremely complex rules that must be followed for correct behavior will result in a program which will exhibit severely incorrect behavior. Worker threads do not have a message pump. Secondary UI threads are useful and in some cases critical, but should not actually have user interface objects in them. The name is historical and consequently very confusing.

    Objects of class CWinThread by default exist only for the duration of the thread. To cause the lifetime to be extended beyond the life of the thread, the thread must be created using AfxBeginThread with the CREATE_SUSPENDED flag, the m_bAutoDelete flag must be set to FALSE, and then ResumeThread should be called. It is erroneous to attempt to modify the m_bAutoDelete flag of a thread that is actually running.

    The CWinThread class is required to make MFC fully thread-safe. In addition, you can use your CWinThread-derived class to hold your own thread-specific information.

    Only threads controlled by a CWinThread object may use MFC objects. In an MFC application, a thread should never be created by using CreateThread, _beginthread, or _endthread.

    It is inappropriate to ever call ExitThread,, _endthread, _endthreadex, or AfxEndThread from within any CWinThread context. This will almost always result in seriously erroneous behavior. No program that does this can be considered robust or correct.

    Note that the inclusion of a method in a CWinThread class does not mean that the code of that method executes in the context of the thread. Code which is called is always executed in the context of the calling thread; thus, any method called from a given CWinThread-managed thread will be executed in the context of that thread, whether the code is part of the CWinThread class or part of some other class. Similarly, other threads are free to call methods of the CWinThread-derived class, and that code is always executed in the context of the calling thread. Note that CWinThread provides no thread safety for objects other than those internal to the MFC framework. To protect your own data, including MFC collections C++ Standard Library collections, members of your CWinThread-derived class, data in any other class read or modified by your thread, you, and you alone, are responsible for providing synchronization mechanisms, such as mutex or CRITICAL_SECTION, or the use of Interlocked... primitives, to control access to that information.

    To create a thread, use AfxBeginThread. There are two forms of this method, one for worker threads and one for UI threads. For UI threads, the CRuntimeClass pointer to the CWinThread-derived class is passed in. This creates a thread with a message pump. This means that the thread can handle messages to windows it owns, or thread messages (via PostThreadMessage). A UI thread may often have windows, which are used as message sinks. However, these windows must be invisible, owned, top-level windows, not visible windows. Many components of Windows require that the thread which invokes operations be running in a thread with a message pump, such as CAsyncSocket and SAPI (Speech API) calls. However, it is always considered bad practice to create user-visible windows from a thread, including any form of a MessageBox call.

    Instead of calling AfxBeginThread, the behavior of AfxBeginThread can be simulated by constructing a CWinThread object on the heap, then calling CWinThread::CreateThread. This two-stage construction method is useful if you wish to retain the CWinThread-derived object and create a new thread after a previous thread has terminated.

    CWinThread::InitInstance 

    InitInstance is overridden in a UI thread to provide any required per-thread initialization.

    Remarks:

    Typically, you override InitInstance to perform any tasks that must be completed when a thread is first created. Note that upon successful return, the message pump will run.

    Note that there is no mechanism for passing parameters into a UI thread. The normal method of "passing parameters" into a UI thread can be accomplished by one of several mechanisms:

    Supply the CREATE_SUSPENDED flag on AfxBeginThread. When the CWinThread-derived object is returned, member values may be set either directly (if public) or via methods (if protected). When CWinThread::ResumeThread is called, the values will be available to the InitInstance method.

    Use new to create an instance of the CWinThread-derived class. You can either add additional parameters to the constructor, or, after creation, set values either directly (if public) or via methods (if protected). Then use CWinThread::CreateThread to create the thread; the values will be available to InitInstance.

    Do not bother to set values, and do not use the in InitInstance. Instead, create Message Map entries for ON_THREAD_MESSAGE or ON_REGISTERED_THREAD_MESSAGE, and use PostThreadMessage to pass information into the thread.

    Note that it is not possible to PostThreadMessage to a UI thread until the Run method has executed a GetMessage or PeekMessage call to create the message queue. Thus, the following code cannot work:

        CWinThread * thread = AfxBeginThread(RUNTIME_CLASS(CMyThreadClass));;
        thread->PostThreadMessage(user_defined_message_id, wparam_value, lparam_value);

    because it is unpredictable when the thread will actually have created its message queue. It is always an error to do a Sleep() call in the hopes that the randomly-chosen interval will be meaningful and allow time for the thread to start running. The only way this can be handled is by starting the thread and waiting for the thread to indicate that it now has a thread queue. One method that accomplishes this is to explicitly do a ::PeekMessage to force the message queue to exist, then notify the creating thread that this has occurred.

       CMyThreadClass * thread = (CMyThreadClass *)AfxBeginThread(RUNTIME_CLASS(CMyThreadClass), THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED);
       thread->target = this;
       thread->ResumeThread();

    then have a user-defined message handler in the class which is the target, such as

       ON_REGISTERED_MESSAGE(message_id_here, OnThreadRunning)

    In the InitInstance handler do

       BOOL CMyThreadClass::InitInstance()
          {
            MSG msg;
           ::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);
           target->PostMessage(message_id_here, (WPARAM)this);
           return TRUE;
          }

    In the target class, the handler is

       LRESULT targetclass::OnThreadRunning(WPARAM wParam, LPARAM)
          {
            CMyThreadClass * thread = (CMyThreadClass *)wParam;
            thread->PostThreadMessage(user_defined_message_id, wparam_value, lparam_value);
            return 0;
          }

     

    CWnd::CalcWindowRect

    This call does not work correctly when applied to a dialog with a menu. The calculated window rectangle does not take into account the height of the menu bar. Therefore, after doing CalcWindowRect, you must compensate for the error, as shown:

    CRect r;
    ...set desired client rectangle
    CalcWindowRect(&r);
    r.bottom += ::GetSystemMetrics(SM_CYMENU);

    the general solution would be

    CRect r;
    ...set desired client rectangle
    CalcWindowRect(&r);
    r.bottom += (GetMenu() != NULL ? ::GetSystemMetrics(SM_CYMENU) : 0);

    Submitted by reader Jim Beveridge

    You list the solution as

    r.bottom += ::GetSystemMetrics(SM_CYMENU);

    This is actually not a complete solution because the menu will take multiple lines if the window is resized small horizontally. I solved this problem years ago, but the solution was ugly. I think I retrieved the client rect and then kept adding ::GetSystemMetrics(SM_CYMENU) until everything added up… But I don’t remember exactly.

    CWnd::Create

    This does not specify that the lpszWindowName parameter can be NULL.

    If the window style is given as WS_POPUP, the hwndParent parameter is ignored, and the parent returned by ::GetParent is not the parent of the window, but the top-level window of the application. This can be verified by using Spy++ to inspect the parent link, which is indeed the handle to the top-level window, not the handle that was passed to ::CreateWindow.

    CWnd::CreateEx

    This does not specify that the lpszWindowName parameter can be NULL.

    If the window style is given as WS_POPUP, the hwndParent parameter is ignored, and the parent returned by ::GetParent is not the parent of the window, but the top-level window of the application. This can be verified by using Spy++ to inspect the parent link, which is indeed the handle to the top-level window, not the handle that was passed to ::CreateWindowEx.

    CWnd::DeferWindowPos

    Does not exist! Why doesn't this method exist? There is a CWindow::DeferWindowPos, so why isn't there a CWnd::DeferWindowPos?

    CWnd::GetFont

    This message is not supported by all window classes; in particular, it is not supported by classes that are derived directly from CWnd. It is also not supported by certain variants of CStatic. (For more details, see CWnd::SetFont)

    CWnd::Invalidate

    This does not have See Also references to CWnd::InvalidateRect or CWnd::InvalidateRgn

    CWnd::InvalidateRect 

    This does not have a See Also to CWnd::InvalidateRgn, or CWnd::Invalidate.

    CWnd::InvalidateRgn

    This does not have a See Also to CWnd::InvalidateRect or CWnd::Invalidate

    CWnd::OnCtlColor

    The documentation is incomplete and incoherent. It starts off by saying, in the Remarks, that "Most controls send this message to their parent (usually a dialog box)..." whereas the correct statement is "The standard controls send this message to their parent". It also says, in the earlier description, that CTLCOLOR_STATIC is sent by a static control. The correct statement is

    It does not mention that if the HBRUSH is NULL, any changes made to the pDC are discarded. It does not hint at the fact that in MFC, the brush must be allocated in the class that contains the handler, and must not be allocated locally.

    There are no hyperlinks to the WM_xxxCTLCOLOR messages

    CWnd::OnDeviceChange

    There is no option in the Properties for creating a handler for this message.

    The message has a BOOL return type but nothing is said about this return type or its significance. Note that while WM_DEVICECHANGE has two possible values, TRUE and BROADCAST_QUERY_DENY, there is no reference to the message or the return values in this discussion.

     

    CWnd::OnHScroll, CWnd::OnVScroll

    At no point is it mentioned that these messages can come from a spin button control or track bar control. In fact, there are no references to these controls in the See Also section.

    It does not mention that for spin buttons, the nSBCode is SB_THUMBPOSITION and that the nPos parameter is valid for controls whose range is < 16 bits.

    At no point is it mentioned that to test for messages from a track bar control or spin button control, the pScrollBar can be checked against a control variable, e.g., it is possible to write

    if(pScrollbar != NULL && pScrollBar == (CScrollBar *)c_MySpinControl)
        ..

    (It is not obvious to a beginner that this is even possible, let alone sensible)

    CWnd::OnNcHitTest 

    There was an unfortunate and undocumented (and probably unnecessary) change in the return type of the CWnd::OnNcHitTest method. Note that although the header files changed with VS2005, the documents for VS2005 are incorrect; the documentation error was corrected in VS2008. This means that if you upgrade an existing project to VS2005, they will not compile; if you make the upgrade, you can't build under earlier versions. The compatibility change I made was to modify the header file and implementation file as shown below. Now the code will compile under all versions of VS.

    The error is

    è      ON_WM_NCHITTEST()
     
     1>filename(ln) : error C2440: 'static_cast' : cannot convert from 'UINT (__thiscall yourclass::* )(CPoint)' to 'LRESULT (__thiscall CWnd::* )(CPoint)'
     1> Cast from base to derived requires dynamic_cast or static_cast

    Create a header file that specifies the following:

    #if _MFC_VER < 0x0800
    typedef UINT NCHITTEST_RESULT ;
    #else
    typedef LRESULT NCHITTEST_RESULT ;
    #endif

    In the header file for your class, do a #include of the header file you just created, and modify the declaration of the OnNcHitTest handler to be

    ...
    NCHITTEST_RESULT OnNcHitTest(CPoint point);

    in the implementation file

    NCHITEST_RESULT YourClassNameHere::OnNcHitTest(CPoint point)
       {
         ...
       }

    There is also an error in the implementation of WM_NCHITTEST. Normally, if you want to do something like disable the ability to drag a window, you might check the return value from the superclass, and if it is HTCAPTION, return HTNOWHERE. This does not work on Vistra if Aero is enabled. Why this should be so is a mystery, and why it is not documented is more of a mystery. It is not clear what other erroneous behavior occurs with this handler if Aero is enabled. Suggestion to Microsoft: have the implementers read the specifications before writing the code!

    CWnd::OnSetCursor

    The documentation contains the statement

    If nHitTest is HTERROR and message is a mouse button-down message, the MessageBeep member function is called.

    Huh? When? How? Does this mean to say something like this?

    If you return FALSE from this method, and nHitTest is HTERROR and message is a mouse button-down message, the default action is that the MessageBeep member function is called with the parameter <undefined parameter, please supply>.

    CWnd::PreSubclassWindow 

    There are serious implications to how this is called. The PreSublcassWindow virtual method is called from two different locations in the framework. One of them is from the DDX_Control handler, indirectly, when it calls SubclassWindow. In this case, the window already exists. So you can pretty much do whatever you want in the PreSubclassWindow handler.

    But a more serious problem arises if you create the window dynamically by calling CWnd::Create on a variable which is a CWnd-derived type. In this case, a hook is set which is a CBT_HOOK and which intercepts the window creation event. In this case, you are inside a hook-handler. The MFC framework calls PreSubclassWindow from the _AfxCbtFilterHook function in wincore.cpp. The problem here is that if you attempt to do a CreateWindow API (which means calling either Create or CreateEx for any CWnd-derived class, by any number of levels of indirection), this will cause a recursive call on the hook function, and MFC will not allow this. It therefore triggers an assertion failure. However, for reasons that are not fully comprehensible, instead of generating a sane error message, this generates an "unhandled exception: breakpoint" failure. The correct behavior would be to pop up an assertion-failure message when an assertion fails, but that is not what happens, even in debug mode.

    This does not occur if you are attempting to create a window in PreSubclassWindow when an existing window (such as a dialog or CFormView control, as the most common example) is being sublcassed.

    This restriction is not documented in the almost-nonexistent PreSubclassWindow documentation.

    The solution is to not attempt to create another window in the PreSubclassWindow handler.

    CWnd::SetFont

    Must add the information:

    Not all window classes support the WM_SETFONT message. In particular, although this is listed as a member of the CWnd class, a class which is derived directly from CWnd will not support WM_SETFONT, and certain variants (undocumented) of CStatic, including static controls with the style bits SS_xxxFRAME or SS_xxxRECT. Therefore a set of handlers must be declared, and a member variable created to hold the font information.

    class CMyWindow : public CWnd {
       protected:
            afx_msg LRESULT OnSetFont(WPARAM, LPARAM);
            afx_msg LRESULT OnGetFont(WPARAM, LPARAM);
            HFONT m_font;
       ...
    };

    The following declarations must be added to the message map of your class:

    ON_MESSAGE(WM_SETFONT, OnSetFont)
    ON_MESSAGE(WM_GETFONT, OnGetFont)

    and the following handlers must be added

    LRESULT CMyWindow::OnSetFont(WPARAM wParam, LPARAM lParam)
       {
        m_font = (HFONT)wParam;
        if(LOWORD(lParam))
           {
            Invalidate();
            UpdateWindow();
           }
        return 0;
       }
    LRESULT CMyWIndow::OnGetFont(WPARAM, LPARAM)
       {
        return (LRESULT)m_font;
       }  

     

    .DATA (MASM)

    The documentation states

    When used with .MODEL, starts a near data segment for initialized data (segment name _DATA)

    I have no idea what this means. What, exactly, is a "near" segment in Win32 or Win64? There is nothing under .MODEL to suggest that this has any meaning.

    .DATA? (MASM)

    The documentation states

    When used with .MODEL, starts a near data segment for uninitialized data (segment name _BSS)

    I have no idea what this means. What, exactly, is a "near" segment in Win32 or Win64? There is nothing under .MODEL to suggest that this has any meaning.

    DB (MASM)

    The description of this is rubbish. It says


    Can be used to define data such as BYTE.

    DB

    See Also

    Other Resources

    Directives Reference


    Now, it is not clear how this documentation is even vaguely usable. For example, the Directives Reference hyperlink returns to a page in which there is no DB crosslink to anything. The only appearance of "db" at all on the page is the part where it asks for feedback! So how is this usable?

    Now, if it duplicated the documentation of BYTE, it might be useful. For example, if it was described as shown below, it would indicate that some intelligence had been used in writing the description.


    Used to define byte data. This is a synonym for BYTE. It allocates and optionally initializes 1 byte of storage for each initializer.

    [[name]] DB iniitalizer [[, initializer]]

    See Also

    Other Resources

    Directives Reference


    It does not mention that the acceptable range of values for the initializer is -128..255.

    This same error occurs in the DD, DF, DQ and DW documentation. Note that the syntax of name and initializer is not specified anywhere in the documentation.

    DD (MASM)

    The description of this is rubbish. It says


    Can be used to define data such as DWORD.

    DB

    See Also

    Other Resources

    Directives Reference


    Now, it is not clear how this documentation is even vaguely usable. For example, the Directives Reference hyperlink returns to a page in which there is no DD crosslink to anything. So how is this usable?

    Now, if it duplicated the documentation of DWORD, it might be useful. For example, if it was described as shown below, it would indicate that some intelligence had been used in writing the description.


    Used to define doubleword data. This is a synonym for DWORD. It allocates and optionally initializes 4 bytes of storage for each initializer.

    [[name]] DD iniitalizer [[, initializer]]

    See Also

    Other Resources

    Directives Reference


    Does not have a hyperlink to SDWORD or DWORD.

    Perhaps because there is no cross-link to the concept of initializer, it is not at all clear that an initializer for DD can be a floating point number, and therefore it is also able to initialize the equivalent of the C/C++ float with an initialization clause.

    It does not mention the integer range is -2147483648..4294967295.

    This same error occurs in the DB, DF, DQ and DW documentation. Note that the syntax of name and initializer is not specified anywhere in the documentation.

    _DEBUG

    Why does this not have hyperlinks to /MDd, /MLd and /MTd? Why just a hyperlink to /LDd?

    DEFINE_GUID

    This is rather important but its proper use is not documented. In fact, the macro itself is not documented.

    It is polymorphic in that the same DEFINE_GUID declaration is used both to define the GUID and to reference the GUID. By default, it references the GUID.

    To actually create a GUID of your own, you must

    1. Create a header file (e.g., myguid.h) you will use to define the GUID
    2. Create a DEFINE_GUID declaration:
    3. Create a source file that will actually contain the GUID, as shown below
    4. Disable precompiled headers for this file (it should not contain #include "stdafx.h" at the start)

    Using GUIDGEN

    The header file: MyGuid.h

    // {2852F987-BDE4-48ad-A36C-D60BC9F52E9C}
    DEFINE_GUID(myguid, 
    0x2852f987, 0xbde4, 0x48ad, 0xa3, 0x6c, 0xd6, 0xb, 0xc9, 0xf5, 0x2e, 0x9c);

    The GUID source file: MyGuid.cpp (or MyGuid.c)

    #define INITGUID
    #include <guiddef.h>
    #include "myguid.h"

    The above is the source file in its entirety.

    Note that you must select "All Configurations" as shown below

    Using the GUID

    You may now use #include "myguid.h" in any other compilation unit and you will have a GUID defined.

    #include "stdafx.h"
    #include <guiddef.h>
    #include "myguid.h"
    
    BOOL SomeFunction()
       {
        const GUID * guid = &myguid;
        ...
       }

    DeviceIoControl 

    The declaration for DeviceIoControl erroneously gives the parameters as

    BOOL WINAPI DeviceIoControl(
       __in     HANDLE hDevice,
       __in     DWORD dwIoControlCode,
       __in     LPVOID lpInBuffer,
       __in     DWORD nInBufferSize.
       __out    LPVOID lpOutBuffer,
       __in     DWORD nOutBufferSize,
       __out    LPDWORD lpBytesReturned,
       __in     LPOLVERLAPPED lpOverlapped);

    but this is simply incorrect. In a sane world, the declaration would be

    BOOL WINAPI DeviceIoCotnrol(
       __in                               HANDLE hDevice,
       __in                               DWORD dwIoControlCode,
       __in_opt_bcount(nInBufferSize)     LPVOID lpInBuffer,
       __in                               DWORD nInBufferSize.
       __out_opt_bcount(nOutBufferSize)   LPVOID lpOutBuffer,
       __in                               DWORD nOutBufferSize,
       __out                              LPDWORD lpBytesReturned,
       __in_opt                           LPOVERLAPPED lpOverlapped);

    and that is just to be consistent with the erroneously-defined API; the lpInBuffer should be an LPCVOID since it only needs to be const!

    The failure to define these parameters correctly generates "false positives" when /analyze is added to the compilation options in VS2008.

    I suspect that far many more APIs are not annotated correctly with respect to /analyze.

    DF (MASM)

    The description of this is rubbish. It says


    Can be used to define data such as FWORD.

    DB

    See Also

    Other Resources

    Directives Reference


    Now, it is not clear how this documentation is even vaguely usable. For example, the Directives Reference hyperlink returns to a page in which there is no DF crosslink to anything. So how is this usable?

    There is also no explanation of why 6-byte blocks of data have any use.

    Now, if it duplicated the documentation of FWORD, it might be useful. For example, if it was described as shown below, it would indicate that some intelligence had been used in writing the description.


    Used to define six-byte data values. This is a synonym for FWORD. It allocates and optionally initializes 6 bytes of storage for each initializer.

    [[name]] DF iniitalizer [[, initializer]]

    See Also

    Other Resources

    Directives Reference


    This same error occurs in the DB, DD, DQ and DW documentation. Note that the syntax of name and initializer is not specified anywhere in the documentation.

    DisableThreadLibraryCalls 

    The documentation is confusing and misleading.

    It starts with the description:

    This can reduce the size of the working set for some applications.

    A more precise description would be

    This means that DllMain will not be called whenever a thread is created or destroyed. This will improve performance in several ways: it means that useless calls will not be made during thread creation or termination, that the possibility of taking page faults in order to page in the code for DllMain is reduced to zero, and this may additionally result in reducing the working set size of an application.

    Then it provides very confusing information about hModule:

    A handle to the DLL module for which the DLL_THREAD_ATTACH and DLL_THREAD_DETACH notifications are to be disabled. The LoadLibrary, LoadLibraryEx or GetModuleHandle function returns this handle. Note that you cannot call GetModuleHandle with NULL because this returns the base address of the executable image, not the DLL image.

    Besides the obvious failure to match plurality of subject and object, the information is very confusing. Where do we know that the GetModuleHandle, which returns an object of type HMODULE, is giving a base address? What does this have to do with anything? In fact, using the handle provided by LoadLibrary or LoadLibraryEx would be the height of folly, because it says that the loader of the DLL is allowed to disable thread library calls, which would be a design blunder of immense proportions! How could the code that loads a DLL possibly know if it is correct to disable thread callbacks? And it is even worse for GetModuleHandle, which says that any random piece of code is permitted to disable thread callbacks!!!! Who writes this crap????

    The only sensible way to allow thread callbacks to be disabled is if the author of the DLL writes this in DllMain, so the correct documentation would be

    This function should only appear in the DLL_PROCESS_ATTACH handler code in DllMain and the parameter provided is the HMODULE parameter of DllMain. It should never be called for any DLL by code that is not part of the DLL, and calling it from anywhere other than the DLL_PROCESS_ATTACH handler introduces possibilities of serious errors under maintenance.

    It goes on to add

    Do not call this function from a DLL that is linked to the static C run-time library (CRT). The static CRT requires DLL_THREAD_ATTACH and DLL_THREAD_DETACH notifications to function properly.

    The correct documentation would be

    Do not call this function from a DLL that is linked to the static C multithreaded run-time library (MT-CRT). The static multithreaded CRT requires DLL_THREAD_ATTACH and DLL_THREAD_DETACH notifications to function properly (note that these notifications are processed in the CRT code that is executed before DllMain is called). It is erroneous to use a DLL that has the static C single-threaded run-time library (ST-CRT) in a DLL that can be potentially called from multiple threads. It should be assumed that every DLL is potentially used in a multithreaded environment and if static linking is used, the static single-threaded CRT must never be considered.

     

    DISP_E_EXCEPTION

    This is a vague error code. At least one cause of this error code is to call an automation interface to open an Office file and give it a nonexistent filename. The error that comes back from the Invoke method is DISP_E_EXCEPTION. Unfortunately, the MFC handler, OleDisp2.cpp, in the code in the function COleDispatchDriver::InvokeHelperV, detects this error, and eventually calls the following code:

    	if (vtRet != VT_EMPTY)
    	{
    		// convert return value
    		if (vtRet != VT_VARIANT)
    		{
    			SCODE sc = VariantChangeType(&vaResult, &vaResult, 0, vtRet);
    			if (FAILED(sc))
    			{
    				TRACE(traceOle, 0, "Warning: automation return value coercion failed.\n");
    				VariantClear(&vaResult);
    				AfxThrowOleException(sc);
    			}
    			ASSERT(vtRet == vaResult.vt);
    		} 

    The values passed to VariantChangeType are (VT_EMPTY, VT_EMPTY, 0, 0x0009), which are apparently unacceptable to the VariantChangeType function, which returns the error code DISP_E_TYPEMISMATCH, which is then erroneously returned to the caller as if it were the actual error incurred!

    DISP_E_TYPEMISMATCH

    See DISP_E_EXCEPTION for an explanation of why this value is erroneously returned from some calls, in particular, Open calls for opening Office documents via automation!

    _DLL

    This says it is defined only when the multithreaded DLL option is specified. It should add

    This applies to the selection of the C runtime. Note that _MT is also defined.

    and the _MT shall be a hyperlink to that actual entry. There should also be an observation that there is no single-threaded DLL option selectable.

    DllMain

    Does not mention the implication of how DLL_THREAD_DETACH works: a DLL can receive DLL_THREAD_DETACH notifications for threads for which it never received DLL_THREAD_ATTACH notifications; if the threads existed before the DLL was loaded, no notifications will take place.

    Does not mention that CreateThread, LoadLibrary or LoadLibraryEx calls cannot be executed in DllMain.

    Makes the truly embarrassing statement that "Unfortunately, there is not a comprehensive list of safe functions in Kernel32.dll". Seriously, guys, how hard is it to create this list? There are a finite number of APIs in Kernel32.dll, How hard is it, really, to have someone check those out and make the list? There is really no valid excuse for not having done this 20 years ago; to have gone this long without it is simply irresponsible!

    DPtoLP

    This does not work correctly. When used in mode GM_ADVANCED and when there is scaling, there is a serious roundoff error In is also inconsistent with the behavior not using GM_ADVANCED, and is inconsistent with the behavior as specified in the documentation.

    When I used GM_ADVANCED mode, and set the scaling factor to 20, each pixel in the original bitmap displays as a 20-pixel square. That's as expected. But when I put the mouse over the window, I get points in device coordinates in the client space. That's as expected. But I want to convert these to logical coordinates. The obvious solution would be to call DPtoLP, but it gives an erroneous answer. I found similar roundoff errors at a scaling factor of 5. There is different erroneous behavior if I do not use GM_ADVANCED and just use SetMapMode(MM_ISOTROPIC) with SetWindowExt and SetViewportExt.

    These are summarized in the table below:

    x

    20× 5×
    expected GM_ADVANCED MM_ISOTROPIC expected GM_ADVANCED MM_ISOTROPIC
    0 0 -1 0 0 0 0
    1 0 -1 0 0 0 0
    2 0 -1 0 0 0 0
    3 0 -1 0 0 1 1
    4 0 -1 0 0 1 1
    5 0 -1 0 1 1 1
    6 0 -1 0 1 1 1
    7 0 -1 0 1 1 1
    8 0 0 0 1 2 2
    9 0 0 0 1 2 2
    10 0 0 0 2 2 2
    11 0 0 1 2 2 2
    12 0 0 1 2 2 2
    13 0 0 1 2 3 3
    14 0 0 1 2 3 3
    15 0 0 1 3 3 3
    16 0 1 1 3 3 3
    17 0 1 1 3 3 3
    18 0 1 1 3 4 4
    19 0 1 1 3 4 4
    20 1 1 1 4 4 4

    This erroneous behavior is not documented anywhere, and most especially it is not documented in the DPtoLP documentation. And frankly, it doesn't make any sense, by any stretch of the imagination.

    DQ (MASM)

    The description of this is rubbish. It says


    Can be used to define data such as QWORD.

    DB

    See Also

    Other Resources

    Directives Reference


    Now, it is not clear how this documentation is even vaguely usable. For example, the Directives Reference hyperlink returns to a page in which there is no DB crosslink to anything. The only appearance of "db" at all on the page is the part where it asks for feedback! So how is this usable?

    Perhaps because there is no cross-link to the concept of initializer, it is not at all clear that an initializer for DQ can be a floating point number, and therefore it is also able to initialize the equivalent of the C/C++ double with an initialization clause.

    Now, if it duplicated the documentation of QWORD, it might be useful. For example, if it was described as shown below, it would indicate that some intelligence had been used in writing the description.


    Used to define byte data. This is a synonym for QWORD. It allocates and optionally initializes 8 bytes of storage for each initializer.

    [[name]] DQ iniitalizer [[, initializer]]

    See Also

    Other Resources

    Directives Reference


    It does not mention that the allowable integer range is -9223372036854775808..18446744073705551615.

    The same error exists for DB, DF, DD, and DW. Note that the syntax of name and initializer is not specified anywhere in the documentation.

    DrawAnimatedRects

    The idAni parameter is said to specify the "type of animation", and says if you specify IDANI_CAPTION, certain effects will be seen. But the description suggests there are other possible values that might have other effects, but what these values might be and what these effects are is not specified.

    It does not specify in the Remarks section (and there isn't a Remarks section) that this will have no effect if the user has selected the option to have no animations displayed.

    DrawText/DrawTextEx

    Some documentation erroneously states that if the nCount (DrawText) or cchText (DrawTextEx) parameter is 1, it assumes the string is null-terminated. This is incorrect. The nCount/cchText parameter should be -1 to indicate the string is null-terminated. This is correct in the discussion of CDC::DrawText. This has been fixed in later versions.

    In addition, for what is apparently backward-compatibility, the second parameter, the string pointer, is erroneously give as LPCTSTR, in spite of the fact that under some conditions this cannot work at all.

    The erroneous documentation says:

    lpString

    [in] Pointer to the string that specifies the text to be drawn. In the nCount parameter is -1, the string must be null-terminated.

    If uFormat includes DT_MODIFYSTRING, the function could add up to four additional characters to this string. The buffer containing the string should be large enough to accommodate these extra characters.

    Unfortunately, the  correct documentation should say

    lpString

    [in/out] NOTE: ALTHOUGH THIS IS SPECIFIED AS AN LPCTSTR, THIS IS ERRONEOUS AND IS MAINTAINED FOR BACKWARD COMPATIBILITY WITH EXISTING CODE. IT IS NOT ACTUALLY CORRECT BECAUSE THIS PARAMETER CAN BE AN 'OUT' PARAMETER AS WELL.

    If DT_MODIFYSTRING is not specified, this parameter can point to a constant string. However, if DT_MODIFYSTRING is specified (along with either DT_END_ELLIPSIS or DT_PATH_ELLIPSIS) the function will modify the string, in which case (a) it cannot be a pointer to a constant string, or an access fault will occur, and (b) since the modification can add up to four characters to the string, the buffer must be at least four characters longer than the input text to allow for this modification. Under these conditions, the parameter should be treated as an LPTSTR, not as an LPCTSTR. Note that if the buffer is not properly allocated so that it can accommodate the additional characters, the integrity of the program is compromised and the program can crash, and in extreme cases the security of the system running the code can be compromised.

    NOTE THAT WHEN EXTENDED COMPILER CHECKING (/W4 or /WALL), OR /ANALYZE ARE USED, THE COMPILER OR PREFAST WILL ERRONEOUSLY ALLOW A CONSTANT STRING TO BE PASSED IN EVEN IF DT_MODIFYSTRING IS SPECIFIED. THE RESULT WILL BE CODE WHICH CAN FAIL AND POTENTIALLY PRESENT A SECURITY HAZARD.

    (Thanks to Giovanni Dicanio for pointing out an ongoing discussion in another newsgroup)

    Note that in my rewrite I replaced the wishy-washy "should be" large enough with the much more forceful "must be" large enough, and emphasized the "must".

    DT (MASM)

    The description of this is rubbish. It says


    Can be used to define data such as TBYTE.

    DB

    See Also

    Other Resources

    Directives Reference


    Now, it is not clear how this documentation is even vaguely usable. For example, the Directives Reference hyperlink returns to a page in which there is no DT crosslink to anything. So how is this usable?

    Now, if it duplicated the documentation of TBYTE, it might be useful. For example, if it was described as shown below, it would indicate that some intelligence had been used in writing the description.


    Used to define byte data. This is a synonym for TBYTE. It allocates and optionally initializes 10 bytes of storage for each initializer.

    [[name]] DT iniitalizer [[, initializer]]

    This can be used to define 10-byte floating point types, the equivalent of what the C/C++ compiler would have done if it supported the long double data type.

    A floating point number can specify <<value here>> digits of precision, and the allowable range for a floating point number is 3.37×10-4392..1.18×104392.

    See Also

    Other Resources

    Directives Reference


    It does not mention that the acceptable range of values for the initializer is -128..255.

    This same error occurs in the DD, DF, DQ and DW documentation. Note that the syntax of name and initializer is not specified anywhere in the documentation.

    DW (MASM)

    The description of this is rubbish. It says


    Can be used to define data such as WORD.

    DB

    See Also

    Other Resources

    Directives Reference


    Now, it is not clear how this documentation is even vaguely usable. For example, the Directives Reference hyperlink returns to a page in which there is no DB crosslink to anything. The only appearance of "db" at all on the page is the part where it asks for feedback! So how is this usable?

    Now, if it duplicated the documentation of WORD, it might be useful. For example, if it was described as shown below, it would indicate that some intelligence had been used in writing the description.


    Used to define byte data. This is a synonym for WORD. It allocates and optionally initializes 2 bytes of storage for each initializer. For signed values, the SWORD declaration can be used.

    [[name]] DW iniitalizer [[, initializer]]

    See Also

    Other Resources

    Directives Reference


    The same error exists for DB, DD, DF, and DQ. Note that the syntax of name and initializer is not specified anywhere in the documentation.

    DWORD (MASM)

    Contains no hyperlink to SDWORD. Does not contain a hyperlink to DD.

    Does not state that the allowable range is -2147483648..4294967295. Does not indicate if floating-point initializers are valid.

    ECHO (MASM)

    This is said to be the same as %OUT, but %OUT is said to be the same as ECHO, but they both have different syntax. It makes no sense.

    Unfortunately, the documentation is also confusing. The syntax for ECHO is

    ECHO message

    but the syntax for %OUT is

    %OUT

    That is, it has no message parameter. Either they are synonyms, or they are different, and if they are the same, they would have the same syntax. Doesn't anyone actually read this documentation to see if it makes sense?

    EM_GETCUEBANNER

    This has to be one of the worst designs I have seen recently; what is more unforgivable is that it was designed recently, and is not a throwback to Win16 and some brain-dead design decisions.

    In an intelligent world, the design would have been that if WPARAM, LPARAM were specified as 0,0 it would return the size of the buffer required (including the terminal NUL character) and if they were non-0 it would return the count of characters actually returned (not including the NUL character). Proving once again that software evolves, because there is no trace of intelligent design to be found here.

    It is not clear why an ANSI version of this is not supported.

    EM_GETCUEBANNER does not contain a hyperlink to EM_SETCUEBANNER.

    See also CEdit::GetCueBanner

    EM_SETCUEBANNER

    Does not contain a hyperlink to EM_GETCUEBANNER.

    EMCOLORMATCHTOTARGET

    This does not exist. Only EMRCOLORMATCHTOTARGETW exists.

    EMRCREATECOLORSPACE

    This is missing a hyperlink to LOGCOLORSPACE. In addition, it is almost certainly not a LOGCOLORSPACE value, which has a TCHAR field; the type of the field would depend upon the creator, not the reader, and consequently, it is most likely that it is a LOGCOLORSPACEA, but this is unspecified.

    EMREOF

    The offPalEntries says, vaguely, "offset to palette entries". The correct documentation is, "The offset in bytes, from the beginning of the structure, to an array of PALETTEENTRY values. Note that the offset might be nonzero even if nPalEntries is 0."

    The nSizeLast documentation is a bit vague, and the documentation would be helped by a picture, e.g.,

           +----------------+  - - - - - - - - - - - - - - - - - - - - - - - -
           | EMR_EOF        |          ^                         ^
           +----------------+          |                         |
           | .nSize         |          |                         |
           +----------------+          | offPalEntries           |
           | .nPalEntries   |          |                         | offPaleEntries +
           +----------------+          |                         |   nPalEntries * sizeof(PALETTEENTRY)
           | .offPalEntries |          V                         |
           +----------------+  - - - - - - - -                   |
           |                | \                                  |
           :                :  > .nPalEntries * sizeof(RGBQUAD)  |
           |                | /                                  V
           +----------------+  - - - - - - - - - - - - - - - - - - - - - - - -
           | .nSizeLast     |
           +----------------+
    

    EMRGRADIENTFILL

    This documentation is confusing and erroneous. The specification of the Ver[1] field is "Pointer to an array of TRIVERTEX structures that each define a triangle vertex". This doesn't make any sense at all, because it is most definitely not a pointer. It is the first element of a TRIVERTEX array. A TRIVERTEX most definitely does not define "a triangle vertex", it defines a vertex, which may also be a rectangle vertex.

    In the remarks it states "The nVer element designates the beginning of the variable-length area"; the nVer element does no such thing; it is actually the count of the number of vertices. The correct documentation is "The Ver element designates the beginning of the variable-length area". The documentation also erroneously refers to the ulMode field as the jlMode field. The documentation does not specify what the ulMode actually is; it should explicitly state that the ulMode is one of GRADIENT_FILL_H, GRADIENT_FILL_V, or GRADIENT_FILL_TRIANGLE.

    The documentation would be immensely helped by a picture

           +--------------+
           |    EMR emr   |
           :              :
           |              |
           +--------------+
           |  DWORD nVer  |
           +--------------+
           |  DWORD nTri  |
           +--------------+
       +---+ ULONG ulMode +---------------------------------------------------+
       |   +--------------+    +--------------+   - - - - - - - - - - - - - - | - - - 
       |   | TRIVERTEX    |    | TRIVERTEX[0] | \                             |   ^
       |   |     Ver[1]   |    |              |  |                            |   |
       |   +--------------+    +--------------+  |                            |   | 
       |                       | TRIVERTEX[1] |  |                            |   | 
       |                       |              |  |                            |   |
       |                       +--------------+  |                            |   |
       |                       |              |   > nVer                      |   | ((nVer - 1) * sizeof(TRIVERTEX))
       |                       :              :  |                            |   |
       |                       :              :  |                            |   |
       +->GRADIENT_FILL_RECT_H |              |  |                            |   |
          GRADIENT_FILL_RECT_V +--------------+  | GRADIENT_FILL_TRIANGLE <---+   |
                   |           | TRIVERTEX    |  |        |                       |
                   v           |     [nVer-1] | /         V                       V
           +--------------+ - -+--------------+ - -+--------------+ - - - - - - - - - - - ((char*)&Ver) + (nVer - 1) * sizeof(TRIVERTEX)
           | GRADIENT_RECT|                        | GRADIENT_TRI-|
           |   [0]        |                        | ANGLE[0]     |
           +--------------+                        +--------------+
           |              |                        |              |
           :              :                        :              :
           |              |                        |              |
           +--------------+                        +--------------+
           | GRADIENT_RECT|                        | GRADIENT_TRI-|
           |  [nTri-1]    |                        | ANGLE[nTri-1]|
           +--------------+                        +--------------+

    EMRMODIFYWORLDTRANSFORM

    There is a word transformation in the description which is apparently a typo.

    The iType is said to be one of the values MWT_IDENTITY (1), MWT_LEFTMULTIPLY (2) or MWT_RIGHTMULTIPLY (3). However, in real metafiles, the numerical value 4 is found, which has no MWT_ symbol defined for it.

    EMRPOLYDRAW

    The description of the structure is nonsense. It is a feeble attempt by someone who didn't understand C to try to explain something using a C structure that cannot be explained using a C structure. It even states the PT_CLOSEFIGURE is added using the bitwise-XOR operator, which is gibberish.

    The correct documentation should be completely rewritten as

    typedef struct tagEMRPOLYDRAW {
      EMR    emr; 
      RECTL  rclBounds; 
      DWORD  cptl; 
      BYTE    Data[1]; 
    } EMRPOLYDRAW, *PEMRPOLYDRAW; 

    And state, as follows

    Data[1]

    An array of cptl POINTL structures, representing 32-bit data points expressed in logical units. Following this array, there will be an array cptl bytes, representing how each of the corresponding POINTL values should be interpreted. The array of bytes will be one of PT_MOVETO, PT_LINETO or PT_BEZIERTO, and for the values PT_LINETO and PT_BEZIERTO, the PT_CLOSEFIGURE flag could be ORed in using the bitwise-OR operator.

    The actual structure, as currently implemented, is (ignoring the completely nonsensical abTypes field at the end of the structure)

          +-------------+
          |     emr     |
          |             |
          +-------------+-----------+
          |  rclBounds  | .left     |
          |             | .top      |
          |             | .right    |
          |             | .bottom   |
          +-------------+-----------+
          |    cptl     |
          +-------------+-----------------+  - - - - - - - - - - 
          |   aptl      | aptl[0]         |                  ^
          +-------------+-----------------+                  |
                        | aplt[1]         |                  |
                        +-----------------+                  |
                        |                 |                  | cptl * sizeof(POINTL)
                        :                 :                  |
                        |                 |                  |
                        +-----------------+                  |
                        | aplt[cptl-1]    |                  v
                        +-----------+-----+  - - - - - - - - - - ((char*)&aptl[0]) + cptl * sizeof(POINTL)
                        | ty[0]     |                        ^
                        +-----------+                        |
                        |           |                        |
                        :           :                        |   cptl * sizeof(BYTE)
                        |           |                        |
                        +-----------+                        |
                        | ty[cptl-1]|                        v
                        +-----------+  - - - - - - - - - - - - -

    EMRPOLYDRAW16

    The description of the structure is nonsense. It is a feeble attempt by someone who didn't understand C to try to explain something use a C structure that cannot be explained using a C structure.

    The correct documentation should be completely rewritten as

    typedef struct tagEMRPOLYDRAW {
      EMR    emr; 
      RECTL  rclBounds; 
      DWORD  cptl; 
      BYTE    Data[1]; 
    } EMRPOLYDRAW, *PEMRPOLYDRAW; 

    And state, as follows

    Data[1]

    An array of cptl POINTS structures, representing 32-bit data points expressed in logical units. Following this array, there will be an array cptl bytes, representing how each of the corresponding POINTL values should be interpreted. The array of bytes will be one of PT_MOVETO, PT_LINETO or PT_BEZIERTO, and for the values PT_LINETO and PT_BEZIERTO, the PT_CLOSEFIGURE flag could be ORed in using the bitwise-OR operator.

    The actual structure, as currently implemented, is (ignoring the completely nonsensical abTypes field at the end of the structure)

          +-------------+
          |     emr     |
          |             |
          +-------------+-----------+
          |  rclBounds  | .left     |
          |             | .top      |
          |             | .right    |
          |             | .bottom   |
          +-------------+-----------+
          |    cpts     |
          +-------------+-----------------+  - - - - - - - - - - 
          |    apts     |    apts[0]      |                  ^
          +-------------+-----------------+                  |
                        |    apts[1]      |                  |
                        +-----------------+                  |
                        |                 |                  | cpts * sizeof(POINTS)
                        :                 :                  |
                        |                 |                  |
                        +-----------------+                  |
                        | apts[cpts-1]    |                  v
                        +-----------+-----+  - - - - - - - - - - ((char*)&apts[0]) + cpts * sizeof(POINTS)
                        | ty[0]     |                        ^
                        +-----------+                        |
                        |           |                        |
                        :           :                        |   cpts * sizeof(BYTE)
                        |           |                        |
                        +-----------+                        |
                        | ty[cpts-1]|                        v
                        +-----------+  - - - - - - - - - - - - -
    

     

    EMRPOLYPOLYLINE, EMRPOLYPOLYGON

    The structure is nonsensical. It isn't at all clear what is going on here. Since it is impossible to have variable-length arrays in structures, having two adjacent variable-length structures cannot happen. The document might explain it as I think it is intended:

    aPolyCounts

    This is the first element of an array of DWORD values that be the lpdwPolyPoints parameter to PolyPolyLine. Following this array is an array of POINTL values.

    aptl

    This is a "placeholder" and is not really a valid array element. The first element of the array would follow the aPolyCounts array

    +----------------------+
    | EMR emr              |
    :                      :
    |                      |
    +----------------------+-------------+
    | RECTL rclBounds      | LONG left   |
    |                      | LONG top    |
    |                      | LONG right  |
    |                      | LONG bottom |
    +----------------------+-------------+
    | DWORD nPolys         |
    +----------------------+
    | DWORD cptl           |
    +----------------------+----------------------+  - - - - - - - - - - - - - - - - - -  
    | DWORD aPolyCounts[1] | aPolyCounts[0]       | \                      ^           
    +----------------------+----------------------+  |                     |
    | DWORD aptl[1]        | aPolyCounts[1]       |  |                     |
    +----------------------+----------------------+  |                     |
                           | aPolyCounts[2]       |  |                     |
                           +----------------------+   > nPolys             | nPolys * sizeof(DWORD)
                           |                      |  |                     |    
                           :                      :  |                     |
                           |                      |  |                     |
                           +----------------------+  |                     |
                           | aPolyCounts[nPolys-1]| /                      V
                           +----------------------+-----------------+  - - - - - - - -   ((char*)&aPolyCounts[0]) + nPolys * sizeof(DWORD)
                                                  | POINTL [0]      | \
                                                  +-----------------+  |
                                                  | POINTL [1]      |  |
                                                  +-----------------+  |
                                                  |                 |   > cptl
                                                  :                 :  |
                                                  |                 |  |
                                                  +-----------------+  |
                                                  | POINTL [cptl-1] | /   
                                                  +-----------------+

    EMRPOLYPOLYLINE16, EMRPOLYPOLYGON16

    See EMRPOLYPOLYLINE, EMRPOLYPOLYGON; the only change is the array of points is a POINTS array.

    EMRSETICMPROFILEA, EMRSETICMPROFILEW

    The documentation states the parameters, but they are incomplete, inaccurate, misleading and/or confusing.

    dwFlags

    Profile flags. [The meaning of this field is not documented, and there is no equivalent field in the SetICMProfile API]

    cbName

    Size of the desired profile name. [It is not stated if this is in characters or bytes, and for EMRSETICMPROFILEW, this is confusing. It also does not suggest if the string is NUL-terminated, and if it is, if this length includes the terminal NUL character]

    cbData

    Size of profile data, if attached [This is probably a correct description, but the nature of the profile data is unspecified and there is no hyperlink suggesting where the specification might be found]

    Data[1]

    Array size is cbName and cbData [This description doesn't even make sense. The correct description might be "This is the data; it represents the start of a variable-length array whose length is cbName + cbData, where the name, if cbName is non-zero, appears first." It might also state whether or not the cbName includes a terminating NUL character, and if there is a terminal NUL character present. In fact, could it be the equivalent of a MULTISZ sequence where each string ends with a NUL character but the entire sequence of strings ends with a sequece of two NUL character? Inquiring Minds Want To Know!]

    EMRSELECTOBJECT

    This states that the ihObject is the index to a handle in the handle table, but neglects to mention that the ENHMETA_STOCK_OBJECT bit (0x80000000) bit can be present, in which case the low-order bits are interpreted as a stock object value from the selected values GetStockObject. Not only should it state this, but there should be a hyperlink to GetStockObject.

    EMRSELECTPALETTE

    The ihObject field of this apparently can have the ENHMETA_STOCK_OBJECT bit ORed it to select a stock palette. See EMRSELECTOBJECT.

    EMRTEXT

    The offString and offDX parameters say they are the "offset to the string" and "offset to the spacing information" but this is incomplete. The correct documentation is

    offString

    The offset of the string, in bytes, measured from the beginning of the EMREXTTEXTOUTA or EMREXTTEXTOUTW structure. For EMREXTTEXTOUTA, this will point to a sequence of nChars 8-bit characters, and for EMREXTTEXTOUTW, this will point to a sequence of nChars Unicode characters. These characters are not NUL-terminated.

    offDX

    The offset of the intercharacter spacing array, measured from the beginning of the EMREXTTEXTOUTA or EMREXTTEXTOUTW structure. This points to an array of (nChars - 1) DWORD values which represent the intercharacter spacing. The values in this array will be DWORD-aligned.

    ENHMETAHEADER

    The offDescription states that it is a pointer to "the array that contains the description of the enhanced metafile's contents". The correct description should be "Specifies the offset from the beginning of the ENHMETAHEADER structure to a string of Unicode characters that contains the description of the enhanced metafile contents"

    Enable3dControls

    While it states these are obsolete, what the documentation does not state is that these calls do appear in 32-bit code generated by earlier versions of 32-bit Visual Studio (in spite of the fact that the documentation says "Their functionality is incorporated into Microsoft's 32-bit operating systems". It is also worth pointing out that there is actual code in the VS6 versions (see file Program Files\Microsoft Visual Studio\vc98\mfc\src\app3d.cpp). So the fix for this is to add the additional conditional to the code below:

    #if _MFC_VER < 0x0700                                                                    // Add this line 
    #ifdef _AFXDLL                                                                                            
            Enable3dControls();                     // Call this when using MFC in a shared DLL               
    #else                                                                                                     
            Enable3dControlsStatic();       // Call this when linking to MFC statically                       
    #endif                                                                                                    
    #endif // _MFC_VER < 0x0700                                                              // Add this line 
    

    Enable3dControlsStatic

    See Enable3dControls.

    EncodePointer

    Does not say what happens if a NULL pointer is passed in. Ideally,

    void * p = EncodePointer(malloc(size));

    should be NULL if malloc returns NULL.

    Experimentation shows that it does not. This should be mentioned in the documentation.

    EnterCriticalSection

    This contains a number of problems. For example, it says it can "raise EXCEPTION_POSSIBLE_DEADLOCK" but it fails to point out that this is a RaiseException call and is not part of the C++ exception handling mechanism, which is not always obvious to most readers. It does not mention that EXCEPTION_POSSIBLE_DEADLOCK was not defined until VS2008, and in earlier versions of Windows it was defined as STATUS_POSSIBLE_DEADLOCK. The documentation, that it was available in earlier platforms, suggests that it was defined in earlier Platform SDKs for those platforms, and that is not true.

    As a piece of user-friendly documentation, every change to a Platform SDK file or for that matter, any other library source file Microsoft distributes, should be logged in a comment at the start of the file, indicating the version level of the file and the changes that were made.

    It says that under low memory situations, "EnterCriticalSection can raise an exception", but fails to say what exception is raised. It suggests using InitializeCriticalSectionAndSpinCount, but at no point does that API specify what the spin count is, what it means, or what the default spin count is assumed to be.

    It specifies that the timeout interval for a CRITICAL_SECTION is specified in a Registry key, but it neglects to say what the units are.

    EnumChildWindows 

    There are interesting effects on the enumeration if the hwndParent is either HWND_MESSAGE or HWND_DESKTOP

    EQU (MASM) 

    Does not contain a cross-reference to the "=" directive (for redefinable values)

    EXPORTS

    This linker directive, in the .def file, has confusing and misleading documentation. Some versions of the documentation suggest that a dense set of integers is required, which is not true.

    From reader Jim Beveridge:

    The EXPORTS section of a .def triggers some undocumented behavior that causes the generated LIB file to contain undecorated names. See my article on the subject (near the end – the “undecorated” attribute):

    http://qualapps.blogspot.com/2007/08/how-to-create-32-bit-import-libraries.html
     

    extern

    In the section labeled "Using extern to Specify Linkage", the example is stupidly misleading, and was clearly written by someone who understood nothing about how the C header files work. It suggests that you need to write

    extern "C" {
    #include <stdio.h>
    }

    which has to rank among the most incredibly stupid examples presented. This is because there is no reason to have to include stdio.h in the scope of an extern "C" declaration!

    The crucial information that is required is not given, for example.

    To create a header file for a C-based library that can be used for both C and C++, you need to put the extern "C" in conditionally, since it will not be recognized by the C compiler. The way to do this is to use the __cplusplus symbol to conditionally include the extern "C" only in C++ builds:

    #ifdef __cpluspllus
    extern "C" {
    #endif
    ....your declarations here
    #ifdef __cplusplus
    } // extern "C"
    #endif

    It also does not mention that the notation

    extern "C" {
    declaration1;
    declaration2;
    }

    can be equally written as

    extern "C" declaration1;
    extern "C" declaration2;

    And therefore could be handled by writing

    #ifdef __cplusplus
    #define MY_EXPORT extern "C" 
    #else
    #define MY_EXPORT 
    #endif

    and therefore you can write in the header file

    MY_EXPORT declaration(args);

    and at the definition point

    MY_EXPORT declaration(args)
       {
        ... function body
       }

    It concludes with the completely irresponsible and out-and-out erroneous example of declaring the variable errno as an extern int, which is nonsensical, because errno is usually not a variable (particularly, in the multithreaded C library), and it suggests that errno should be explicitly declared by the user ( a holdover from the early K&R C compilers; hello, has anyone been paying attention to the fact that we are not using K&R C any longer?), instead of doing #include <errno.h> which would be the correct way to handle it in the modern world. Anyone who learned to program after 1988 would know that you do not declare errno as an extern int and besides, in the multithreaded header file, it is declared as

    _CRTIMP extern int * __cdecl _errno(void);
    #define errno (*_errno())

    so the indicated declaration cannot possibly ever be right!

    The examples of using trivial stdio examples by renaming the functions in trivial ways to illustrate the point is truly awful. There is also no rationale of why you would want to do this; the notion that this would be required by a static library or a DLL that is to be used by either C or C++ code is not actually mentioned.

    Correct documentation would state that this is only required to access libraries that use C-style naming conventions; typically, this means a library (static or DLL) that has been built with the C compiler, but it should also mention that this is a technique to allow DLLs built using C++ to suppress the C++ "name mangling" and therefore simplify the use of GetProcAddress. It should further mention that this means that such function names cannot be overloaded using C++ language overload rules. GetProcAddress should contain a hyperlink to this page.

    EXTERN (MASM)

    The documentation for this is

    EXTERN [[langtype]] name [[(altid)]] : type [[, [[langtype]] name [[(altid)]] : type]] ...

    Now, this would be a lot more useful if there was some way to know what permissible values could be used for langtype, what an altid was and when you would use it, and what values other than ABS were permitted for type. It also fails to mention that if ABS is a constant, it is a link-time constant, which is not at all the same as a compile-time constant.

    Apparently, this documentation is only used inside Microsoft, where the reader can walk down the hall to the MASM maintainer, or read the source, to find out what is permitted here. Unfortunately, for the Rest Of Us, it isn't that easy.

    Examination of the .cod output from the C/C++ compiler suggests that one of the permissible types is PROC. So we now have PROC and ABS. I wonder what else is permitted?

    ExtTextOut

    From a conversation on microsoft.public.vc.mfc. Question from "TheOne" and response by Mark Salsbery:

    If the string pointer str is given as NULL and the nOptions contain ETO_OPAQUE, the operation just clears the rectangle specified by lpRect to the background color established by SetBkColor.

    FAR

    All occurrences of the word FAR in any definition, anywhere, are erroneous; this keyword died when Win32 was created, and its continued appearance confuses new users, who don't know that it is nonsense. The word should simply be dropped entirely from all usages, everywhere. It is still used extensively in the documentation and in the header files. See also PASCAL.

    __FILE__

    This example is excruciatingly poorly written. It only shows how to get a Unicode string, not a Unicode-aware string. The corrected example should be

    #include <tchar.h>
    #define __TFILE__ _T(__FILE__)
    TCHAR * filename = __TFILE__;

    FIXED 

    This type is undocumented, although it is used in critical definitions, such as MAT2.

    The type for an X86 is defined in wingdi.h:

    typedef struct _FIXED {
        short   value;
        WORD    fract;
    } FIXED;

    The value part is a number in the range of -32768..32768 which represents the integer part of the range.

    The fract part is a number in the rnage of 0..65535 which represents the interpolation between two values; it is accurate to one part in 65536. That is, "2.5" in floating point would be represented as <2, 32768>.

    .FPO 

    This MASM has the following documentation:

    FPO (cdwLocals, cdwParams, cbProlog, cbRegs, fUseBP, cbFrame)

    Parameters

    cdwLocals

    Number of local variables, an unsigned 32 bit value.

    cdwParams

    Size of the parameters, an unsigned 16 bit value.

    cbProlog

    Number of bytes in the function prolog code, an unsigned 8 bit value.

    cbRegs

    Number of bytes in the function prolog code, an unsigned 8 bit value.

    fUseBP

    Indicates whether the EBP register has been allocated. either 0 or 1.

    cbFrame

    Indicates the frame type. See http://msdn.microsoft.com/en-us/library/ms679352.aspx for more information.

    Notice that both the cbProlog and cbRegs have exactly the same documentation, yet there is no explanation as to why something designated as "regs" would represent bytes of prolog code. Also, fUseBP indicates "whether the EBP register has been allocated" but gives no explanation about what this means. Clearly, in an FPO function, the EBP is not used, so we are left to guess what might be mean by "allocated". Perhaps it means "Indicates if the contents of the EBP register can be modified by the suburoutine, thus rendering in meaningless", but who knows?

    FreeHeap

    NOTE: DO NOT CONFUSE THIS WITH HeapFree, WHICH IS DIFFERENT!

    The FreeHeap API describing what should be a typedef to a function. The problem is that its description is completely nonsensical in one critical area. It states

    This function does not return a value. However, if the function sets Base to NULL, the buffer was freed. If Base is not NULL after the function call ends, the buffer could not be freed.

    Note the definition

    VOID FreeHeap(__int_out PVOID Base)

    Now how could this possibly set Base to NULL? Only someone totally functionally illiterate in programming could possibly think that this description makes sense! It is IMPOSSIBLE for Base to be set to NULL!!!!

    The correct definition is

    The FreeHeap function is a member of the SECPKG_DLL_FUNCTIONS structure and specifies a pointer to the function to be called to deallocate memory previously allocated by the function specified by the AllocateHeap member of the SECPKG_DLL_FUNCTIONS structure

    typedef VOID (NTAPI LSA_FREE_LSA_HEAP) (IN PVOID Base);

    Note that because the function calling convention is NTAPI, ordinary C functions like malloc or new could not be specified.

    Note that the absolutely essential calling convention type is not specified!

    See also AllocateHeap

    ftell

    This code contains a serious bug, such that in a "text" mode file (that is, fopen(filename, "a") and other variants that include "a" in the mode), if the file was created on a Unix system, the code behaves incorrectly.

    The error is in the way it computes the file position. It erroneously assumes that every \n must be preceded by a \r, and consequently has a loop of the form (found in the ...\crt\src\ftell.c file):

                                            for (p = stream->_base; p < max; p++)
                                                    if (*p == '\n')
                                                            /* adjust for '\r' */
                                                            rdcnt++;

    incorrectly adjusts when the file is a Unix-format file that does not have a \r before each \n. The correct code would look for the actual \r character instead of \n, or, alternatively, the test should see if each \n has a \r preceding it before incremented the rdcnt variable. As the code stands, it will not work correctly for a Unix-formatted file. Of course, it is likely that if this bug is reported it will be designated as "this is by design", which is a weasly way to say "we screwed it up, and because we screwed it up, we declare the screwup to be the correct design" (Q: How many Microsoft support people does it take to change a light bulb? A: None. They declare that the darkness is "by design")

    __FUNCDNAME__, __FUNCSIG__, __FUNCTION__

    These preprocessor symbols do not specify what version of Visual Studio they are defined in (they are not, for example, defined in VS6, but are in VS.NET 2003), and more importantly, do not say which value of _MSC_VER for which they are defined. Furthermore, macros do not "return" values, they are "defined as" values! Good documentation would say something like the following

    __FUNCDNAME__ Valid only within a function. A quoted string which is the decorated name of the enclosing function (as an 8-bit character string; see below to get a Unicode-aware string). Defined only for _MSC_VER >= 1300. Not expanded if the /EP or /P compiler options are specified.

    This generates an 8-bit quoted string. If a Unicode string is required or a Unicode-aware string is required, you must use a level of indirection as shown below

    #include<tchar.h>
    #define __TFUNCDNAME__ _T(__FUNCDNAME__)

    or it could be written as shown below, which is a trace statement indicating entry to a function which takes an integer parameter

    TRACE(_T("=>") _T(__FUNCDNAME__) _T("(%d)\n"), parameter);

    An example using 8-bit characters is

    class Test {
        public:
        void Show() {
                     printf("__FUNCTION__ [%s]\n", __FUNCTION__);
                     printf("__FUNCDNAME__ [%s]\n", __FUNCDNAME__);
                     printf("__FUNCSIG__ [%s]\n", __FUNCSIG__);
                    }
    };

    produces

    __FUNCTION__ [Test::Show]
    __FUNCDNAME__ [?Show@Test@@QAEXXZ]
    __FUNCSIG__ [void __thiscall Test::Show(void)]
    __FUNCSIG__ Valid only within a function. A quoted string which is the signature of the enclosing function (as an 8-bit character string; see below to get a Unicode-aware string). Defined only for _MSC_VER >= 1300/ Not expanded if the /EP or /P compiler options are specified.

    This generates an 8-bit quoted string. If a Unicode string is required or a Unicode-aware string is required, you must use a level of indirection as shown below

    #include<tchar.h>
    #define __TFUNCSIG__ _T(__FUNCSIG__)

    or it could be written as shown below, which is a trace statement indicating entry to a function which takes an integer parameter

    TRACE(_T("=>") _T(__FUNCSIG__) _T("(%d)\n"), parameter);

    An example using 8-bit characters is

    class Test {
        public:
        void Show() {
                     printf("__FUNCTION__ [%s]\n", __FUNCTION__);
                     printf("__FUNCDNAME__ [%s]\n", __FUNCDNAME__);
                     printf("__FUNCSIG__ [%s]\n", __FUNCSIG__);
                    }
    };

    produces

    __FUNCTION__ [Test::Show]
    __FUNCDNAME__ [?Show@Test@@QAEXXZ]
    __FUNCSIG__ [void __thiscall Test::Show(void)]
    __FUNCTION__ Valid only within a function. A quoted string which is the undecorated name of the enclosing function (as an 8-bit character string; see below to get a Unicode-aware string). Defined only for _MSC_VER >= 1300. Not expanded if the /EP or /P compiler options are specified.

    This generates an 8-bit quoted string. If a Unicode string is required. or a Unicode-aware string is required, you must use a level of indirection as shown below

    #include <tchar.h> 
    #define __TFUNCTION__ _T(__FUNCTION__)

    or it could be written as shown below, which is a trace statement indicating entry to a function which takes an integer parameter

    TRACE(_T("=>") _T(__FUNCTION__) _T("(%d)\n"), parameter);

    An example using 8-bit characters is

    class Test {
        public:
        void Show() {
                     printf("__FUNCTION__ [%s]\n", __FUNCTION__);
                     printf("__FUNCDNAME__ [%s]\n", __FUNCDNAME__);
                     printf("__FUNCSIG__ [%s]\n", __FUNCSIG__);
                    }
    };

    produces

    __FUNCTION__ [Test::Show]
    __FUNCDNAME__ [?Show@Test@@QAEXXZ]
    __FUNCSIG__ [void __thiscall Test::Show(void)]

    GCP_RESULTS

    This is poorly documented.

    lStructSize Specifies the size, in bytes, of the structure. [no change here]
    lpOutString Pointer to a buffer that receives the output string. This may be NULL if the output string is not needed. This buffer must be at least the value of nCount+1 where nCount is the parameter to GetCharacterPlacement

    The output string is a version of the original string, in the order that the characters will be displayed on the specified device associated with the HDC. Typically, the output string is identical to the original string, but may be different if the string needs reordering and the GCP_REORDER flag is set, or if the original string exceeds the maximum extent and GCP_MAXEXTENT flag is set.

    The output string will be NUL-terminated, and therefore the buffer size allocated must be equal to the number of characters in the input string, plus one.

    lpOrder A pointer to the array that receives the ordering indices, or NULL if the ordering indices are not needed. The length of the array should be set to the number of characters in the nCount parameter of GetCharacterPlacement. The meaning of the order array depends on other elements of GCP_RESULTS
    • If the glyph indexes are to be returned, the indexes are the order for interpreting the lpGlyphs array
    • If the glyph indexes are not returned, and lpOrder is non-NULL, the indexes are positions in the lpOutString array. In this case, the value of lpOrder[i] is the position of lpString[i] in the output string lpOutString

    The typical usage of this would be to obtain the ordering if GetFontLanguageInfo returns the GCP_REORDER flag, which indicates that the original string requires reordering for display.

    lpDX An array of int values. The length of the array is the number of characters in the nCount parameter for GetCharacterPlacement. This pointer may be NULL if these distances are not needed. If glyph rendering is done, the distances are for the glyphs, and not the characters, so the resulting array in this case can be used with the ExtTextOut function.

    The distances in this array appear in display order. To find the distance for the ith character in the original string, the lpOrder array can be used as follows:

    width = lpDx[lpOrder[i]];
    lpCaretPos Pointer to the array that receives the caret position values. The length of this array should be at least as long as the number of characters specified by the nCount parameter for GetCharacterPlacement. This member may be NULL if the caret positions are not needed. Each value specifies the caret position immediately before the corresponding character. In some languages the position of the caret for each character may not be immediately to the left of the character; for Hebrew, a right-to-left-reading language, the before position is to the right of the character. If glyph ordering is done, lpCaretPos matches the original input string, not the output string. In this case, some adjacent values may be the same (if, for example, there is a mix of right-to-left and left-to-right text).

    The values in the array are in input order. To find the caret position value for the ith character in the original string, use the array as follows

    position = lpCaretPos[i];
    lpClass Pointer to an array that contains and/or receives character classifications. The length of this array should be at least as long as the number of characters specified by the nCount parameter for GetCharacterPlacement. If the GCP_CLASSIN flag is specified for GetCharacterPlacement, this array must be initialized to the character class flags desired. [rest of text]
    lpGlyphs Pointer to an array that receives the values identifying glyphs used for rendering of the string. Its length should be as long as the number of characters specified by the nCount parameter for GetCharacterPlacement. It may be NULL if glyph rendering is not needed. This parameter should be set to NULL if glyph rendering is not needed. Note that the number of glyphs in the array may be less than the number of characters in the original string if the string contains ligated glyphs; the actual count of glyphs will be found in the nGlyphs member, which must be initialized to the maximum length. Also, if reordering is required, the order of the glyphs may not be sequential.

    This array is used if more than one operation is being done n a string which any form of ligation, kerning, or order-switching. This array allows for operations to use the same array and not have to compute it each time.

    This array always contains glyph indices. When using ExtTextOut to produced kerned output, the ETO_GLYPH_INDEX flag must be specified in the ExtTextOut call.

    nGlyphs [as in documentation]

    GDI Coordinates

    GDI coordinates are limited to 32 bits. What is undocumented in Win64 is that they seem to be limited to bitmaps whose physical size is < 2GB, so if you have a 24-bit RGB bitmap, some operations (such as StretchBlt) will fail.

    Gdip*

    The documentation of the GDI+ flat API is an example of documentation arrogance at its worst. There are no detailed discussions of any of the methods or their parameters, and most especially their return values, or the reason that an error might occur. You might think, as the idiots who decided not to document these methods, that "nobody needs to know that because they are using the wrapper classes" but in fact when the wrapper classes fail in some way, and you single-step into them, you find they are calling these low-level functions with parameters you cannot understand, and you cannot discover what they should be, because they are undocumented. So you cannot figure out what is missing. An example is the GdipCreateFromHBITMAP call, which requires a palette, but in fact the primitive for creating the Bitmap object that is used inside CImage::Save does not seem to provide for a means to create a palette from an 8-bit bitmap. Essentially, the quality of this documentation is inexcusable. Exactly what part of "documentation" was missed by the creators of this trash?

    GET_APPCOMMAND_LPARAM

    The list of options is very incomplete. In addition it lists

    APPCOMMAND_MEDIA_SELECT

    an option which does not otherwise exist.

    GetCharABCWidths

    In GM_ADVANCED mode, the ABC widths returned by GetCharABCWidths are different from the widths returned by that API when GM_COMPATIBLE mode is used.

    Note that the ABC widths are dynamically adjusted based on font size (and do not scale linearly with font magnification) and may also depend upon the mapping mode selected. The same character in the same font may not give the same ABC widths in MM_TEXT and MM_ISOTROPIC even though everything else in the DC is the same.

    GetCharacterPlacement

    While it is true that this is obsolete, its documentation is still poor.

    For example, GCP_RESULTS is not hyperlinked except at the top of the page. Every instance that is reasonable should be hyperlinked.

    The documentation for GCP_CLASSIN syas "Specifies that the lpClass array contains preset classifications..". It should say "Specifies that the lpClass member of the GCP_RESULTS structure contains preset classifications...". In addition the reference to "see GCP_RESULTS is not hyperlinked.

    There is no example of how this could be used to take advantage of pair kerning.

    It does not mention that if the GCP_USEKERNING flag is set, the lpGlyphs member of the GCP_RESULTS must be non-NULL and must be at least as long as the nCount parameter, or the API will return with a failure and GetLastError will return error 87, ERROR_INVALID_PARAMETER. This should be explicitly stated with the GCP_USEKERNING flag.

    GetClipboardData

    This states that if the text is not in the correct form for the request, it will be implicitly converted; related documentation states that if the data is, for example, in CF_UNICODETEXT, and the program is an ANSI program that is asking for CF_TEXT, then it will be converted. However, it does not state what code page is used for the conversion. Presumably the active code page as obtained from GetACP( ), but this is not stated and should be.

    GetClassName 

    It appears to be undocumented; correspondent Kero points out that the result of GetClassName or an HWND_MESSAGE window is _T("Message").

    TCHAR name[MAX_PATH];
    if(GetClassName(hwnd, name, MAX_PATH))
       {
         ASSERT(_tcscmp(name, _T("Message")) == 0);
        ...
       }

    ...and RealGetWindowClass

    In a later note, he points out that in the case of common controls, CommCtrl.dll version 6 (themes), and reports that

    GetClassName == RealGetWindowClass

    in that the string returned by GetClassName is the same string returned by RealGetWindowClass.

    This led me to investigate the definitions of these two functions.

    The GetClassName function retrieves the name of the class to which the specified window belongs

    int GetClassName(HWND nWnd, LPTSTR lpClassName, int nMaxCount);

    or

    The RealGetWindowClass function retrieves a string that specifies the window type.

    UINT RealGetWindowClass(HWND hWnd, LPTSTR pszType, UINT cchType);

    Now, other than the change from int to UINT, I don't see any difference between these two calls. What is the distinction between a "window type" and a "class"? In fact, given the name of the API, why does it not say that it "retrieves a name of the window class to which the specified window belongs"? This leaves me wondering what is the difference between these two calls? Note that neither of these APIs cross-references the other.

    GetClipboardFormatName

    This also returns the string for a Registered Window Message.

    (thanks to Giovanni Dicanio for this tip!)

    GetDeviceCaps

    For some properties of some devices, the GetDeviceCaps API returns -1. For example, it can return -1 for NUMBRUSHES, NUMPENS, or NUMCOLORS. There is no documentation that suggests what this can mean; it merely says "Number of device-specific brushes". There is nothing in the Return Value section to suggest that there might be an illegal value returned, or what the meaning of a negative number would be.

    GetDIBits

    This is not indexed in the VS2008 documentation.

    GetFullPathName

    The Parameters description is incomplete:

    lpFilePart

    [out] Pointer to a buffer that receives the address (in lpBuffer) of the file name component in the path.  If this value is not needed, this parameter can be NULL.

    GetImageEncoders

    This fails to have hyperlinks to ImageCodecInfo and GetImageEncoderSize at all instances of these terms. This is caused by people who are stupid enough to believe English majors who have written that a hyperlink should appear only once per page. Such an attitude is childish, and pointless, because it forces me to search the page to find where the one-and-only hyperlink is, instead of letting me use it as an intelligent design would, by clicking the word at the point where it appears! PLEASE DO NOT BELIEVE THE KINDS OF IDIOTS WHO WRITE THESE RULES! THEY DO NOT ACTUALLY EVER USE THE PAGES CREATED ACCORDING TO THESE ASININE PRINCIPLES! THEREFORE, THEY ARE UNQUALIFIED TO CREATE SUCH RULES, AND ANYONE WHO BELIEVES THEM IS ACTING IRRESPONSIBLY!

    In addition, the ImageCodecInfo hyperlink does not appear in the See Also section!

    GetLogicalProcessorInformation 

    The example code that accompanies this is another example of the failure of anyone to actually supervise the amateurs who write these examples. It contains the horrific code

    BOOL rc;
    
    rc = Glpi(buffer, &returnLength);
    if(FALSE == rc)
       {
        ...stuff
       }

    Comparing a Boolean variable to a Boolean literal shows a total lack of understanding about what a Boolean variable actually means. It is stupid and irresponsible to allow code like this to appear as example code.

    The simple solution, since the return value is not used anywhere, is to write

    if(!Glpi(buffer, &returnLegnth))
       {
        ...stuff
       }

    There is no reason to introduce a gratuitous variable for this purpose, and there is no excuse for comparing a Boolean value to a Boolean literal to get, guess what, a Boolean value!

    Similarly, the introduction of the gratuitous done variable. Why introduce a gratuitous variable when the break statement does everything required? Sloppy, sloppy coding, based on some academic's silly rule about the use of break, no doubt.

    But what is truly sad is that the processor information block is of static size and therefore no loop is required to repeat the buffer sizing.

    DWORD len;
    PSYSTEM_LOGICAL_PROCESSOR_INFORMATION buffer;
    
    if(!GetLogicalProcessorInformation(NULL, &len))
       { /* failed (expected) */
        DWORD err = ::GetLastError();
        if(err != ERRROR_INSUFFICIENT_BUFFER)
           { /* actual failure */
            ...deal with failure
           } /* actual failure */
        buffer = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION)malloc(len);
        if(!GetLogicalProcessorInformation(buffer, &len))
           { /* total failure */
            ...deal with failure
           } /* total failure */
        

     

    GetLogonSid 

    This is not actually an API, but is a really-badly-written piece of code from the Web page http://msdn.microsoft.com/en-us/library/aa446670(VS.85).aspx titled "Getting the Logon SID in C++". What we do not get is an example of getting the logon SID in C++; what we have is a badly-written piece of pure C code. Like most of the other MSDN examples that claim to be in C++, this was written by some beginner who had never seen C++, had no idea what the language was, had never learned how to program properly in C, and did not receive any adult supervision to say "this piece of code is complete trash, rewrite it so it looks like it was written by an intelligent human being". As with other examples, I will present the code side-by-side with a decent example.

    As usual, it contains a lot of goto statements, proving beyond any shadow of a doubt that the person knows nothing about how to program correctly. And instead of using new, it uses HeapAlloc. Why anyone in C++ would use HeapAlloc escapes me. Then it declares all the variables, including loop counters, at the top of the block. Hello? What part of "C++" was not comprehended by the programmer?

    Original code Rewritten code
    Header file: LogonSid.h
      Note that API calls should be preceded by "::" to indicate they are in the global namespace. Instead of using primitive C structures like pointers, a useful C++ class is defined.

    Since I normally use MFC, I would have used CString for the ToString method instead of the rather obsolete std::string. If you support Unicode, you need to have the lines in a header file somewhere:

    #ifdef _UNICODE
    typedef tstring std::string;
    #else
    typedef tstring std::wstring;
    #endif

    and replace the std::string declarations shown below with tstring. It is unfortunate that std:: does not have built-in the concept of Unicode-aware strings.

    Apparently there is no header file used for this subroutine example
    class CSID {
        public:
           //****************
           // CSID()
           // !CSID()
           //****************
           CSID() { }
           virtual ~CSID() { }
           //****************************************************************
           //                               Get
           // Result: PSID
           //    A pointer to the SID if one is present
           //    NULL if there is no SID
           //****************************************************************
           PSID Get() { return buffer.size() > 0 ? (PSID)&buffer[0] : NULL; }
    
           //****************************************************************
           //                        CopySid(PSID sid)
           // Inputs:
           //    PSID sid: The SID to be copied into this structure
           // Result: BOOL
           //    TRUE if successful
           //    FALSE if error
           //****************************************************************
           BOOL CopySid(PSID sid) {
              DWORD len = ::GetLengthSid(sid);
              buffer.resize(len);
              if(!::CopySid(len, (PSID)&buffer[0], sid))
                 { /* failed */
                  buffer.resize(0);
                  return FALSE;
                 } /* failed */
              return TRUE;
           }
           //****************************************************************
           //                       ToString
           // Result: std::tstring
           //    The SID expressed as a string
           //    This will be the empty string if there is an error
           //****************************************************************
           std::string ToString() {
              LPTSTR result;
              if(::ConvertSidToStringSid((PSID)&buffer[0], &result))
                 { /* success */
                 std::string t = result;
                  LocalFree(result);
                  return t;
                 } /* success */
              else
                  return std::string(_T(""));
           }
        //****************
        protected:
           std::vectorbuffer;
    };
    
    /****************************************************************************
    *                                 GetLogonSid
    * Inputs:
    *       HANDLE hToken: Token from OpenProcessToken or other source
    *       CSDI & sid: A reference to a CSID structure
    * Result: BOOL
    *       TRUE if successful
    *       FALSE if error (use ::GetLastError to determine reason)
    * Effect: 
    *       Sets the CSID to the sid of the logon account.  This is undefined
    *       if the return value is FALSE
    ****************************************************************************/
    
    BOOL GetLogonSid(HANDLE hToken, CSID & sid);
    
    File LogonSid.cpp
    #include <windows.h>
    #include <stdafx.h>
    #include <LogonSid.h>
      It is not at all clear why all the variables are declared at the top of the function. The title clearly says that this example is written in C++. It is not clear why a temporary structure that is used only inside a single function and which must be deleted before exiting is declared as if the code had been written for the PDP-11 in 1975. In C++, the programmer, had he or she been even marginally literate, would have used at std::vector<BYTE>.
    BOOL GetLogonSID (HANDLE hToken, PSID *ppsid) 
    {
       BOOL bSuccess = FALSE;
       DWORD dwIndex;
       DWORD dwLength = 0;
       PTOKEN_GROUPS ptg = NULL;
    
    BOOL GetLogonSid(HANDLE hToken, CSID & sid) 
       {
        std::vector<BYTE> tg;
    
        // Get required buffer size and allocate the TOKEN_GROUPS buffer.
        DWORD dwLength = 0; 
    // Verify the parameter passed in is not NULL.
        if (NULL == ppsid)
            goto Cleanup;
    Only a C programmer would pass in a PSID *. A C++ programmer would have used a PSID &. I chose to implement a class CSID that provides useful functionality. Since I use a reference, there is no need to check the parameter to see if it is NULL. And the goto Cleanup is simply childish. There is no reason to go to a cleanup routine when there is nothing to clean up!
      In the code below, there is no reason to pass the argument ptg into the call; NULL is sufficient. There is no reason to cast it to LPVOID because that is implicitly handled by the nature of the C++ language. Note that if GetTokenInformation would succeed, the world is really screwed up. There is no need to use the childish goto or to goto Cleanup at all, because, surprise, there is nothing to clean up!

    To allocate the required buffer space, I simply set the size of the std::vector to be the size I want. Now there is never a need to goto Cleanup to get rid of it because the destructor of std::vector will free the space!

    // Get required buffer size and allocate the TOKEN_GROUPS buffer.
    
       if (!GetTokenInformation(
             hToken,         // handle to the access token
             TokenGroups,    // get information about the token's groups 
             (LPVOID) ptg,   // pointer to TOKEN_GROUPS buffer
             0,              // size of buffer
             &dwLength       // receives required buffer size
          )) 
       {
          if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) 
             goto Cleanup;
    
          ptg = (PTOKEN_GROUPS)HeapAlloc(GetProcessHeap(),
             HEAP_ZERO_MEMORY, dwLength);
    
          if (ptg == NULL)
             goto Cleanup;
       }
        if (!::GetTokenInformation(
                                   hToken,         // handle to the access token
                                   TokenGroups,    // get information about the token's groups 
                                   NULL,           // buffer pointer (ignored)
                                   0,              // size of buffer
                                   &dwLength       // receives required buffer size
                                  )) 
           { /* allocate buffer */
            if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) 
               return FALSE;
           
            tg.resize(dwLength);
           } /* allocate buffer */
        else
           { /* nominally impossible */
            return FALSE;
           } /* nominally impossible */
      In the code below, I obtain a pointer to the buffer. If the operation fails, I can simply return FALSE and don't need the childish goto. In pure C, this could be handled with _try/_finally or simply by allocating in a function outside, and calling a second function that produced the result; the calling function will always delete any dynamically-allocated storage when the function it calls returns. You can always spot a programmer too lazy to write a second function because of how they use the goto. But this code is allegedly C++, and it should be written in C++.
    // Get the token group information from the access token.
    
       if (!GetTokenInformation(
             hToken,         // handle to the access token
             TokenGroups,    // get information about the token's groups 
             (LPVOID) ptg,   // pointer to TOKEN_GROUPS buffer
             dwLength,       // size of buffer
             &dwLength       // receives required buffer size
             )) 
       {
          goto Cleanup;
       }
        // Get the token group information from the access token.
        
        if (!::GetTokenInformation(
                                   hToken,            // handle to the access token
                                   TokenGroups,       // get information about the token's groups 
                                   (LPVOID) &tg[0],   // pointer to TOKEN_GROUPS buffer
                                   dwLength,          // size of buffer
                                   &dwLength          // receives required buffer size
                                  )) 
           {
            return FALSE;
           } 
      In the original code, the DWORD dwIndex was declared at the top of the block, as if it were 1975 PDP-11 code. This, however, is C++. A variable should not be declared until it is needed, and in the case of a loop index, if it is not needed outside the loop construct, it should be declared within the for-loop itself.

    Since I'm actually writing in C++, instead of PDP-11 C, I actually use genuine C++ constructs. There seemed to be no reason to use a pointer which might be NULL when I could declare a reference parameter, and there seemed to be no reason to use primitive allocation (such as the silly HeapAlloc calls) when working in C++, so I put all the smarts, including how to do a copy, in the class. I sort of took the idea of writing the code in C++ seriously as opposed to being noise words.

    // Loop through the groups to find the logon SID.
    
       for (dwIndex = 0; dwIndex < ptg->GroupCount; dwIndex++) 
          if ((ptg->Groups[dwIndex].Attributes & SE_GROUP_LOGON_ID)
                 ==  SE_GROUP_LOGON_ID) 
          {
          // Found the logon SID; make a copy of it.
    
             dwLength = GetLengthSid(ptg->Groups[dwIndex].Sid);
             *ppsid = (PSID) HeapAlloc(GetProcessHeap(),
                         HEAP_ZERO_MEMORY, dwLength);
             if (*ppsid == NULL)
                 goto Cleanup;
             if (!CopySid(dwLength, *ppsid, ptg->Groups[dwIndex].Sid)) 
             {
                 HeapFree(GetProcessHeap(), 0, (LPVOID)*ppsid);
                 goto Cleanup;
             }
             break;
          }
    
       bSuccess = TRUE;
    
    Cleanup: 
    
    // Free the buffer for the token groups.
    
       if (ptg != NULL)
          HeapFree(GetProcessHeap(), 0, (LPVOID)ptg);
    
       return bSuccess;
    }
     	
        // Loop through the groups to find the logon SID.
    
        PTOKEN_GROUPS ptg = (PTOKEN_GROUPS)&tg[0];
    
        BOOL result = FALSE;
        for (DWORD dwIndex = 0; dwIndex < ptg->GroupCount; dwIndex++) 
           { /* scan tokens */
            if ((ptg->Groups[dwIndex].Attributes & SE_GROUP_LOGON_ID) ==  SE_GROUP_LOGON_ID) 
              { /* logon SID */
               // Found the logon SID; make a copy of it.
               if(!sid.CopySid(ptg->Groups[dwIndex].Sid))
                  return FALSE;
               result = TRUE;
               break;
              } /* logon SID */
           } /* scan tokens */
        return result;
       } 		
      There are serious questions here about the competence of the programmer who wrote this example. Such as: why is an explicit deallocation required in C++? Why is this apparently written as a separate .cpp file? Why is it using HeapFree instead of delete? The code in the box below illustrates the total amount of code that is required to accomplish this task when it is written competently in C++.
    #include <windows.h>
    
    VOID FreeLogonSID (PSID *ppsid) 
    {
        HeapFree(GetProcessHeap(), 0, (LPVOID)*ppsid);
    }
     
    34 lines of poorly-structured 1975-style C code
    01 lines of missing header file
    ===
    35 lines of incredibly-poorly-written code reminiscent of bad programming in 1975 C
    19 lines of well-structured C++
    18 lines of a generally-useful C++ class
    ====
    37 lines of well-structured C++ consistent with the concept of "Getting the Logon SID in C++"

    GetMessage

    The example which follows this description is nonsensical. The return type is BOOL, and therefore it should be treated as a BOOL value. Saying that a BOOL value can be -1 makes no sense whatsoever. If the return type is int, it should be declared as int and the description should read that the int value can be -1 (error), 0 (quit) or >0 (successful retrieval of a message). It should not involve declaring a BOOL variable and treating it as if it were an int.

    GetModuleFileName 

    The documentation of this API is inadequate. It points out that the filename can be in the "\\?\" format, but it is not obvious to the unsophisticated reader what the implication of this is. It isn't helped by the fact that the API does not actually give a failure, merely silently truncates the file name; failing to check for ERROR_INSUFFICIENT_BUFFER means that the file name may not be correct.

    The documentation stats that the global variable _pgmptr is automatically initialized to the full path, but it omits rather useful information, such as the type of the variable. Is in an LPTSTR? Inquiring Minds Want To Know.

    While in most cases, simply using MAX_PATH (and the number of times I have erroneously seen the value "256" substituted for MAX_PATH is amazing), the fully-general solution is not at all obvious. For example, the classic repeat-until-smaller loop will not occur to most programmers.

    It should be pointed out that

    TCHAR name[MAX_PATH];
    GetModuleFileName(NULL, name, MAX_PATH);

    will work most of the time, the only fully-correct algorithm is the one shown below

    LPTSTR p;
    DWORD len = MAX_PATH;
    while(TRUE)
       { /* get name */
        p = (LPTSTR)malloc(len * sizeof(TCHAR));
        DWORD n = GetModuleFileName(NULL, p, len);
        if(n == 0)
            { /* error */
              ...deal with error
              break; // for example
            } /* error */
        if(n < len)
           break; // success
        len *= 2;
        free(p);
       } /* get name */

    or in MFC, (and note that the existence of MFC must not be ignored!)

    CString s;
    DWORD len = MAX_PATH;
    while(TRUE)
       { /* get name */
        p = s.GetBuffer(len);
        DWORD n = ::GetModuleFileName(NULL, p, len);
        s.ReleaseBuffer();
        if(n == 0)
           { /* error */
             ... deal with error
             break; // for example
           } /* error */
        if(n < len)
           break; // success
        len *= 2;
       } /* get name */

    GetNumaProcessorNode

    The example shown in the article "Allocating Memory NUMA Node [Base]" shows a slovenly approach to coding that under no conditions should ever be encouraged. The example contains the test

    if(TRUE != GetNumaProcessorNode(i, &NodeNumber))

    This is stupid and irresponsible. The specification of GetNumaProcessorNode clearly states,

    "If the function succeeds, the value is nonzero"

    At no point does it state that this value is going to be TRUE. It is the height of slovenliness to ever compare a Boolean value to the literal TRUE or FALSE because the value is itself a Boolean value. It is unfortunate that the people who code these examples have neither adult supervision nor do they bother to actually read the documentation of the functions they are trying to show how to use! It is also unfortunate they were never trained as programmers.

    GetNumaProximityNode

    This states "Retrieves the node number for the specified proximity identifier".

    It does not give the slightest hint as to what a "proximity identifier" actually is!

    There are no symbols defined in the header file.

    GetProcessAffinityMask

    Prior to VS2005, this description contains a gross misstatement of fact: it says "a process affinity mask is a proper subset of a system affinity mask". This statement is simply stupid. It shows that the author had no idea what "proper subset" means. People who do not know how to use terms should not try to be pretentious by using them. A "proper subset" means that the the subset cannot equal the set itself, and it is perfectly clear that a process affinity mask can equal the system affinity mask! Even the revised documentation, which drops the word "proper", can be confusing to those who think that "subset" must mean "proper subset". The correct documentation should say

    A process affinity mask may not specify a 1 bit where the system affinity mask specifies a 0 bit. A process affinity cannot exceed the number of processors in the system.

    GetProcessWorkingSetSize

    The documentation states that the parameters to the API as

    BOOL GetProcessWorkingSetSize(HANDLE hProcess, PSIZE_T lpMinimumWorkingSetSize, PSIZE_T lpMaximumWorkingSetSize);

    however, the example which follows is erroneous; it declares the variables as type DWORD. This will not port to Win64. The example was not corrected when the documentation was updated for Win64.

    GetThreadTimes

    This does not tell the resolution of the values returned. It only says the units are 100ns units of FILETIME; it does not suggest what the resolution of the times actually is. A bit of experimentation suggests that the resolution is the resolution of the system timer, 15ms on a multiprocessor.

    GetTokenInformation

    The example titled "Getting the login SID in C++" (http://msdn.microsoft.com/en-us/library/aa446670(VS.85).aspx) is a particularly good example of especially bad coding style. There is very little "right" about it. It is not only abysmal C++, it is actually very bad C programming as well. There is no excuse for code this poor to be presented as an example!

    A critique of the original code (GetTokenInformation)

    BOOL GetLogonSID (HANDLE hToken, PSID *ppsid)

    *************************************************************************************************
    and what part of "C++" did the author of this code miss? The sensible thing to do here is to designate the second parameter as a PSID & psid
    . Only a C programmer would think a pointer made sense here!

    *************************************************************************************************

    {
       BOOL bSuccess = FALSE;

    *************************************************************************************************

    The only reason this variable exists is because the code is badly written; it does not need to exist

    *************************************************************************************************

       DWORD dwIndex;

    *************************************************************************************************
    The only reason the above variable exists is because the programmer never learned C++. There is no reason for it to exist at all
    *************************************************************************************************

       DWORD dwLength = 0;
       PTOKEN_GROUPS ptg = NULL;

    *************************************************************************************************
    Why is this variable declared here? It does not need to be declared until it is needed, so it doesn't need to be initialized to NULL

    *************************************************************************************************

    
    
    // Verify the parameter passed in is not NULL.
        if (NULL == ppsid)
            goto Cleanup;

    *************************************************************************************************
    Note that if the argument were a PSID & there would be no need for this test! Useless code, a waste of effort! And what in the world is a goto doing here? There isn't even anything to clean up! return FALSE would work just fine!

    *************************************************************************************************

    // Get required buffer size and allocate the TOKEN_GROUPS buffer.
    
       if (!GetTokenInformation(
             hToken,         // handle to the access token
             TokenGroups,    // get information about the token's groups 
             (LPVOID) ptg,   // pointer to TOKEN_GROUPS buffer
             0,              // size of buffer
             &dwLength       // receives required buffer size
          )) 
       {
          if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) 
             goto Cleanup;

    *************************************************************************************************
    Another example of an unnecessary goto. Since there is nothing to clean up, there is no need to goto cleanup; return FALSE would be just fine!
    *************************************************************************************************

          ptg = (PTOKEN_GROUPS)HeapAlloc(GetProcessHeap(),
             HEAP_ZERO_MEMORY, dwLength);

    *************************************************************************************************
    This makes no sense at all! It is already established we are in C++. The correct technique here is to declare

          std::vector<BYTE> ptgbuffer;
          ptgbuffer.resize(dwLength);
          PTOKEN_GROUPS ptg = (PTOKEN_GROUPS)&ptgbuffer[0];

    Now there is no need to ever do something as poor as goto cleanup since the vector will be automatically deallocated! An MFC programmer might have written

        CByteArray ptgbuffer;
        ptgbuffer.SetSize(dwLength);
        PTOKEN_GROUPS ptg = ptgBuffer.GetData();

    For VS2008 with the Feature Pack, this would have been done by a smart shared pointer

        <TODO: example>

    But in any case, this would have been better in C++ if it had been written as

        PTOKEN_GROUPS ptg = (PTOKEN_GROUPS)new BYTE[dwLength];

    instead of using the bizarre and inappropriate HeapAlloc here.
    *************************************************************************************************

          if (ptg == NULL)
             goto Cleanup;

    *************************************************************************************************
    If ptg is NULL, there is nothing to clean up, and no need for a goto cleanup! Therefore, as written this makes no sense, and as rewritten, it is unnecessary. If C++ is being used, MFC will throw a CMemoryException * and the C standard library std::vector<T>::resize will throw a std::bad_alloc exception. Therefore, they will throw an exception and the caller has to deal with the handling of this exception.
    *************************************************************************************************

       }
    
    // Get the token group information from the access token.
    
       if (!GetTokenInformation(
             hToken,         // handle to the access token
             TokenGroups,    // get information about the token's groups 
             (LPVOID) ptg,   // pointer to TOKEN_GROUPS buffer
             dwLength,       // size of buffer
             &dwLength       // receives required buffer size
             )) 
       {
          goto Cleanup;

    *************************************************************************************************
    The LPVOID cast is probably unnecessary except to suppress warnings by overzealous type checking tools.

    As originally written, this goto is required, but this leads to the question about program structure. If you have to clean things up, why not call a subroutine passing the pointer in? Then upon return, clean the value up?

           ptg = new BYTE[dwLength];
           DoSomething(various_parameters, ptg);
           delete [] ptg;

    will do the job, and the called function is free to simply return whenever it feels like it. But self-freeing structures including smart pointers are the preferred method in C++. As far as I can tell, in spite of the title, there is nothing here that is C++-related.
    *************************************************************************************************

    
       }
    
    // Loop through the groups to find the logon SID.
    
       for (dwIndex = 0; dwIndex < ptg->GroupCount; dwIndex++) 

    *************************************************************************************************
    Apparently, the programmer who wrote this code had never, ever looked at the C++ language. The obvious way to write this is

        for(DWORD dwIndex = 0; dwIndex < ptg->GroupCount; dwIndex++)

    but apparently the use of a declaration in the loop was unknown to the original programmer, who only knew K&R C.
    *************************************************************************************************

          if ((ptg->Groups[dwIndex].Attributes & SE_GROUP_LOGON_ID)
                 ==  SE_GROUP_LOGON_ID) 
          {
          // Found the logon SID; make a copy of it.
    
             dwLength = GetLengthSid(ptg->Groups[dwIndex].Sid);
             *ppsid = (PSID) HeapAlloc(GetProcessHeap(),
                         HEAP_ZERO_MEMORY, dwLength);

    *************************************************************************************************

    But if the parameter is a PSID &, the correct code here would be

          psid = (PSID)new BYTE[dwLength];

    and there is no need to force the user to call HeapFree or even know that HeapFree exists! Actually, starting with VS2008 with the Feature Pack, it would make a lot more sense to pass a reference to a smart pointer so the object will self-destruct when it leaves scope of the parent or other owning scope.

    *************************************************************************************************

             if (*ppsid == NULL)
                 goto Cleanup;

    *************************************************************************************************
    The above test is unnecessary, because the new operator will throw either a std::bad_alloc exception (pure C++) or a CMemoryException * (MFC), and it is the responsibility of the caller to handle these.

    (Thanks to Giovanni Dicanio for pointing out the change in new from VS6)

    *************************************************************************************************

             if (!CopySid(dwLength, *ppsid, ptg->Groups[dwIndex].Sid)) 
             {
                 HeapFree(GetProcessHeap(), 0, (LPVOID)*ppsid);
                 goto Cleanup;

    *************************************************************************************************
    And why is *ppsid not set to NULL? We now have a pointer to invalid memory left there, which is bad practice!
    *************************************************************************************************

             }
             break;

    *************************************************************************************************
    If this were written in C++, and not in 1975 K&R C, it would have been written as

             if(!CopySid(dwLength, psid, 0, ptg->Groups[dwIndex=.Sid)
                {
                 delete [] psid;
                 return FALSE;
                }
             else
                return TRUE;

    *************************************************************************************************

          }
    
       bSuccess = TRUE;

    *************************************************************************************************
    There is no need for the above assignment because there is no need for the bSuccess variable to exist.
    In a properly-written C++ program (which the title of this article claims to demonstrate) this piece of crap below would not exist. There would not be a single goto anywhere in the code.

    In general, if you have to write a goto, you can be reasonably certain your code is poorly designed.

    *************************************************************************************************

    Cleanup: 
    
    // Free the buffer for the token groups.
    
       if (ptg != NULL)
          HeapFree(GetProcessHeap(), 0, (LPVOID)ptg);
    
       return bSuccess;
    }

    Rewritten in C++/MFC (GetTokenInformation)

    BOOL GetLogonSID (HANDLE hToken, PSID & psid) throw()
       {
        DWORD dwLength = 0;
        CByteArray b;
    
        // Get required buffer size and allocate the TOKEN_GROUPS buffer.
    
        if (!GetTokenInformation(
                 hToken,         // handle to the access token
                 TokenGroups,    // get information about the token's groups 
                 NULL,           // pointer to TOKEN_GROUPS buffer
                 0,              // size of buffer
                 &dwLength       // receives required buffer size
                 )) 
           {
            if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) 
               return FALSE;
    
            b.SetSize(dwLength);
           }
    
        PTOKEN_GROUPS ptg = b.GetData();
        // Get the token group information from the access token.
    
        if (!GetTokenInformation(
                 hToken,         // handle to the access token
                 TokenGroups,    // get information about the token's groups 
                 (LPVOID) ptg,   // pointer to TOKEN_GROUPS buffer
                 dwLength,       // size of buffer
                 &dwLength       // receives required buffer size
                 )) 
            {
             return FALSE;
            }
    
        // Loop through the groups to find the logon SID.
    
        for (DWORD dwIndex = 0; dwIndex < ptg->GroupCount; dwIndex++) 
           if ((ptg->Groups[dwIndex].Attributes & SE_GROUP_LOGON_ID)
                 ==  SE_GROUP_LOGON_ID) 
             {
              // Found the logon SID; make a copy of it.
    
              dwLength = GetLengthSid(ptg->Groups[dwIndex].Sid);
              psid = (PSID) new BYTE[dwLength];
              if (!CopySid(dwLength, *ppsid, ptg->Groups[dwIndex].Sid)) 
                 {
                  delete [] psid;
                  psid = NULL;
                  return FALSE;
                 }
              break;
             }
    
         return TRUE;
        }

    But what if I needed it in C? (GetTokenInformation)

    The argument about C++ using techniques that cannot be used in C cannot even be made well ("Well, you need the goto if you are programming in C") because in properly-written C the code would have been

    BOOL GetLogonSID (HANDLE hToken, PSID *ppsid)
       {
        BOOL bSuccess = FALSE;
        DWORD dwIndex;
        DWORD dwLength = 0;
        PTOKEN_GROUPS ptg = NULL;
    
        // Verify the parameter passed in is not NULL.
        if (NULL == ppsid)
            return FALSE;
        // Get required buffer size and allocate the TOKEN_GROUPS buffer.
    
        if (!GetTokenInformation(
                 hToken,         // handle to the access token
                 TokenGroups,    // get information about the token's groups 
                 NULL,           // pointer to TOKEN_GROUPS buffer
                 0,              // size of buffer
                 &dwLength       // receives required buffer size
                 )) 
          {
           if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) 
              return FALSE;
           ptg = (PTOKEN_GROUPS)calloc(dwLength);
           if (ptg == NULL)
             return FALSE;
          }
    
       // Get the token group information from the access token.
       __try   // <==== NOTE USE OF __try!
          { /* try */
           if (!GetTokenInformation(
                     hToken,         // handle to the access token
                     TokenGroups,    // get information about the token's groups 
                     (LPVOID) ptg,   // pointer to TOKEN_GROUPS buffer
                     dwLength,       // size of buffer
                     &dwLength       // receives required buffer size
                     )) 
             {
              return FALSE;
             }
    
           // Loop through the groups to find the logon SID.
    
           for (dwIndex = 0; dwIndex < ptg->GroupCount; dwIndex++) 
              if ((ptg->Groups[dwIndex].Attributes & SE_GROUP_LOGON_ID)
                     ==  SE_GROUP_LOGON_ID) 
                 {
                  // Found the logon SID; make a copy of it.
    
                  dwLength = GetLengthSid(ptg->Groups[dwIndex].Sid);
                  *ppsid = (PSID) malloc(dwLength);
                  if (*ppsid == NULL)
                      return FALSE;
                  if (!CopySid(dwLength, *ppsid, ptg->Groups[dwIndex].Sid)) 
                     {
                      free(*ppsid);
                      *ppsid = NULL;
                      return FALSE;
                     }
                  break;
                }
           return TRUE;
          } /* try */
       __finally
          {
           free(ptg);
          }
    }

    Note how much simpler the code is if it is not written in 1975 K&R C, but in fact uses the actual language implemented on Windows, that is, Microsoft C. The __try/__finally mechanism was created precisely so ugly gotos would not exist and there is no possibility of bypassing the freeing of local resources.

    I would encourage Microsoft to actually exercise some adult supervision over these code examples. They should, every single one of them, be exemplars of best practice.

    Yet another (highly refactored) rewrite (GetTokenInformation)

    Alf P. Steinbach took the time to do a much more massive rewrite. His discussion and the resulting code appear below, reproduced with his permission. I've inserted a few responses to his comments. Overall, a much more thorough job than I would have considered for this example. Perhaps Microsoft can learn something from code like this.

    Other than using some boldface and italic fonts and a fixing a couple minor mis-spellings, the code and comments are as he posted them.


    Ah well. I think your attempt to expose Bad Programming in MSDN is laudable, good, because many novices and even professionals take these examples as being examples of good ways to do things. Which they decidedly are not, in general.

    But let's critique your code also. ;-)

    The first thing that hit me was the 'BOOL' result type. Why not use standard C++ 'bool'? For that matter, I think it would be more practical with an exception on error, and simply 'void' result type, but that's very much personal preference.

    jmn: because I'm an MFC programmer, I use BOOL, because mixing BOOL and bool is confusing and potentially hazardous.

    Next, that it is a single routine (OK, function). There's a lot of functionality in here. Much of it will have to be duplicated elsewhere when it's stuffed into a routine like this. Separating out this functionality will however produce more lines of code (example below). But I think it's worth it, because reuse is not only desirable on its own, it's also very desirable for testing and to be a bit more sure about correctness.

    jmn: I didn't bother to refactor because I didn't feel that motivated. In practice, I tend to refactor a lot more than this code shows, and end up with something much closer to your rewrite.

    Next, the line after the function head, indentation of the {. I used to drive my co-workers mad by indenting like that, once upon a time. Got that from early student work implementing compiler and indentation "should" match parsing level, I thought, and besides, Petzold did it that way. But it is about communicating to other programmers, not just to oneself. So I gave up that style. ;-)

    jmn: Most C indentation styles suck, and I've been indenting like this for 40 years and see no advantage to changing my style to one I don't like. This is also the style my text editor automatically does for me.

    Next, we have that "if( !GetTokenInformation ..." for checking buffer length. I'm reasonably sure that you didn't invent this idiocy, but simply didn't recognize it as such in the original MSDN Library code. It is pure idiocy. For if GetTokenInformation returns logical false, then all is OK, and the if is unnecessary. But if against expectation GetTokenInformation returns logical true, then, hey, this code isn't executed, and you're into UB (or at least very unexpected and not-catered-for) land! Ouch! :-)

    jmn: I found this whole construct suspect. But some APIs, if you supply a 0-length NULL-pointered buffer, return a success code; others return a failure code with the error shown. This is rarely documented. I had to assume that the programmer who wrote this had determined that this API failed when it should have successfully returned the length, even though it successfully returns the length if it fails! But the code looks really weird!

    What's needed, if any result checking is to be done, is not an 'if', but an 'assert': asserting that with these arguments, GetTokenInformation returns logical false, and if it doesn't then something is Very Very Wrong. Btw., regarding the arguments, the ptg argument is unnecessary here (simply use 0), and in addition that cast to 'LPVOID' is unnecessary, and in addition if there should be a cast it should be a static_cast and to 'void *'. Again, this stems from the original MSDN Library code. I'm very very sure it isn't intentional!

    jmn: ASSERTs are very good for debugging, although what would be used here would be a VERIFY in MFC or _ATLVERIFY. In release mode, it should just silently return FALSE. As written, I'd missed the use of ptg and consequently my rewrite could not compile, because it doesn't declare ptg until it is needed. I left the (LPVOID) casts because sometimes tools such as lint or prefast, or possibly even the compiler option /W4, will complain if there is an implicit widening to LPVOID without a cast, but my original suspicion is that these were there because the programmer was fundamentally clueless about either C or C++ rules for implicit widening of pointer types to LPVOID.

    Next, further down there is a check "if (psid == NULL)". Well, if this code is supporting VC6, then OK, it might happen. But I'd put a comment on that. In modern C++ 'new' throws a std::bad_alloc exception on failure. By the way, for the cleanup below that check (after CopySid), I suggest looking at Marginean & Alexandrescu's ScopeGuard. It needs some adjustment for Visual C++ (because MSVC is defective re __LINE__ macro, so instead use __COUNTER__), but very useful.

    jmn: The behavior of new is confusing and inconsistent. In pure C++, it will in fact throw an exception, std::bad_alloc. But if MFC is in use, the new operator is redefined, and in fact it still returns NULL (I single-stepped through some code in both kinds of apps to verify this). In fact, Giovanni Dicanio had already pointed it out and I'd made a correction.

    OK, over to what I suggested regarding refactoring.

    In this code I use some very primitive exception throwing. It's ungood because it doesn't pick up e.g. GetLastError, but it's simple, so, I use it for examples like this, & simple test code. Goes like (Visual C++ specific code):

    #pragma warning( disable: 4646 )    // noreturn func has non-void result type
    __declspec( noreturn )
    bool throwX( LPCSTR s ) { throw std::runtime_error( s ); }

    As an example usage,

    std::string stringFromSid( PSID sid )
    {
         LPTSTR  p;
         ConvertSidToStringSid( sid, &p )
             || throwX( "stringFromSid: ConvertSidToStringSid failed" );
         std::string const result = p;
         LocalFree( p );
         return result;
    }

    Next, primitive API-wrapper level (I had to reformat for posting, hope that hasn't introduced any errors),

    typedef HANDLE                      Token;
    typedef TOKEN_INFORMATION_CLASS     TokenInfoClassEnum;
    
    DWORD tokenInfoLength( Token token, TokenInfoClassEnum infoClass )
    {
         DWORD       length;
         BOOL const  ok      = GetTokenInformation( token, infoClass, 0, 0, &length );
    
         assert( !ok );
         (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
             || throwX( "tokenInfoLength: GetTokenInformation failed" );
         return length;
    }

    jmn: My only critique here is that I would throw not a string but a STRINGTABLE id. Or I'd use a function that took a STRINGTABLE id and returned a string, or I'd use my ErrorString function which converts a ::GetLastError code to a string. Putting literal native-language strings in a program is a Fundamentally Bad Idea®. Also, the use of LPCSTR (8-bit) strings shows a fundamental defect in the design of the std:: library, which really ought to be uniformly Unicode-aware.

    void getTokenInfo(
         Token              token,
         TokenInfoClassEnum infoClass,
         std::vector& result
         )
    {
         std::vector   resultData( tokenInfoLength( token, infoClass ) );
         DWORD               dummy;
         BOOL const          ok      = GetTokenInformation(
             token, infoClass, &resultData[0],
             static_cast( resultData.size() ), &dummy
             );
    
         (ok) || throwX( "tokenInfo: GetTokenInformation failed" );
         resultData.swap( result );
    }

    Here's one important aspect, that 'getTokenInfo' cannot be reimplemented as or wrapped by an 'tokenInfo' function returning std::vector.

    The reason it cannot, is that GetTokenInformation places SID data in the buffer, and it places pointers to those SIDs in the buffer. Copying and destroying original buffer would trash those embedded pointers.

    At a slightly higher level of abstraction, factoring out the "get group info" functionality, that non-copyability can be enforced in C++:

    class AttributedSids
    {
    private:
         std::vector       myData;
         TOKEN_GROUPS const*     myDataPtr;
    
         AttributedSids( AttributedSids const& );
         AttributedSids& operator=( AttributedSids const& );
    
    public:
         typedef SID_AND_ATTRIBUTES AttributedSid;
    
         AttributedSids( Token token )
         {
             getTokenInfo( token, TokenGroups, myData );
             myDataPtr = reinterpret_cast( &myData[0] );
         }
    
         size_t size() const { return myDataPtr->GroupCount; }
    
         AttributedSid const& operator[]( size_t i ) const
         {
             return myDataPtr->Groups[i];
         }
    
         AttributedSids const& ref() const { return *this; }
    };

    But then, it might be useful to really copy SIDs, so, a class for that:

    class Sid
    {
    private:
         std::vector   myData;
    
    protected:
         Sid( PSID aSid )
         {
             myData.resize( ::GetLengthSid( aSid ) );
             ::CopySid( static_cast( myData.size() ), &myData[0], aSid )
                 || throwX( "Sid::: CopySid failed" );
         }
    
    public:
         operator PSID () const { return const_cast( &myData[0] ); }
         std::string toString() const { return stringFromSid( *this ); }
    };

    Probably that constructor should be public for a reusable class, but I chose the least access that would work for this example, namely, for derived class that provides logon SID:

    class LogonSid: public Sid
    {
    public:
         static PSID ptrIn( AttributedSids const& sids )
         {
             for( size_t i = 0;  i < sids.size();  ++i )
             {
                 if( (sids[i].Attributes & SE_GROUP_LOGON_ID) == SE_GROUP_LOGON_ID )
                 {
                     return sids[i].Sid;
                 }
             }
             throwX( "LogonSid::ptrIn: logon SID not found" );
         }
    
         LogonSid( HANDLE token )
             : Sid( LogonSid::ptrIn( AttributedSids( token ).ref() ) )
         {}
    };

    Now if I were to critique this code myself I would probably focus on that constructor initializer line, because it uses some non-obvious C++ features and rules. But I'm not sure how to do that better. Essentially, the problem is keeping a copy of the SID data until base class has been initialized, and the non-obvious rule is that the current C++ standard doesn't support binding a temporary of non-copyable class to a ref-to-const formal arg. An alternative could be to introduce a dependency on AttributedSids in the Sid base class. But I think that's even uglier, but possibly someone here has Bright Idea?


    GetVersionEx

    The prototypes of this function are erroneously declared as LPOSVERSIONINFO, when it should be declared as LPOSVERSIONINFOEX. This is clearly an error, and the documentation fails to explain it away.

    GetWindowThreadProcessId 

    The documentation says the return value is the identifier of the thread that created the window.

    However, there have been reports that this call will return 0. There is nothing specified as to whether this call can return 0, and there is no suggestion about calling GetLastError if it fails.

    Correct documentation would state

    Return Value

    If the hWnd parameter represents a valid window handle, returns the thread ID of the thread that created the window. Note that if the hWnd is not a valid window handle, this function can return 0 for the thread ID, and in such a case, if the lpdwProcessId parameter was non-NULL, the value of the DWORD thus referenced is undefined.

    Remarks

    Note that while the hWnd might have been valid at the point it was obtained, perhaps even in the line preceding the call to GetWindowThreadProcessId, the nature of a preemptive, multithreaded, multiprocessor operating system is that between the time the window handle was obtained and the call was executed, the window could have been destroyed..

    Ideal documentation might have said, supposing the support really was in the kernel for this

    [same as above]

    GetLastError will indicate the reason for the failure. Possible values are ERROR_INVALID_PARAMETER meaning that hWnd is not a valid window handle.

    GradientFill

    The documentation erroneously states that the pVertex parameter is a "pointer to an array of TRIVERTEX structures that each define a triangle vertex". This is not true. It is a pointer to an array of TRIVERTEX structures that define the vertices of a rectangle or triangle.

    From reader Jim Beveridge:

    IMHO, the documentation for GradientFill is almost useless. I found this page that provides many more details:

    http://www.flounder.com/gradientfiller.htm

    HDN_BEGINTRACK

    Contains no cross-reference to HDN_ENDTRACK or HDN_TRACK

    HDN_ENDTRACK

    Contains no cross-reference to HDN_BEGINTRACK or HDN_TRACK

    HDN_TRACK

    Contains no cross-references to HDN_BEGINTRACK or HDN_ENDTRACK

    HeapCompact

    The documentation of this API is vague and misleading.

    First, it does not "compact" a heap at all. The term as used is completely nonsensical. The API itself is a leftover from the old Win16 method of the same name, which really did compact the heap. But the description

    The HeapCompact function attempts to compact a specified heap. It compacts the heap by coalescing adjacent free blocks of memory and decommitting large free blocks of memory.

    is absurd. The incorrect part can be rephrased properly by stating

    The HeapCompact function coalesces adjacent free blocks of memory on a heap.

    and not using the word "compact" at all in any part of the documentation. Compaction is a well-understood concept that involves moving blocks of memory so there are no unallocated blocks of memory between allocated blocks of memory, and this is, of course, impossible, because there is no way all the pointers can be adjusted (Managed C++ will allow heap compaction, because it owns and manages all the pointers and can update them when a block is moved). But HeapCompact is a base API and would not be used by the CLR for this purpose.) The vague part is the phrase about "decommitting". There is a substantial difference between the idea of decommitting memory and freeing memory. Memory which is decommitted still exists in the process's address space, but has no block allocated on the paging file. So it does not reduce the actual address space or logical footprint of the process, it merely frees up some paging space. Freeing a page removes it from the process address space entirely. The vague term "blocks" is not at all the same as "page", so it is not clear what is meant. The documentation needs to be rewritten to replace the word "decommit" with the word "free" if this is the appropriate description, or, alternatively, state something about what decommitting blocks really means. As such, the description of this API is unusable insofar as determining what it actually does.

    _heapmin

    The documentation of this function is confusing. It claims to "minimize the heap by releasing unused heap memory to the operating system" but in fact it has other side effects. For example, in single-stepping through it, it is clear that what it does in addition is coalesce adjacent free blocks. In addition, it is not clear that it actually returns blocks to the operating system, because it calls HeapCompact, which is explicitly stated as decommitting blocks of memory, which is not at all the same as freeing blocks of memory.

    It states that "In Visual C++ 4.0, the underlying heap structure was moved to the C run-time libraries to support the new debugging features. As a result, the only Win32 platform that is supported by _heapmin is Windows NT"

    What could this mean? What is the correlation between a version of Visual C++ and a version of the operating system? For that matter, it explicitly states "Windows NT", which would, of course, mean "Windows NT 4.0", and it says nothing about Windows 2000, Windows XP, Windows Server 2003, Windows Vista, or Windows Server 2008. If Windows 95, 98 and Me were excluded, the correct statement would be

    "This function is not supported on Windows 95, Windows 98 or Windows Me"

    but that is not what it says.

    Furthermore, it does not state,

    The allocator operates by allocating blocks of storage from the operating system, then sub-allocating those when malloc or calloc are called. Consequently, a 'block' can be freed to the operating system only if there are no small blocks allocated within that major block. This means that under ordinary operating conditions, it is actually unlikely that a major block can be freed to the operating system, and it is therefore unlikely that this function will accomplish anything meaningful, except in the statistically improbable situation where all the sub-blocks have been freed. It is nearly impossible to guarantee that this will actually ever occur.

    HMONITOR 

    Searching for this word gives a completely useless reference to a page which just lists Windows data types. The description itself gives the nonsensical statement

    if(WINVER >= 0x0500) typedef HANDLE HMONITOR;

    It is not clear who wrote this, but obviously not anyone who knows C. The correct definition would simply state

    This symbol is not defined for versions of Windows prior to Windows 2000.

    If one wished to show code, the correct definition would be

    #if WINVER >= 0x0500
    DECLARE_HANDLE(HMONITOR);
    #endif

    Note that in a sane world, this hyperlink would have been to the top of the multi-monitor API section.

    HWND_DESKTOP 

    See the discussion of the use of this symbol as the parent handle for EnumChildWindows in the discussion of HWND_MESSAGE.

    HWND_MESSAGE 

    Correspondent Kero sends the following information. I have reformatted some of it for presentation, so if there are any errors introduced here, they may be my fault.

    I am not sure where he finds the Microsoft documentation he cites below, because I can find nothing in the existing documentation about either HWND_MESSAGE or a built-in window class named "MESSAGE". However, given the overall quality of the documentation, this is hardly surprising. --jmn


    According to the Microsoft documentation:

    Kero points out the following inconsistencies with the documentation

    HWND wnd = ::GetAncestor(FindWindowEx(HWND_MESSAGE, NULL, NULL, NULL), GA_PARENT);
    ::EnumChildWindows(hwnd, ...)

    enumerates all message-only windows.

    and adds that

    HWND DesktopWindow == ::GetAncestor(FindWindowEx(HWND_DESKTOP, NULL, NULL, NULL), GA_PARENT);
    ::EnumCnildWindows(DesktopWindow, ...)

    will enumerate all usual windows.

    A further note is that

    GetClassName provides the following result:

    TCHAR name[MAX_PATH];
    if(::GetClassName(hwnd, name, MAX_PATH))
       {
         ASSERT(_tcscmp(name, _T("Message")) == 0);
        ...
       }

    The documentation states

    To create a message-only window, specify the HWND_MESSAGE constant or a handle to an existing message-only window in the hWndParent parameter of the CreateWindowEx function.

    Kero observes that

    But its right only for the top message-only window (class "Message"). Any other message-only owner in the hwndParent parameter of the CreateWindowEx preordains the usual, non-message-only, window.

    With respect to SetParent, the observation reported is

    The hWndNewParent parameter can be any message-only window, not only the HWND_MESSAGE constant or the topmost "Message" window.

    To demonstrate the enumeration features, a link was provided:

    http://www.geocities.ws/xmemor/2lz/wintreesnap.zip and http://www.geocities.ws/xmemor/2lz/tinyasm.html


    ICClose

    Does not contain a reference to ICOpen in its See Also

    ICCompress

    The documentation states

    "The compressor sets the contents of lpdwFlags tp AVIIF_KEYFRAME when it creates a key frame. If your application creates AVI files, it should save the information returned for lpckid and lpdwFlags in the file.

    I do not know how to relate this statement to the specification

    lpckid

    Reserved; do not use

    The documentation states that lpbiOutput, lpbiInput, and lpbiPrev are pointers to BITMAPINFO structures, but the specification of the prototype clearly states that they are pointers to BITMAPINFOHEADER structures! It won't compile if the parameters are given as BITMAPINFO pointers.

    ICCompressBegin

    This does not explicitly state if the lpbiOutput parameter can be NULL. In many other compression APIs, the output parameter can be NULL. It is not clear what is going on here, or why I would want the output format, or what I would do with it if I had it.

    The Remarks section blathers about what the driver should do, as if, as an application programmer, I care in the slightest. What does this blather have to do with anything I would care about? If necessary, the Remarks section should be divided into two sections. one for programmers, and one for developers of drivers; blending the two in some incoherent mish-mash is confusing.

    The statement that "VCM saves the settings" is confusing because I have to magically guess that "VCM" stands for Video Compression Manager. It wouldn't hurt to spell out the acronym. And why do I care about how the Video Compression Manager is implemented internally?

    It does not have a hyperlink to ICCompress.

    ICImageCompress

    This document erroneously states

    To obtain the format information from the LPBITMAPINFOHEADER structure, use the GlobalLock function to lock the data. Use the GlobalFree function to free the DIB when you are finished.

    I have no idea what this means. The parameters are clearly stated to be LPBITMAPINFO pointers, and you cannot apply GlobalLock to a pointer. You apply GlobalLock to an HGLOBAL to get a pointer. The entire piece of information is nonsensical.

    The result type is specified as a HANDLE, but the documentation should explicitly state

    Returns an HBITMAP to a compressed DIB.

    or possibly

    Returns an HGLOBAL to a compressed DIB.

    The random ramblings about GlobalLock and GlobalFree would make sense if the return type was HGLOBAL, but that raises the question about (a) why it isn't specified as an HGLOBAL and (b) why the discussion about GlobalLock and GlobalFree (and there is no mention of GlobalUnlock) which would have said

    The function returns an HGLOBAL to the bits of the compressed DIB. This should be cast to an HGLOBAL. To obtain the bits, GlobalLock would be called. When finished, you would call GlobalUnlock and then to free the bits, call GlobalFree.

    This means that GlobalLock are not used to get information from the LPBITMAPINFOHEADER, but from the HGLOBAL which is erroneously specified as a HANDLE. If that's what is really happening.

    I have no idea what it means by the sentence "The image data follows the format header". The last I looked, bits did not lead each other around. Does it mean to say "The format of the DIB is based on the format specified by the lpbiOut parameter", or something else informative. Since there are two BITMAPINFO headers, the phrase 'the' format header makes no sense.

    The hyperlinks to the BITMAPINFO structure on these pages give the Microsoft Web site, not the local help pages. Doesn't work too well at 39,000 feet over the Atlantic ocean.

    The plSize description is unclear. What I think it means to say is

    plSize

    [in/out] Points to a LONG value. On input, this should be set to the maximum size desired for the compressed image; if there is no constraint, it should be set to the lpbiIn->bmiHeader.biSizeImage value (for BI_RGB input, this value can be zero, in which case the size could be set to <formula here>. Upon completion, the location pointed to by this parameter is set to the size of the compressed image, as a byte count.

    Note: I am not certain of the precise formula to be used; for example, is the biPlanes value involved? Would ICCompressQuery be involved in this? Inquiring Minds Want To Know. (The lack of any examples does not make this any easier to deal with...)

    ICImageDecompress

    This document erroneously states

    To obtain the format information from the LPBITMAPINFOHEADER structure, use the GlobalLock function to lock the data. Use the GlobalFree function to free the DIB when you are finished.

    I have no idea what this means. The parameters are clearly stated to be LPBITMAPINFO pointers, and you cannot apply GlobalLock to a pointer. You apply GlobalLock to an HGLOBAL to get a pointer. The entire piece of information is nonsensical.

    The hyperlinks to the BITMAPINFO structure on these pages give the Microsoft Web site, not the local help pages. Doesn't work too well at 39,000 feet over the Atlantic ocean.

    ICOpen

    This does not contain a reference to ICClose in its See Also.

    ICQUALITY_DEFAULT

    From vfw.h:

    /* quality flags */
    #define ICQUALITY_LOW 0
    #define ICQUALITY_HIGH 10000
    #define ICQUALITY_DEFAULT -1

    This symbol, which is defined in vfw.h, apparently is intended to be assigned to the AVISTREAMINFO.dwQuality field, which is a DWORD. At /W4, the symbol ICQUALITY_DEFAULT generates a warning. It should be declared as

    #define ICQUALITY_DEFAULT ((DWORD)-1)

    IEnumVARIANT 

    Contains no hyperlink to VARIANT or HRESULT. Contains stupid uses of the obsolete pointless noise word FAR, which has no reason to exist in modern programming, and whose existence should be deleted from every existing header file. It should certainly not be documented as if it had meaning!

    It even gives the header file for 16-bit systems! Hello, what part of "Win16 is totally dead" was missed here?

    It states that "To see how to implement a collection of objects using IEnumVariant, refer to the file Enumvar.cpp in the Lines sample code. We have no hyperlink to this code, and no known way to find it, and could someone please explain what is wrong with PUTTING THAT EXAMPLE IN THIS PAGE????

    /IGNORE:nnnn (linker)

    The linker's /IGNORE switch to suppress warning messages is not documented. (See LNK4089)

    ImageCodecInfo

    Apparently in VS9, some deluded designer has decided that the "data members" should be some scrollable control INSIDE a scrollable page. Who would be stupid enough to think that this design could possibly make sense? It is a pain to use. Can anyone explain why the structure is no longer displayed? But putting a scrollable image inside a scrollable image means someone is not paying attention to users!

    InitializeCriticalSectionAndSpinCount

    There is no guidance given as to what the spin count might be set to. For example, it does not mention what the spin count is set to if an ordinary InitializeCriticalSection is done, therefore, there is no way to tell how to change it, for example, to increase the spin count. Is 1000 too large a number, too small, or a good balance? Inquiring Minds Want To Know!

    The documentation for VS2008 suggests that the storage allocator uses "roughly 4000" as a value, but gives no rationale of why this is a good value, and no notion of what the default is if you just InitializeCriticalSection. It does not suggest how this value should vary with different platforms, or how it correlates with CPU processor speed.

    InitializeSRWLock

    Does not state if this lock can be placed in memory shared between multiple processes, such as in a shared-segment DLL, a shared-segment executable, or a memory-mapped file.

    From inspection of the code: It can be placed in shared memory shared between processes.

    InitOnceExecuteOnce

    The example is completely incomprehensible. For example, it uses the creation of an event, which is a particularly poor choice for an example. This should be eliminated. Instead, something much simpler and more obvious, such as initializing a table, should be used. The first thread to execute initializes the table, all other threads detect the initialization has taken place and do not initialize the table. It is not at all clear why the context needs to be passed in; the code is erroneous in that it says

        *lpContext = hEvent;

    without the slightest hint of knowledge that the context pointer in fact points to something large enough to hold a handle value. It does not state if the INIT_ONCE object can be in the shared data segment of a DLL and thus used by threads in different processes to detect if the value has been initialized. Overall, the documentation and example are very poorly written.

    @Interface (MASM)

    The documentation of this, in its entirety, is

    Information about the language parameters (numeric equate)

    I'm sure there are actual numbers that have actual meanings. I wonder why we are not told what these are. Is it embarrassing? Or is it just designed, like the documentation of @Cpu, as a direct insult to intelligent readers everywhere?

    IoCreateDevice

    This is actually a DDI call (an operation in the kernel which is part of the Device Driver Interface), but it has a long-standing bug. It says

    DeviceName

    Optionally points to a buffer containing a zero-terminated Unicode string that names the device object. The string must be a full path name.

    This is nonsensical, because the parameter is a PUNICODE_STRING which is a counted string which does not need to be NUL-terminated.

    The correct documentation is

    DeviceName

    Optionally points to a UNICODE_STRING structure that refers to a string which names the device object. The string must be a full path name.

    This is apparently a bug in the documentation which has been known for years, but which no one is willing to fix.

    IsCharSpace

    The documentation erroneously states that this function is defined in shlwapi.h. It is not. It is not defined in the VS 2003 library.

    ITypeInfo::ReleaseFuncDesc

    The documentation states that this returns an HRESULT but the function prototype clearly states that the return type is void:

    From OAIdl.idl, line 1417ff

    void ReleaseFuncDesc
        FUNCDESC * pFuncDesc
    );

    A wave of the Flounder Fin to Dmitry Kovalenko, who submitted this via email.

    ITypeInfo::ReleaseTypeAttr

    The documentation states that this returns an HRESULT but the function prototype clearly states that the return type is void:

    From OAIdl.idl, line 1407ff

    void ReleaseFuncDesc
        TYPEATTR * pFuncDesc
    );

    A wave of the Flounder Fin to Dmitry Kovalenko, who submitted this via email.

    ITypeInfo::ReleaseVarDesc

    The documentation states that this returns an HRESULT but the function prototype clearly states that the return type is void:

    From OAIdl.idl, line 1427ff

    void ReleaseFuncDesc
        VARDESC * pFuncDesc
    );

    A wave of the Flounder Fin to Dmitry Kovalenko, who submitted this via email.

    KB104111: How to propagate environment variables to the system 

    This contains a serious error. In particular, it shows the method as being

    SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0,
    			(LPARAM) "Environment", SMTO_ABORTIFHUNG,
    			5000, &dwReturnValue);

    which cannot possibly work correctly in a Unicode app. It should be

    SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0,
    			(LPARAM) _T("Environment"), SMTO_ABORTIFHUNG,
    			5000, &dwReturnValue);

    If the first form is written in a Unicode app, it will malfunction.

    KB166129: How to programmatically print to a non-default printer in MFC

    This was adapted from an article posted in the microsoft.public.vc.mfc newsgroup on 19-Sep-08, by Charlie Brown.

    The original code in the article needs to be modified as shown.

    The remark made by the poster is "Don't know if it'll help you, but it I did have memory corruption errors until I made the change." According to a private email sent to me, he reports that the Application Verifier was reporting the memory damage.

    I have taken the original code and added a few additional comments.

    #include 
    
    // returns a DEVMODE and DEVNAMES for the printer name specified
    BOOL GetPrinterDevice(LPTSTR pszPrinterName, HGLOBAL* phDevNames, HGLOBAL* phDevMode)
    {
        // if NULL is passed, then assume we are setting app object's
        // devmode and devnames
        if (phDevMode == NULL || phDevNames == NULL)
            return FALSE;

    ****
    jmn: the comment doesn't make sense; what exactly are we assuming here, and why are we returning FALSE if we assume it? For that matter, why are we passing HGLOBAL * parameters when any rational C++ programmer would have used HGLOBAL &?  Remember, the title of this article includes the phrase "in MFC"!
    ****

        // Open printer
        HANDLE hPrinter;
        if (OpenPrinter(pszPrinterName, &hPrinter, NULL) == FALSE)
            return FALSE;
    
        // obtain PRINTER_INFO_2 structure and close printer
        DWORD dwBytesReturned, dwBytesNeeded;
        GetPrinter(hPrinter, 2, NULL, 0, &dwBytesNeeded);
        PRINTER_INFO_2* p2 = (PRINTER_INFO_2*)GlobalAlloc(GPTR,
            dwBytesNeeded);

    ****
    jmn: It is unclear why GlobalAlloc is being used here instead of simply new or malloc.
    ****

        if (GetPrinter(hPrinter, 2, (LPBYTE)p2, dwBytesNeeded,
           &dwBytesReturned) == 0) {

    ****
    jmn: why is a BOOL-returning function value being explicitly tested against 0? Why is it not written as

        if (!GetPrinter(hPrinter, 2, (LPBYTE)p2, dwBytesNeeded, &dwBytesReturned))
        {

    ?
    ****

           GlobalFree(p2);
           ClosePrinter(hPrinter);
           return FALSE;
        }
        ClosePrinter(hPrinter);
    
        // Allocate a global handle for DEVMODE
        // HGLOBAL  hDevMode = GlobalAlloc(GHND, sizeof(*p2->pDevMode) +   // erroneous code
        //   p2->pDevMode->dmDriverExtra);                              
        HGLOBAL hDevMode = GlobalAlloc(GHND, p2->pDevMode->dmSize +        // corrected code
             p2->pDevMode->dmDriverExtra);                         
        ASSERT(hDevMode);

    ****
    jmn:But note that if the ASSERT fails, but we are in release mode, it blindingly goes on to use the illegal pointer which it nominally proved is wrong. Or, if you continue from the ASSERT, it also fails. But a resource-sensitive ASSERT should not presume it will succeed in Release mode. Thus, I've added the if-test below. But the fact that I now have someplace where I need to GlobalFree on something suggests this should be decomposed into several functions which handle the allocation and freeing without having to have all these multiple points of control
    ****

        if(hDevMode == NULL)                                               // added by jmn
          {                       
           GlobalFree(p2);        
           ClosePrinter(hPrinter);
           return FALSE;          
          }                       
        DEVMODE* pDevMode = (DEVMODE*)GlobalLock(hDevMode);
        ASSERT(pDevMode);

    ****
    jmn: so there's an ASSERT here. If it is important enough to test with an ASSERT, it is important enough to have an if-test
    ****

        if(pDevMode == NULL)                                               // added by jmn
           {                       
            GlobalFree(p2);        
            GlobalFree(hDevMode);  
            ClosePrinter(hPrinter);
            return FALSE;          
           }                       
        // copy DEVMODE data from PRINTER_INFO_2::pDevMode
        // memcpy(pDevMode, p2->pDevMode, sizeof(*p2->pDevMode) +          // erroneous code
        //   p2->pDevMode->dmDriverExtra);                       
        memcpy(pDevMode, p2->pDevMode, p2->pDevMode->dmSize +              // corrected code
           p2->pDevMode->dmDriverExtra);                     
        GlobalUnlock(hDevMode);
    
        // Compute size of DEVNAMES structure from PRINTER_INFO_2's data
        DWORD drvNameLen = lstrlen(p2->pDriverName)+1;  // driver name
        DWORD ptrNameLen = lstrlen(p2->pPrinterName)+1; // printer name
        DWORD porNameLen = lstrlen(p2->pPortName)+1;    // port name
    
        // Allocate a global handle big enough to hold DEVNAMES.
        HGLOBAL hDevNames = GlobalAlloc(GHND,
            sizeof(DEVNAMES) +
            (drvNameLen + ptrNameLen + porNameLen)*sizeof(TCHAR));
        ASSERT(hDevNames);
        if(hDevNames == NULL)
           {
            GlobalFree(p2);
            GlobalFree(pDevMode);
    
           }
        DEVNAMES* pDevNames = (DEVNAMES*)GlobalLock(hDevNames);
        ASSERT(pDevNames);
    
        // Copy the DEVNAMES information from PRINTER_INFO_2
        // tcOffset = TCHAR Offset into structure
        int tcOffset = sizeof(DEVNAMES)/sizeof(TCHAR);
        ASSERT(sizeof(DEVNAMES) == tcOffset*sizeof(TCHAR));
    
        pDevNames->wDriverOffset = tcOffset;
        memcpy((LPTSTR)pDevNames + tcOffset, p2->pDriverName,
            drvNameLen*sizeof(TCHAR));
        tcOffset += drvNameLen;
    
        pDevNames->wDeviceOffset = tcOffset;
        memcpy((LPTSTR)pDevNames + tcOffset, p2->pPrinterName,
            ptrNameLen*sizeof(TCHAR));
        tcOffset += ptrNameLen;
    
        pDevNames->wOutputOffset = tcOffset;
        memcpy((LPTSTR)pDevNames + tcOffset, p2->pPortName,
            porNameLen*sizeof(TCHAR));
        pDevNames->wDefault = 0;
    
        GlobalUnlock(hDevNames);
        GlobalFree(p2);   // free PRINTER_INFO_2
    
        // set the new hDevMode and hDevNames
        *phDevMode = hDevMode;
        *phDevNames = hDevNames;
        return TRUE;
    }

     

    KB192570: Multithreaded asynchronous sockets

    The article KB192570 on asynchronous sockets in multiple threads is a disaster. See my rewrite.

    KernelIoControl (Windows CE)

    There is an error in the documentation; the OEMIoControl has the same error

    The documentation says

    [in] Number of bytes returned in lpOutBuf

    The correct documentation should say

    [out] A pointer to a DWORD value. NULL is not permitted for this parameter. Upon completion of the operation, this will contain the number of bytes returned in lpOutBuf.

    A Wave of the Flounder Fin to Reiß. who submitted this error.

    LABEL (MASM)

    Like most assembler documentation, this is completely useless. Consider it, in its entirety:


    Creates a new label by assigning the current location-counter value and the given type to name.

    name LABEL type
    name LABEL [[NEAR | FAR | PROC]] PTR [[type]]

    See Also

    Other Resources

    Directives Reference


    Now, this opens all kinds of questions. For example, what values are permitted for type? In the second form, why is type optional and what type is used by default? In Win32 and Win64, where concepts like NEAR and FAR are nonsensical noise words, what is their significance? What is the meaning of PROC? What is a PTR and why is it different?

    Also, it is not clear what a "location counter" is. For example, it does not mean the IP or EIP or RIP value, which is a run-time concept. It does not state if the symbol thus created is relocatable, so if the code is loaded in a different place, the label and all symbols based on it are relocated. Overall, the documentation is next-to-useless. Note also that the syntax of a name is not defined anywhere in the documentation.

    LB_ADDSTRING 

    For lParam, it should state that for a non-owner-draw control, or an owner-draw control with LBS_HASSTRINGS, a copy of the string referenced by the LPARAM is stored in the control.

    LB_INSERTSTRING 

    For lParam, it should state that for a non-owner-draw control, or an owner-draw control with LBS_HASSTRINGS, a copy of the string referenced by the LPARAM is stored in the control.

    LNK4089

    The /IGNORE: linker switch is not documented. But to suppress this warning message, add /IGNORE:4089 to the linker command line settings.

    LOCAL (MASM)

    This is described as

    LOCAL label [[ [count ] ]] [[:type]] [[, label [[ [count] ]] [[type]]]]...

    Note that the thing which defines the type of the variable seems to be similar to the thing in PROTO which appears to want to define a type, but there it is called tag.

    Note that this description does not state:

    This declaration will cause the standard C function entry sequence

        push ebp
        mov ebp, esp
        sub esp, amount

    to be generated, where amount is the number of bytes required to hold all the variables declared by the LOCAL declaration.

    It does not state that the side effect of this is to make usage of the name generate an address which is [EBP-offset] where offset is the offset of the name from the EBP, and it is expressed as a subtraction since all local variables are below the EBP.

    Nothing is said about how variables are packed so that if the sequence is

    LOCAL data:BYTE, value:QWORD

    we aren't told if they are packed so that they are adjacent or if there is padding added so the values are something-aligned.

    It does not mention that using the LOCAL macro triggers a state in which every instance of the ret instruction is converted to

        mov esp, ebp
        pop ebp
        ret

    LOGBRUSH

    Some documentation describes this as having a member

    LONG lbHatch;

    but the correct documentation is

    ULONG_PTR lbHatch;

    There are errors in the description of the lbStyle parameter. It is absolutely not true that BS_PATTERN8X8 is the same as BS_PATTERN. The value BS_PATTERN is 3 but the value BS_PATTERN8X8 is 7! It is absolutely not true that BS_DIBPATTERN8X8 is the same as BS_DIBPATTERN. The value of BS_DIBPATTERN is 5 but the value of BS_DIBPATTERN8X8 is 8. The correct documentation would be

    BS_DIBPATTERN8X8: This value, distinct from BS_DIBPATTERN, has the same implications (in terms of lbHatch) as BS_DIBPATTERN.

    BS_PATTERN8X8: This value, distinct from BS_PATTERN, has the same implications (in terms of lbHatch) as BS_PATTERN.

    The descriptions are not really coherent. The possible values for lbStyle and their corresponding interpretations are shown below. Note that the documentation would be a lot clearer if it were presented in tabular form like this

    .lbStyle Meaning of .lbHatch
    BS_DIBPATTERN A Global or Local Memory Handle to a packed DIB. This handle is obtained by calling GlobalAlloc with the option GMEM_MOVEABLE or LocalAlloc with the option LMEM_MOVEABLE to allocate a block of memory, which will contain the DIB. The format of a packed DIB is a BITMAPINFO structure immediately followed by the array of bytes that define the pixels of the bitmap.
    BS_DIBPATTERNPT The current documentation is erroneous and confusing. It states

    A Global or Local Memory Pointer to a packed DIB. This pointer is obtained by calling GlobalAlloc with the option GMEM_FIXED or LocalAlloc with the option LMEM_FIXED to allocate a block of memory, or from a pointer returned by LocalLock of the DIB handle.

    What is missing here is the following information; it is not clear why this is omitted

    • The pointer may be also obtained by calling HeapAlloc, by calling malloc (or any of its variants) or, in C++, calling new to allocate storage.
    • The pointer may be the pointer returned by GlobalLock of a DIB handle allocated by GlobalAlloc

    I do not see any reason that the pointer is limited to memory allocated by GlobalAlloc or LocalAlloc. I believe the correct documentation is

    A memory pointer to a packed DIB. The format of a packed DIB is a BITMAPINFO structure immediately followed by the array of bytes that define the pixels of the bitmap.

    If there is a need to specify the means of obtaining a memory pointer, the following could be included, although I think the documentation should stop right here. But if there is a compulsion to talk about it, then the following can be supplied

    The pointer may be obtained by any of the following means

    Using GlobalAlloc with GMEM_MOVEABLE, followed by GlobalLock; the value returned by GlobalLock is the pointer, and filling the allocated storage with a packed DIB.

    Using GlobalAlloc with GMEM_FIXED. The value returned by GlobalAlloc is the pointer to memory, and filling the allocated storage with a packed DIB.

    Using LocalAlloc with LMEM_MOVEABLE, followed by a LocalLock; the value returned by LocalLock is the pointer, and filling the allocated storage with a packed DIB.

    Using LocalAlloc with LMEM_FIXED. The value returned by LocalAlloc is the pointer to memory, and filling the allocated storage with a packed DIB.

    Using HeapAlloc to allocate from the default or a custom heap, and filling the allocated storage with a packed DIB.

    Using VirtualAlloc to do raw memory allocation, and filling the allocated storage with a packed DIB.

    Using malloc or calloc to allocate memory on the heap, and filling the allocated storage with a packed DIB.

    Using (in C++) new to allocate memory on the heap, and filling the allocated storage with a packed DIB.

    Using GetDIBits to retrieve the bits from a bitmap handle

    BS_DIBPATTERN8X8 undocumented

    This should be lumped into the discussion of BS_DIBPATTERN. Note that the value is not "the same as"; BS_DIBPATTERN=5 but BS_DIBPATTERN8X8=8

    BS_HATCHED HS_ value for hatched style
    BS_HOLLOW/BS_NULL Ignored
    BS_PATTERN Handled to a bitmap
    BS_PATTERN8X8 undocumented

    This should be lumped into the discussion of BS_PATTERN. Note that the value is not "the same as"; BS_PATTERN=4 but BS_PATTERN8X8=7

    BS_SOLID Ignored

    LOGFONT

    The LOGFONT description omits the option for lfPitchAndFamily the value MONO_PITCH.

    In VS2008, the page

    ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.en/shellcc/platform/shell/reference/structures/logfont.htm

    does not contain cross-references nor does it explain any of the values that can go into any of the fields! Why is space wasted on such a completely pointless description?

    There is a perfectly good description found in

    ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.en/gdi/fontext_1wmq.htm

    but for some reason the index entry "LOGFONT structure" does not actually link to this entry; instead it links to CreateFontIndirect, which then requires that the reader find the LOGFONT link and click on that! I can see no reason whatsoever for the first cited link to exist at all, since it links to a piece of worthless text that has no meaningful content and no See Also references!

    Furthermore, in the See Also references, it does not have a reference to GetObject or CFont::GetLogFont.

    LOGPALETTE

    The first member of this structure is described as "Specifies the version number of the system". WHAT VERSION NUMBER? OBTAINED BY WHAT MEANS? WOULD IT INCONVENIENCE THE WRITERS TERRIBLY TO EXPLAIN THIS, AND GIVE A SEE ALSO REFERENCE TO THE MEANS OF OBTAINING THIS???

    LVCOLUMN

    There is a consistent set of errors in the description of the mask member. It states "the member is valid" when that makes sense only for SetColumn. The correct documentation should be:

    mask

    Field specifying which members contain valid information for SetColumn, or which members should be retrieved on GetColumn. This member can be 0, or one or more of the following values:

    LVCF_FMT

    The fmt member is valid for SetColumn, or should be retrieved on GetColumn.

    LVCF_IMAGE

    Version 4.70. The iImage member is valid for SetColumn, or should be retrieved on GetColumn

    LVCF_ORDER

    Version 4.70: The iOrder member is valid for SetColumn, or should be retrieved on GetColumn.

    LVCF_SUBITEM

    The iSubItem member is valid for SetColumn, or should be retrieved on GetColumn

    LVCF_TEXT

    The pszText member is a valid pointer for SetColumn, or should be retrieved on GetColumn. Note that for GetColumn, the cchTextMax field must also specify the number of characters that are available in the pszText buffer. It is ignored on SetColumn, and the string referenced by pszText is assumed to be NUL-terminated.

    LVCF_WIDTH

    The cx member is valid for SetColumn, or should be retrieved on GetColumn.

    LVS_EX_* symbols

    Why are these not settable at design time from the Properties? What good is to have Properties if they apply only to what some random VS programmer thought they should apply to at some time in history, without any understanding of the role of the Properties page? All extended flags for every control that supports them must be settable from the Properties page!

    It is not documented that these "extended" styles are not set by using ModifyStyleEx but must be set using CListCtrl::SetExtendedStyle.

    In addition, in my version of the VS2008 documentation, these do not appear in the index. EVERY symbol, without exception, must appear in the index! Apparently, they are not documented at all, because a "Search" for symbols like LVS_EX_FULLROWSELECT do not yield a hit.

    _makepath (_wmakepath, _tmakepath), _makepath_s (_wmakepath_s, _tmakepath_s)

    The documentation erroneously states "if drive is a null character or an empty string, no drive letter and colon appear in the composite path string", and it should state "if drive is a NULL pointer or points to an empty string, no driver letter and colon appear in the composite path string".

    The documentation erroneously states "if dir is a null character or an empty string, no slash is inserted in the composite path string", and it should state "if dir is a NULL pointer or points to an empty string, no slash is inserted in the composite path string".

    The documentation erroneously states "if ext is a null character or points to an empty string, no period is inserted in the composite path string", and it should state "if dir is a NULL pointer or points to an empty string, no period is inserted in the composite path string".

    In addition, the statement about buffer overrun does not explicitly state that there is no bound check performed in the function itself, and consequently it can cause a buffer overrun; the documentation states somewhat ambiguously

    path
    Full path buffer. _makepath does not check that path does not exceed _MAX_PATH.

    This should be explained in the notes, and omitted from the parameter description. Better still, in VS >= 2005, the code should be modified to essentially call _makepath_s() specifying MAX_PATH as the maximum value, thus ensuring that there can never be a buffer overrun.

    The description of path should appear in the Remarks section along with the other parameters

    path
    The output buffer. No bounds checks are made on the number of characters written to this buffer. While the recommended practice is that this buffer be _MAX_PATH characters in length, if the sum of the drive, dir, fname and ext plus required punctuation exceeds the size of the buffer, even if it is _MAX_PATH in length, there will be a buffer overrun and a potential security violation. If path is a fixed-size buffer, it is the responsibility of the programmer to ensure that the constraint

    sizeof(path)/sizeof(TCHAR) >= (drive ? _tcslen(drive) + 1 : 0) + (dir ? _tcslen(dir) + 1 : 0) + (fname ? _tcslen(fname) : 0) + (ext ? _tcslen(ext) + 1 : 0)

    is obeyed. Note this constraint is "conservative" in that if the path terminates with a / or \, or the extension has a '.' already, this constraint slightly overestimates the required size.

    map::erase 

    No mention is made of the proper use of this in an iterator. The corrected documentation must include the following information:

    Remarks

    If the element being erased is the current element in an iteration, the iteration becomes undefined. The recommended mechanism to handle this is as shown below.

    typedef std::map<T1, T2> sometype;
    sometype SomTypeInstance;
    for(sometype::iterator iter = SomeTypeInstance.begin(); iter != SomeTypeInstance.end(); )
       { /* loop over elements */
        if(somecondition)
           { /* erase element */
            iter = SomeTypeInsance.erase(iter);
           } /* erase element */
        else
           { /* no erasure */
            ++iter;
           } /* no reasure */
        } /* loop over elements */

    A Wave of the Flounder Fin to Doug Harrison for this information. He observes that the Microsoft iterator returns an iterator type. There is another case where some implementations do not return an iterator type and the code below would be used.

    map::erase does not return an iterator

    for(sometype::iterator iter = SomeTypeInstance.begin(); iter  != SomeTypeInstance.end(); )
       { /* loop over elements */
        if(somecondition)
          { /* erase element */
           SomeTypeInstance.erase(i++);  // note use of PostIncrement!  Eseential!
          } /* erase element */
        else
          { /* no erasure */
           ++iter;
          } /* no erasure */
       } /* loop over elements */

    MAT2 

    This contains references to the undefined type FIXED which is defined nowhere else in the MSDN documentation. It is not indexed, and can only be found by searching for the definition in the Platform SDK header files.

    mciSendCommand 

    The documentation for this API is highly erroneous. This is because it is still the original 16-bit API documentation, and neither the documentation nor the functionality are compatible with a Unicode-aware Win32 system.

    For example, the definition given in the documentation is

    MCIERROR mciSendCommand(
        MCIDEVICEID IDDevice,
        UINT uMsg,
        DWORD fdwCommand,
        DWORD_PTR dwParam)

    the correct documentation is

    MCIERROR mciSendCommand(
       MCIDEVEICEID IDDevice,
       UINT uMsg,
       DWORD_PTR fdwCommand,
       DWORD_PTR dwParam)

    In addition, every example that follows shows a DWORD cast for the last parameter! For example, for MCI_STATUS, the example is

    MCIERROR micSendCommand(
       MCIDeviceID wDeviceID,
       MCI_STATUS,
       DWORD dwFlags,
       (DWORD)(LPMCI_STATUS_PARMS)lpStatus);

    which is nonsensical. It must be DWORD_PTR for the cast, and dwFlags is now a DWORD_PTR. And in what sane, intelligent universe is a 32-bit value designated as a "wDeviceId", which means "16-bit unsigned value"? Silly, silly Hungarian Notation, that's what!\

    Now, when we look at the values in the MCI_STATUS_PARMS, we find that the dwReturn field is a DWORD_PTR. But if the option is MCI_STATUS_LENGTH, it is supposed to return the "total media length". This is a value which can easily exceed 32 bits, since it is the file size. Big news! FILE SIZES CAN BE MORE THAN 32 BITS!!!!! I wonder if anyone actually read the 32-bit system documentation. So the API cannot work correctly to return the length of a file whose size is > 32 bits.

    Didn't anyone notice that this API cannot work correctly in a 32-bit environment? Didn't anyone notice that this should appear in the documentation?

    MCI_STATUS:MCI_STATUS_LENGTH 

    This documentation utterly fails to mention that this variant cannot work correctly if the file length is > 4.2GB, due to fundamental design flaws in the API, which was built for 16-bit Windows! The dwReturn value is only 32 bits, insufficient to hold real file lengths.

    memcpy_s 

    The documentation for this function is confusing and misleading. It changes terminology, introduces previously unknown terms, and generally leave the reader confused. So confused, in fact, that it is impossible to actually tell what is going on. I think the documentation of wmemcpy_s shown below is correct. Note that it should be possible to deduce the correct behavior of such a trivial function without having to read the Remarks section.

    errno_t memcpy_s(
        void * dest,
        size_t number_of_elements,
        const void * src,
        size_t count)
    
    errno_t wmemcpy_s(
        void * dest,
        size_t number_of_elements,
        const void * src,
        size_t count)

    dest

    Address of a location into which to copy the data

    numberOfElements

    The size of the destination buffer. For memcpy_s this is a byte count, for wmemcpy_s this is a wchar_t count.

    src

    Address of a buffer from which to copy the data

    count

    The number of items to be copied. For memcpy_s this is a byte count; for wmemcpy_s this is a wchar_t count.

    MessageBeep

    This documentation uses the horribly obsolete names for the MessageBox flags. Its documentation is horribly confusing. The correct documentation should state

    The codes from MessageBox can be used to select which sound is played. The MessageBeep will play the same sound that a MessageBox using these sounds would play. The sounds are determined by looking up the sound name in the Registry. These Registry entries are set by the user when the "Sounds" control panel applet is chosen, and should not be manipulated directly by hand-editing or programmatically editing the Registry. The sounds names used in the setup still use the obsolete Win16 names, but it is strongly recommended that Best Practice is to use the new names.

    Sound name Sound selected
    -1 A simple beep sound, which has no equivalent in the MessageBox codes.
    MB_ICONINFORMATION
    MB_ICONASTERISK
    (obsolete)
    The sound selected by the user as the Windows.Asterisk sound
    MB_ICONWARNING
    MB_ICONEXCLAMATION
    (obsolete)
    The sound selected by the user as the Windows.Exclamation sound
    MB_ICONERROR
    MB_ICONHAND
    (obsolete)
    MB_ICONSTOP
    The sound selected by the user as the Windows.CriticalStop sound
    MB_OK The sound selected by the user as the Windows.Default Beep sound

    The completely silly statement that -1 is resolved to 0xFFFFFFFF within the function must be eliminated! It appears that somebody read the source code to write this documentation. Who cares?

    Using the obsolete Win16 names for these sounds is silly.

    MessageBox

    The documentation is incoherent and misleading. The correct documentation would carefully split the information up into multiple tables instead of putting the dividing text incoherently among the regular text, making it impossible to see the categories.

    uType

    [in] Specifies the contents and behavior of the dialog box. This parameter can be formed by the bitwise OR operation based on the following groups of flags. If no flag is specified, the indicated highlighted value is the default
    Button Selection
    MB_ABORTRETRYCANCEL (as per the current documentation, I'm not going to retype all that here)
    MB_CANCELTRYCONTINUE  
    MB_OK  
    MB_OKCANCEL  
    MB_RETRYCANCEL  
    MB_YESNO  
    MB_YESNOCANCEL  
    The following flag can be added to any of the above
    MB_HELP  

    The button selection may be combined with one of the flags to indicate an icon

    Icon Selection
    MB_ICONWARNING
    MB_EXCLAMATION (obsolete)
    (as per the current documentation, I'm not going to retype all that here)
    MB_ICONINFORMATION
    MB_ICONASTERISK (obsolete)
     
    MB_ICONQUESTION  
    MB_ICONERROR
    MB_ICONSTOP
    MB_ICONHAND (obsolete)
     

    The above may be combined with a flag that indicates which button will be the default button.

    Default Button Selection
    MB_DEFBUTTON1 (as per the current documentation, I'm not going to retype all that here)
    MB_DEFBUTTON2  
    MB_DEFBUTTON3  
    MB_DEFBUTTON4  

    This may be combined with a flag that indicates the modality of the dialog box

    Modality Selection
    MB_APPLMODAL (as per the current documentation, I'm not going to retype all that here)
    MB_SYSTEMODAL  
    MB_TASKMODAL  

    (The current documentation erroneously states that you can use one or more of the following options. The correct documentation is that you can use zero or more of the following options)

    This may be combined with any of the following flags

    Miscellaneous options
    MB_DEFAULT_DESKTOP_ONLY (as per the current documentation, I'm not going to retype all that here)
    MB_RIGHT  
    MB_RTLREADING  
    MB_SETFOREGROUND  
    MB_TOPMOST  
    MB_SERVICE_NOTIFICATION  
    MB_SERVICE_NOTIFICATION_NT3X  

    _MFC_VER

    It would have been nice to have something meaningful in the description of _MFC_VER, such as the following table. Note that none of this is helped by the total inconsistency where some values are expressed in decimal and some in hex; EVERY symbol that has a "version" meaning shall have all its version history displayed. No exceptions. Ever.

    _MFC_VER Product release
    ? Visual Studio 4.0
    ? Visual Studio 4.1
    ? Visual Studio 4.2
    0x0421 ‡ Visual Studio 5.0
    0x0600 Visual Studio 6.0
    0x0700 Visual Studio .NET 2002
    0x0710 Visual Studio .NET 2003
    0x0800 Visual Studio 2005
    0x0900 Visual Studio 2008

    A Wave of the Flounder Fin to David Scambler for this information

    Microsoft Macro Assembler Reference

    This does not discuss

    It does not state anywhere about how the case mapping of identifiers is controlled. If you know about OPTION:CASEMAP:NONE or the /C switches, you know the answer already, but if you don't know, you can't find out!

    Microsoft Macro Assembler: Operator +

    This erroneously contains the definition of the unary-minus operator

    Microsoft Macro Assembler: Operator -

    This contains the description of the binary subtract operator, but omits the definition of the unary minus operator, which is erroneously placed under "Operator +"

    Microsoft Macro Assembler: Operator Hierarchy 

    I'm sure there is one. It just isn't documented. Another carefully guarded secret!

    Microsoft Macro Assembler Reference: OPTION

    It lists a set of options which have no explanations; we are therefore left to guess as to what these might mean, or how they might be written. Sadly, this information was documented, but due to terminal incompetence on the part of the documentation people, this information has been discarded, apparently to save space.

    Thus far, I have discovered:

    OptionMeaning
    OPTION CASEMAP:NONE
    Does not change the case of public or external identifiers, or internal identifiers
    OPTION CASEMAP:NOPUBLIC
    PUBLIC symbols are case-sensitive, but internal symbols and non-PUBLIC symbols are case-insensitive
    OPTION CASEMAP:ALL
    All symbols are case-insensitive
    OPTION DOTNAME
    Allows "." as the leading character in variable, macro, structure, union and member names
    OPTION EPILOGUE:macroname
    Calls macroname to generate a user-defined epilogue when a ret instruction is executed
    OPTION EXPR16
    Sets the expression word size to 16 bits. The default is EXPR32, unless the M510 option is specified, in which case the default is EXPR16
    OPTION EXPR32
    Sets the expression word size to 32 bits. This is the default, unless the M510 option is specified, in which case the default is EXPR16
    OPTION LANGUAGE:BASIC
    Functions expects to be called from Basic
    OPTION LANGUAGE:C
    Indicates C-style function calling and naming conventions. External and public names will be case-sensitive
    OPTION LANGUAGE:FORTRANT
    Functions expect to be called from FORTRAN
    OPTION LANGUAGE:PASCAL
    (Obsolete): Pascal-style function calling and naming conventions
    OPTION LANGUAGE:STDCALL
    Indicates __stdcall-style function calling and naming conventions. External and public names will be case-sensitive
    OPTION LANGUAGE:SYSCALL
     
    OPTION LJMP
    Enables automatic jump-lengthening (note that experimentation with this feature seems to indicate that it does nothing; instead of jump-lengthening, assembly errors about the jump distance being too great will appear)
    OPTION NODOTNAME
    Disallows "." as the leading character in variable, macro, structure, union and member names
    OPTION NOKEYWORD:keyword
    Disables the specified reserved word
    OPTION NOLJMP
    DIsables automatic jump-lengthening
    OPTION NOOLDMACROS
    Use the MASM 6.1 treatment of macros
    OPTION NOOLDSTRUCTS
    Use the MASM 6.1 treatment of structure members
    OPTION NOREADONLY
    Code segments will not be read-only. Note that on some machines that create data segments as no-execute, this will generate a #GP fault when there is an attempt to execute the code.
    OPTION NOSCOPED
    Makes all labels used within a PROC/ENDP global to the compilation unit
    OPTION NOSIGNEXTEND
    Overrides the default sign-extending opcodes for the AND, OR, and XOR instructions and generates the larger non-signed-extended forms of these instructions for compatibility with the NEC V25 and NEC V35 controllers.
    OPTION OFFSET:FLAT
    Determines the result of OFFSET operator fixups. Sets the defaults for fixeups to be computed relative to a flag frame (the .386 mode [or higher] must be enabled to use FLAT)
    OPTION OFFSET:GROUP
    Determines the result of OFFSET operator fixups. Sets the defaults for fixeups to be computed group-relative (if the label is in a group). This is the default
    OPTION OFFSET:SEGMENT
    Determines the result of OFFSET operator fixups. Sets the defaults for fixups to be segment-relative (compatible with MASM 5.1)
    OPTION OLDMACROS
    Use the old MASM 5.1 treatnent of macros
    OPTION OLDSTRUCTS
    Enables MASM 5.1 treatment of structure members
    OPTION PROC:visibility
    Sets the default visibility for all PROCs. Allowable visibility values are PUBLIC, EXPORT or PRIVATE
    OPTION PROLOGUE:macroname
    Instructs the assembler to call the user-defined macroname to generate a function prologue instead of the standard prolog code
    OPTION READONLY
    Enables checking for instructions that modify code segments, thereby guaranteeing that read-only code segments are not modified. Same as the /p command-line option of MASM 5.1, except that it affeacts only segments with at least one assembly instruction, not all segments. The argument is useful for protected mode programs, where code segments must remain read-only. This is the default.
    OPTION SCOPED
    All labels within a PROC/ENDP are scoped to that procedure and will not conflict with labels of the same name in other PROCs. This is the default
    OPTION SEGMENT:setSize
    Allows global default segment size to be set. Also determines the default address size for external symbols defined outside any segment. The segSize can be USE16, USER32, or FLAT.
    OPTION SETIF2:TRUE|FALSE
    If TRUE, .ERR2 statements and IF2 and ELSEIF2 conditional blocks are evaluated on every pass. If FALSE, they are not evaluated. If SETIF2 is not specified (or implied), .ERR2, IF2 and ELSEIF2 expressions cause an error. Both the /Zm command-line argument and OPTION M512 imply SETIF2:TRUE

    Microsoft Macro Assembler Reference: Syntax of identifiers

    identifier = dotletter (dotletter | digit)*
    letter = 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' | 'L' | 'M' |
             'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X' | 'Y' | 'Z' |
             'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm' |
             'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x' | 'y' | 'z' |
             '@' | '$' | '_' | '?'
    dotletter = IF OPTION:DOTNAME
                       '.' | letter 
                ELSE
                       letter

    This does not encompass the idea that the '@' can only form identifiers if there is at least one other letter or digit in the identifier. I don't feel like doing the complex syntax for this; Microsoft is supposed to have done this, and I'm getting tired of having to do all the detail work all the time.

    Microsoft Macro Assembler Reference: Syntax of initializers 

    initializer = value | count DUP (value)
    value = constant | '?'
    count = constant

    An initializer can initialize a sequence of values by using the DUP keyword. The left of the DUP is the number of times to duplicate the value, and the parenthesized value to the right of the DUP is the value to use for the initialization. The value can be '?' indicating that the data is not initialized to a specific value.

    To declare an uninitialized array of 100 integers:

    array DD 100 DUP (?)

    To declare an initialized array of 100 integers, all initialized to 1:

    array1 DD 100 DUP (1)

    Microsoft Macro Assembler Reference: Syntax of hexadecimal constants

    hexconstant = digit+ 'h' | '0' (hexdigit | digit)* h
    digit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
    hexdigit = 'A' | 'a' | 'B' | 'b' | 'C' | 'c' | 'D' | 'd' | 'E' | 'e' | 'F' | 'f'

    For example:

        22h
        0FFh

    Note that

        FFh 

    is an identifier.

    Microsoft Macro Assembler Reference: Syntax of string contstants 

    A string constant is expressed as a sequence of characters in single quote marks. The characters that appear within the single quotes include all printable characters. However, unlike C/C++, there is no "escape" convention for non-printing characters. To get a single quote mark, two single quote marks are used. Strings are not implicitly NUL-terminated.

    C/C++ string:

    char name = "A\tB'C\r\n";               

    Equivalent MASM string:

    name DB 'A', 09H, 'B''C', 0Dh, 0Ah, 00H 

    if this is done frequently, it makes sense to do definitions of the sequences

    _CR  EQU 0DH                               
    _LF  EQU 0AH                               
    _TAB EQU 09H                               
    _NUL EQU 00H                               
                                               
    name DB 'A'. _TAB, 'B''C', _CR, _LF, _NUL  

    Unicode is not supported.

    wchar_t nameU = L"A\tB'C\r\n";                                                           

    is written in assembler as

    nameU db 'A', 00H, 09H, 00H, 'B', 00H, '''', 00H, 'C', 00H, 0DH, 00H, 0AH, 00H, 00H, 00H 

    ML and ML64 Command-Line Reference

    This states that the /Zi option creates "CodeView" information. Hello? What is CodeView? Perhaps it should say, correctly, "Creates symbol table information for the debugger."

    mmioOpen

    This states,

    MMIO_GETTEMP Creates a temporary filename, optionally using the parameters passed in szFilename. For example, you can specify "C:F" to create a temporary file residing on drive C, starting with letter "F". The resulting filename is placed in the buffer pointed to by szFilename. The return value is MMSYSERR_NOERROR (cast to HMMIO) if the temporary filename was created successfully or MMIOERR_FILENOTFOUND otherwise. The file is not opened, and the function does not return a valid multimedia file I/O file handle, so do not attempt to close the file. This flag overrides all other flags.

    There are so many things wrong with this it is hard to figure out what is going on. For example, does C:F mean the same as C:\F or does it mean that the file is created in the current directory selected on the C: drive? Why was this particular example chosen (note that on Vista, it is often the case that the root drive C:\ is write-protected, so this is a particularly poor choice for Vista if it really goes in the root directory of the drive. What happens if I just specify "F" as the name?  What is the format of the temporary name? Is the prefix limited to one letter? How large is the buffer pointed to by szFileName? 128 or MAX_PATH? Why the odd documented restriction to 128 bytes when MAX_PATH is 260?

    Note also that this call is illegal:

    mmioOpen("C:F", NULL, MMIO_GETTEMP)

    although it compiles is completely illegal, because "C:F" is a literal, an LPCSTR and not an LPSTR; a sane compiler would diagnose this as an error, but a defective compiler (as most C compilers are) will allow this and then take an access fault when the program tries to write back to the string literal pool (which is write-protected).

    One also wonders why the multimedia functions are limited to 8-bit character names in this modern world. Note that the parameter type is LPSTR, not LPTSTR.

    Due to a serious oversight in perceiving reality, mmioOpen erroneously states that it wants an LPSTR argument. However, if you actually look at the header files, there are two mmioOpen calls, mmioOpenA and mmioOpenW. These take the appropriate argument types. Surprisingly, the file name, which in any intelligent design would be a const, is specified explicitly as a non-const value. The question is whether this is the consequence of a poor design of the function, which requires a modifiable string, or a slovenly definition, that should have been const but isn't. So the correct documentation would have been an LPTSTR argument for the file name, and in a sane world it should be an LPCTSTR argument.

    _MSC_VER

    It would be nice to have something meaningful in the description of the _MSC_VER macro, such as the following table. Note that none of this is helped by the total inconsistency where some values are expressed in decimal and some in hex; EVERY symbol that has a "version" meaning shall have all its version history displayed. No exceptions. Ever.

    _MSC_VER Product release
    § 600 Microsoft C 6.0 (16-bit)
    § 700 Microsoft C 7.0 (16-bit)
    § 800 Visual C for Windows 1.0
    § 900 Visual C++ for Windows 2.0
    § 1000 Visual Studio 4.0
    § 1000 Visual Studio 4.1
    § 1000 Visual Studio 4.2
    § 1100 Visual Studio 5.0
    § 1200 Visual Studio 6.0

    1300 Visual Studio .NET 2002

    1310 Visual Studio .NET 2003
      1400 Visual Studio 2005
      1500 Visual Studio 2008

    § Special thanks to "BobF" who pointed out the obsolete KB article 65472, which is several versions out of date but provided the earlier information through VS6.0.

    Special thanks to David Wilkinson for this information

    Note to documentation creators of MSDN documentation: THIS INFORMATION BELONGS INLINE IN THE DESCRIPTION OF THE PREPROCESSOR SYMBOL _MSC_VER, not in some random KB article that shows that this should have been put into the documentation years ago! To omit critical information like this inline is simply irresponsible! The failure to have a hyperlink between such critical pieces of information is equally irresponsible. (When I complained about this at the 2007 MVP Summit, I was told that this was because the KB articles are a completely different political entity within Microsoft from the Platform SDK documentation team. I pointed out that Microsoft's internal politics or lack of organization should not be used as an excuse to penalize the end users, and the solution is to fix their internal political problems, not tell us they have them).

    MsgWaitForMultipleObjectsEx

    There is a table of return values, and a sentence AFTER the table that talks about WAIT_FAILED. Why is this not an entry in the table itself? Why is it a separate sentence and not part of the table of return values?

    _MSVC_RUNTIME_CHECKS

    The documentation does not state that this is defined only for _MSC_VER >= 1300.

    _MT

    This should also point out that if the multithreaded DLL (as opposed to the multithreaded static library) is specified, the symbol _DLL will also be defined. The hyperlink shall be to the exact paragraph defining _DLL.

    NETRESOURCE

    The NETRESOURCE type is insufficiently documented. There are several values that are not documented that appear in actual results. The corrected documentation, insofar as I can deduce it, is shown below.

    Key here is that it does not distinguish between the use of NETRESOURCE as an input to a network API call, and as output from a network API call.

    dwScope

    Scope of the enumeration. This member can be one of the following values:

    Value Meaning
    RESOURCE_CONNECTED Enumerate currently connected resources. The dwUsage member cannot be specified
    RESOURCE_GLOBALNET Enumerate all resources on the network. The dwUsage member is specified
    RESOURCE_REMEMBERED Enumerate remembered (persisten) connections. The dwUsage member cannot be spcified
    RESOURCE_RECENT ?
    RESOURCE_CONTEXT ?

    dwType

    Set of bit flags identifying the type of resource. This member can be one of the following values:

    What sense does it mean to say that this is 'bit flags' and the member can be 'one of'. It can be either exactly one of, or one or more of, but it can't be just 'one of'. Perhaps the correct documentation is

    Set of bit flags identifying the type of resource to be enumerated; upon an enumeration, a specific resource will be represented by exactly one of the values below.

    Value Meaning
    RESOURCETYPE_ANY Input request: Indicates that any resource should be enumerated

    Output: never appears

    RESOURCE_DISK Input request: Indicates disk resources should be scanned

    Output: indicates the resource is a disk resource

    RESOURCE_PRINT Input request: Indicates printer resources should be scanned

    Output: indicates the resource is a printer resource

    RESOURCE_RESERVED ?
    RESOURCE_UNKNOWN Input request: illegal value(?)

    Output: Indicates the resource is an unknown type(?)

    dwDisplayType

    Display options for the network object in a network browsing user interface. The member can be one of the following values

    Value Meaning
    RESOURCEDISPLAYTYPE_DOMAIN The object should be displayed as a domain
    RESOURCEDISPLAYTYPE_SERVER The object should be displayed as a server
    RESOURCEDISPLAYTYPE_SHARE The object should be displayed as a share
    RESOURCEDISPLAYTYPE_GENERIC The method used to display the object does not matter
    RESOURCEDISPLAYTYPE_FILE The object should be displayed as a file
    RESOURCEDISPLAYTYPE_GROUP ?
    RESOURCEDISPLAYTYPE_NETWORK The object should be displayed as a network
    RESOURCEDISPLAYTYPE_ROOT ?
    RESOURCEDISPLAYTYPE_SHAREADMIN The object should be displayed as an administrative share
    RESOURCEDISPLAYTYPE_DIRECTORY The object should be displayed as a directory
    RESOURCEDISPLAYTYPE_TREE ?
    RESOURCEDISPLAYTYPE_NDSCONTAINER ?

    dwUsage

    Value Meaning
    RESOURCEUSAGE_CONNECTABLE Input request: Requests enumeration only of connectable resources (?)

    Output: The resource is a connectable resource; the name pointed to by the lpRemoteName member can be passed to the WNetAddConnection function to make a network connection

    RESOURCEUSAGE_CONTAINER Input request: Never used

    Output: The resource is a container resource; the name pointed to by the lpRemoteName member can be passed to the WNetOpenEnum function to enumerate the resources in the container

    RESOURCEUSAGE_NOLOCALDEVICE ?
    RESOURCEUSAGE_SIBLING ?
    RESOURCEUSAGE_ATTACHED Input request: Requests enumeration only of attached resources (?)

    Output: The resource is an attached resource. (?say more?)

    RESOURCEUSAGE_ALL Input: (RESOURCEUSAGE_CONNECTABLE | RESOURCEUSAGE_CONTAINER | RESOURCEUSAGE_ATTACHED)

    Output: Never appears as a single value

    RESOURCEUSAGE_RESERVED Input request: ?

    Output: ?

    lpLocalName

    If the dwScope parameter is equal to RESOURCE_CONNECTED or RESOURCE_REMEMBERED, this member is a pointer to a null-terminated character string that specifies the name of the local device. This member is NULL if the connection does not use a device.  If this is pointed to by an element returned by WNetEnumResource, this string will be in the block of data pointed to by the lpBufferSize parameter, and will follow the array of NETRESOURCE objects that are returned.

    lpRemoteName

    If the entry is a network resource, this member is a pointer to a null-terminated character string that specifies the remote network name.
    If the entry is a current or persistent connection, the lpRemoteName points to the network name associated with the name pointed to by the lpLocalName member.
    The string can be MAX_PATH characters in length, and it must follow the network provider's naming conventions.
    If this is pointed to by an element returned by WNetEnumResource, this string will be in the block of data pointed to by the lpBufferSize parameter, and will follow the array of NETRESOURCE objects that are returned.

    lpComment

    Pointer to a null-terminated character string that contains a comment supplied by the network provider. It is unspecified if this can NULL if there is no comment provided.

    lpProvider

    Pointer to a null-terminated string that contains the name of the provider that owns the resource. This member can be NULL if the provider name is unknown. To retrieve the provider name, you can call the WNetGetProviderName function.
    If this is pointed to by an element returned by WNetEnumResource, this string will be in the block of data pointed to by the lpBufferSize parameter, and will follow the array of NETRESOURCE objects that are returned.

    NM_DBLCLK

    So I want to find out what happens when I double-click a CListView control. To discover what message is sent, I have to read the MESSAGE_MAP entry, which tells me it is NM_DBLCLK. So I go to the MSDN index for NM_DBLCLK and get routed to an article on double-clicking toolbar controls, which is less than useful. I want to find out if the LPARAM for the NMITEMACTIVATE structure is defined, because the poorly-written documentation says it is not defined "for those notifications that do not use it". So how am I expected to discover this if there is no documentation on this event for a list view?

    NMITEMACTIVATE

    States that LPARAM is not defined "for those notifications which do not use it". Hello? Why am I not told what notifications use it/don't use it? This isn't even a listed notification for a list control.

    The answer is that for the NM_DBLCLK notification for a CListView, this does indeed have a non-NULL LPARAM field, but it does not represent the actual data set for the item. For example, consider

    int n = c_Data.InsertItem(i, _T("Test"));
    c_Data.SetItemData(n, new Thing(_T("Test")));

    Now, when I get to my handler, I expect to be able to write

    Thing * t = (Thing *)pNMItemActivate->lParam;

    but this in fact gives an erroneous answer; in particular

    ASSERT(t == (Thing*)GetItemData(pNMItemActivate->iItem));

    fails. So what is the lParam field that is delivered in the pNMItemActivate parameter?

    In addition, the VS2008 handler for this message generates the erroneous code

     LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<NMITEMACTIVATE>(pNMHDR);

    which makes no sense at all; the correct code would have been

     LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
    

    NMTREEVIEW

    The documentation for this structure is erroneous and confusing.

    For example, it states

    itemOld
    TVITEM structure that contains information about the old item state. This member is zero for notification messages that do not use it.
    itemNew

    TVITEM structure that contains information about the new item state. This member is zero for notification messages that do not use it.

    What could it possibly mean by the nonsensical statement 'This member is zero for notification messages that do not use it'? The member is not a scalar and consequently cannot have a value of 0! The field is an entire member. It might have said "All members of this structure are set to zero for notification messages that do not use it". But the statement as it stands is nonsense. It sounds like the person writing it has confused a pointer to a structure with a structure.

    Having a list of the notification messages and what fields they use would be useful. I infer that the following use it

    Notification message itemOld itemNew
    TVN_BEGINDRAG unused hItem, state and lParam provide information about the object being dragged
    TVN_BEGINRDRAG
    TVN_DELETEITEM hItem and lParam contain information about the item being deleted unused
    TVN_ITEMEXPANDED unused hItem, state and lParam provide information about the object which has just been or is being expanded
    TVN_ITEMEXPANDING
    TVN_SELCHANGED mask, hItem, state, and lParam contain information about the previously-selected item. The stateMask member is undefined. mask, hItem, state, and lParam contain information about the newly-selected item. The stateMask member is undefined.
    TVN_SELCHANGING "the itemOld and itemNew members contain valid information about the currently-selected item and the newly-selected item". There appears to be no restriction about any fields being invalid (see TVN_SELCHANGED)
    TVN_SINGLEEXPAND "contains information about this notification"

    NMTTDISPINFO 

    This is erroneously documented, it indicates that the szText parameter is a char array of 80, but in fact it is a TCHAR array of 80.

    __noop 

    A typical slovenly example. It uses an option that is remarkably close to the symbol _DEBUG, but is not, and therefore is likely to be confusing. A sane example would have been done in one of the two following ways

    #ifdef _DEBUG
    #define PRINT printf_s
    #else
    #define PRINT __noop
    #endif

    or

    #ifdef MY_INTERNAL_TRACING
    #define PRINT printf_s
    #else
    #define PRINT __noop
    #endif

    By selecting a name close to the official name _DEBUG, the programmer reading this might likely become very confused. It is not obvious that /DDEBUG actually sets the symbol to 1; and in any case, it is more common to use #ifdef for conditional compilation.

    NTDDI_VERSION

    The discussion of this macro is essentially incoherent. Also, why do we see internal (and now obsolete) names like "LONGHORN"? We ended up dealing with the godawful name "CHICAGO" for years. These internal names should never escape the development process. I realize this requires marketing to be proactive (read "having a shred of intelligence and more than two functioning neurons shared among the whole team") but we should not be seeing these artifacts of the development process appearing in released products.

    When would I use this macro? When would I see it? Obviously, the header files themselves, that should be using these macros, do not, so why should we care?

    OEMIoControl (Windows CE)

    There is an error in the documentation; the KernelIoControl has the same error

    The documentation says

    [in] Number of bytes returned in lpOutBuf

    The correct documentation should say

    [out] A pointer to a DWORD value. NULL is not permitted for this parameter. Upon completion of the operation, this will contain the number of bytes returned in lpOutBuf.

    A Wave of the Flounder Fin to Reiß. who submitted this error.

    OLE Interfaces

    When inserting an OLE object into a project, a collection of header files is created; for example, when inserting a PowerPoint presentation interface, files like CPresentation.h are generated.

    Erroneously, these do not have either classic "include guards" nor #pragma once declarations, which makes their inclusion in real projects a pain; the solution is to hand-edit into the files the #pragma once directive.

    It would have been easier if the tools had done the job right.

    ON_CONTROL_REFLECT_EX

    For reasons that make no sense whatsoever, assuming that anyone ever thought that there were reasons, which I doubt...

    The macros should get first-class documentation, meaning that each of the macros is documented on a fully-self-contained page (which should reference TN062). I should not have to read lots of text to figure out where the critical documentation that I need is found.

    Due to a complete brain failure of the designers of Visual Studio, this is not one of the options that can be selected. Instead of enhancing the capabilities of VS6, VS.NET merely destroyed the usability of the interface without actually adding any value, such as allowing ON_CONTROL_REFLECT_EX message handlers to be added.

    ON_CONTROL_REFLECT_EX is not documented except by reading rather tedious text in TN062, and the critical piece of information, the return type information, is hidden in the text. The correct documentation is


    ON_CONTROL_REFLECT_EX(notification_code, fn)

    notification code

    A notification code valid for the control. For example, if this is in a subclass of CEdit, a valid notification code might be EN_CHANGE.

    fn

    A function to be called. The function has the prototype

    BOOL fn();

    The function returns FALSE to allow the parent to also process the notification, and TRUE to indicate the notification has been entirely processed within this handler and should not be seen by the parent.

    Remarks

    This macro is used in a message map in a subclassed control that handles WM_COMMAND messages. It allows the control to perform actions and then pass the message to the parent window, making the handler transparent to the parent. This is accomplished by return FALSE. Otherwise, the handler does whatever it needs to do, and returns TRUE, and the parent never receives the notification.


    ON_MESSAGE 

    This documentation erroneously states that user messages are "usually defined in the range WM_USER to 0x7FFF". This is not true. The correct documentation is

    "User defined messages are defined in the range of WM_USER to 0x7FFF and WM_APP to 0xBFFF. The ON_MESSAGE macro is used to handle any user-defined message in the range of WM_USER to 0xBFFF, and is also required for standard Windows WM_ messages that are not supported by the Visual Studio Message Properties window."

    ON_WM_NCHITTEST 

    In an very unfortunate misfeature, the return type of CWnd::OnNcHitTest was changed from a UINT to a LRESULT. This means that code is neither upward nor downward compatible between VS2008 and earlier versions. There is no indication that his has happened, other than the fact that code fails to compile. This manifests itself as an error as shown below. See CWnd::OnNcHitTest.

    è      ON_WM_NCHITTEST()
    
    1>filename(ln) : error C2440: 'static_cast' : cannot convert from 'UINT (__thiscall yourclass::* )(CPoint)' to 'LRESULT (__thiscall CWnd::* )(CPoint)'
    1> Cast from base to derived requires dynamic_cast or static_cast

    OPATTR (MASM)

    This says, in its entirety,

    Returns a word defining the mode and scope of expression. The low byte is identical to the byte returned by .TYPE. The high byte contains additional information.

    However, it appears that this "additional information" is considered a State Secret, since if were important, it would be documented on this page. As far as I can tell, it is not documented anywhere. And go follow the link to .TYPE to see how informative that is!

    OpenClipboard 

    Although the documentation suggests that using GetLastError is supposed to provide additional information, it has been observed by one reader that under some conditions where OpenClipboard returns FALSE, GetLastError returns 0. This is either a bug in the kernel or a bug in the documentation.

    %OUT 

    The documentation of %OUT says it is the same as ECHO, and the documentation of ECHO says it is the same as %OUT. Recursive: see Recursive.

    Unfortunately, the documentation is also confusing. The syntax for ECHO is

    ECHO message

    but the syntax for %OUT is

    %OUT

    That is, it has no message parameter. Either they are synonyms, or they are different, and if they are the same, they would have the same syntax. Doesn't anyone actually read this documentation to see if it makes sense?

    PagePaintHook Function

    The documentation for this function was first reported by me as erroneous in 1997 (see, for example, page 1019 of Win32 Programming), and was reported to the documentation group at that time. Twelve years later, the same erroneous documentation still exists.

    The function is specified as

    UINT_PTR CALLBACK PagePaintHook(HWND hDlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)

    where

    hdlg
    [in] Handle to the Page Setup dialog box

    This is completely erroneous. The correct definition would be

    UINT_PTR CALLBACK PagePaintHook(HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)

    where

    hwnd
    [in] Handle to the static control in which the page image is drawn.

    It is astonishing that an error this grotesque still remains.

    Note that the messages WM_PSD_PAGESETUPDLG, WM_PSD_FULLPAGERECT and WM_PSD_MINMARGINRECT do not have hyperlinks in the page (apparently because they have hyperlinks elsewhere on the page; standard rant of stupid and irresponsible documentation guidelines designed by unemployed English majors who have nothing else to do with their time applies). There is no excuse to not hyperlink every instance of a term, especially when they are in a table.

    PAGESETUPDLG

    This does not have a hyperlink to the PagePrintHook function in the description of lpfnPagePrintHook. This is probably due to the stupid guideline, promulgated by amateurs with too much time on their hands and no experience whatsoever with hyperlinked documents, that there Shall Be Only One, that is, no more than one hyperlink to the same page on a page. This is irresponsible. The hyperlink can only be found in the description of the PSD_DISABLEPAGEPAINTING flag, a place that no sane person would think to look for it.

    PALETTEENTRY

    This erroneously states the value of peFlags can be NULL. This makes no sense. NULL is a pointer value, and it would be assigned to a BYTE type. The correct documentation is that the peFlags can be 0.

    PASCAL

    Yes, it is documented that this is obsolete. But it continues to appear in any number of Microsoft header files and is erroneously used in other parts of the documentation as if it still had meaning. It should be consistently replaced, as the documentation of the "Pascal calling convention" topic suggests, by WINAPI, in all other instances of the documentation. Microsoft has failed to actually delete it from all other documentation and from the header files and replace it as their own documentation says should be done.

    Given it has been obsolete since the release of Windows NT 3.1, something like 15 years ago, there is no excuse for it to still exist in any documentation file (except the one saying it is obsolete) or header file. See also FAR.

    PostMessage

    There is no hyperlink to UIPI at the point where that term is used.

    There is no documentation of what happens if more messages than the message limit are posted, for example, one might presume it returns FALSE but what does GetLastError return in this case?

    This description states that marshalling is only done for messages messages in the range of 0 to WM_USER - 1, but there is no hyperlink on the word "marshalling" nor is there any text here explaining what this term means.

    See also SendMessage, PostThreadMessage.

    PostThreadMessage

    There is no hyperlink to UIPI at the point where that term is used.

    This description states that marshalling is only done for messages messages in the range of 0 to WM_USER - 1, but there is no hyperlink on the word "marshalling" nor is there any text here explaining what this term means.

    There is no documentation of what happens if more messages than the message limit are posted, for example, one might presume it returns FALSE but what does GetLastError return in this case?

    The recommended practices for determining if a queue has been created miss a completely non-blocking mechanism, described below.

    See also SendMessage, PostMessage.


    Generally, when creating a thread that is intended to receive messages, there should be no attempt to post messages to the thread until the thread queue has been created. One way of doing this is to break the syntactic sequentiality of the thread creation and the message posting, and not post the message until the thread asynchronously posts a message back to the creating thread. This is particularly effective in working with the main GUI thread, which ideally should not be blocked. This is accomplished by having the secondary thread create the message queue and send a user-defined message to the main thread, then responding to that user-defined message with a PostThreadMessage call. For example, if we have

    #define WMU_THREAD_STARTED (WM_APP+something)

    then the code which is intended to be of the form

    ...create the thread
    PostThreadMessage(...);
    ...do other stuff
    return;

    does not require arbitrary blocking to handle the PostThreadMessage, but instead is written as

    ...create the thread
    ...do other stuff
    return;
    LRESULT wndproc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
        {
          ...
          if(msg == WMU_THREAD_STARTED)
             {
               PostThreadMessage(...);
               return 0;
             }

    and the thread body is of the form

    ...
    PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);
    PostMessage(hwnd, WMU_THREAD_STARTED, 0, 0);

    The hwnd is typically passed into the thread via the thread function parameter.


    Note that the example given, the WM_USER value is used for the low and high limits of the PeekMessage; there appears to be no justification for the use of this specific value, when 0, 0 would work just as well and not have any "baggage" to suggest that there is any significance whatsoever to the values of these two parameters.

    #pragma optimize 

    The #pragma optimize says that the option list is the set of options specified by the /O command line switch. It then describes a "p" option that says "improve floating point consistency" without saying what this means. Of course, the natural reaction is to go to the command line options and look at /O, but there is no "/Op" optimization option, making it hard to figure out what is really going on here.

    #pragma push_macro

    The documentation of this is nearly incoherent, and the example even more so. The reference to having to read the incredibly incoherent example under #pragma pop_macro is even stranger. The annotation that this is compiled with /W1 is nonsensical; an example that doesn't compile under /W4 should not be written at all, and as far as I can tell, there is no rationale for using /W1 at all, and the comment makes no sense.

    The documentation should read:

    Allows a macro to be temporarily redefined. The #pragma push_macro("name") saves the definition of the macro whose name is specified on a stack of definitions for that macro. This allows a new #define to be made for that macro, which replaces the current definition. It also allows the existing macro to be undefined within the scope of the push_macro, so that it can be defined with a new body but not generate a warning about a conflict.

    For example,

    #define X 1
    #define X 2
    _tprintf(_T("X is %d\n"), X);

    The problem is that if the definitions appear in different header files, the second definition simply replaces the first. It does issue a warning message that this has been done, and references the site of the first definition.

    #define X 1
    #pragma push_macro("X")
    #define X 2
    _tprintf(_T("X is %d\n"), X);
    #pragma pop_macro("X")
    _tprintf(_T("X is %d\n"), X);

    Note that since this still redefines the existing macro X, it will generate a warning C4005,

    filename.cpp(21) : warning C4005: 'X' : macro redefinition
           filename.cpp(19) : see previous definition of 'X'

    To avoid a "macro redefinition" warning, providing that this was your intent, you will need to undefine the macro. as shown below

    #define X 1
    #pragma push_macro("X") //--------------------------+
    #undef X                                          //|
    #define X 2                                       //|
    _tprintf(_T("X is %d\n"), X);                     //|
    #pragma pop_macro("X") //---------------------------+
    _tprintf(_T("X is %d\n"), X);
    

    The latter two examples produce the output shown below

    Note that if the macro is not already defined, this #pragma still does reasonable things; in this case the corresponding #pragma pop_macro restores the state to "undefined"

    // Y is not a defined macro name at this point
    #pragma push_macro("Y") //-------------------------+
    #define Y 1                                      //|
    #ifdef Y                                         //|
    #pragma message("Y is defined\n")                //|
    #else                                            //|
    #pragma message("Y is undefined\n")              //|
    #endif                                           //|
    #pragma pop_macro("Y") //--------------------------+
    
    #ifdef Y
    #pragma message("Y is defined\n")
    #else
    #pragma message("Y is undefined\n")
    #endif 

    This will generate, during compilation, the following output:

    ------ Build started: Project: test_push_macro, Configuration: Debug Win32 ------
    
    Compiling...
    test_push_macro.cpp
    Y is defined
    Y is undefined
    Linking...
     

    PROTO (MASM)

    The documentation gives this as:

    label PROTO [[distance]] [[langtype]] [[, [[parameter]]:tag]]...

    Prototypes a function

    Now, surprisingly, most of us would like to know what the distance parameter means, what values are permitted for langtype, and what the parameter specification is. Note that for LOCAL, the thing which follows the colon and describes the local variable type is called a type whereas the similar, and probably identically functional, component of PROTO is called a tag. Are they the same, or do they mean something different? If so, what do they mean, and why are they different? If they are not different, why are two different notations used? Note that a hyperlink to the syntax of langtype and/or tag might be useful. But then again, professional documentation would be useful, also. I'm sure this directive is meaningful, perhaps even useful, but I did not realize it is protected by the Official Secrets Act.

    PUBLIC (MASM)

    The syntax is

    PUBLIC [[langtype]] name [[, [[langtype]] name]]...

    but there is no hyperlink to explain what langtype is!

    QueryDosDevice

    First, note that there is a documented bug in this API dealing with MBCS input; see http://support.microsoft.com/kb/931305.

    However, there is an inconsistency in the documentation. The Return Values section says

    If the function succeeds, the return value is the number of TCHARs stored into the buffer pointed to by lpTargetPath.

    If the function fails, the return value is zero. To get extended error information, call GetLastError.

    If the buffer is too small, the function fails, and the last error code is ERROR_INSUFFICIENT_BUFFER.

    Windows 2000/NT: If the buffer is too small, the return value indicates how many characters were stored.

    What is incomprehensible here is the last qualification for Windows 2000/NT. This is inconsistent with the line above it. If the buffer is too small, it fails, and this is indicated by a value of 0. But this line says that if it fails, the value is non-zero and indicates how many characters were stored! It can't be both. It can't both succeed (returning a nonzero value) and fail (returning a zero value) but for failure return return a nonzero value.

    See also my QueryDosDevice Explorer.

    QWORD (MASM) 

    Does not contain a hyperlink to SQWORD. Does not contain a hyperlink to DQ.

    It does not mention that the allowable integer range is -9223372036854775808..18446744073705551615.

    ReadProcessMemory 

    Does not contain a hyperlink to VirtualAllocEx.

    RegGetValue 

    There is a rumor that this API will call ExpandEnvironmentStrings for a REG_EXPAND_SZ value, but nothing in the documentation even hints that this happens.

    ReleaseSRWLockExclusive 

    Does not state conditions such as "the lock must have been acquired by AcquireSRWLockExclusive. If the lock was acquired by AcqureSRWLockShared, the behavior is undefined". Does not state if there is an order to which thread runs next (should explicitly state that the order is undefined, if this is so).

    According to the MSDN Magazine article I found, if this API is called and the lock is not owned by the thread, it throws an SEH exception STATUS_RESOURCE_NOT_OWNED. Of course, this is so blindingly obvious from the documentation that it didn't bother to mention it! Note also that the MSDN article does not state if this happens if the thread actually has the lock but not in the mode specified by the release.

    ReleaseSRWLockShared 

    Does not state conditions such as "the lock must have been acquired by AcquireSRWLockShared. If the lock was acquired by AcqureSRWLockExclusive, the behavior is undefined". Does not state if there is an order to which thread runs next (should explicitly state that the order is undefined, if this is so).

    According to the MSDN Magazine article I found, if this API is called and the lock is not owned by the thread, it throws an SEH exception STATUS_RESOURCE_NOT_OWNED. Of course, this is so blindingly obvious from the documentation that it didn't bother to mention it! Note also that the MSDN article does not state if this happens if the thread actually has the lock but not in the mode specified by the release.

    RealGetWindowClass 

    There seems to be massive confusion in the documentation about the purpose of this API, and how it relates to GetClassName. See the discussion under GetClassName.

    Note that this API does not contain a hyperlink referring to GetClassName or explain in any way why it is different from GetClassName. In checking various Web postings, mostly in VB forums, it appears that it "does not work" (whatever that means) for common controls in themed apps (whatever that means). The definition of "does not work" is apparently left to the imagination. What is completely unclear is why this API exists at all, or what value it has over the long-standing GetClassName. Nothing in its documentation suggests why it is in any way different from GetClassName. One complex explanation seemed to suggest that it returned the "real" class name, but how a "real" class name differs from some fictitious, virtual, or fake class name is apparently left to the imagination of the reader of that post. Nothing in the documentation suggests why this exists or how it differs in any way from GetClassName.

    Inquiring Minds Want To Know.

    RegisterCallback 

    This documentation has the same set of colossal blunders as the AllocateHeap and FreeHeap "functions". The correct documentation is

    The RegisterCallback is a function pointed to by the SECPKG_DLL_FUNCTIONS structure, and is used to register user-mode callback functions for a user-mode security package.

    The definition is

    typedef NTSTATUS (NTAPI LSA_REGISTER_CALLBACK)(ULONG CallbackId, PLSA_CALLBACK_FUNCTION Callback);

    Note that the critical NTAPI calling convention is omitted from the definition given in the MSDN. Note that the critical information about the calling convention is omitted from the specification of the PLSA_CALLBACK_FUNCTION.

    RegisterDeviceNotification

    NotificationFilter

    A pointer to a lbock of data that specifies the type of device for which ntifications should be sent. This block always begins with the DEV_BROADCASE_HDR structure. The data following the header is dependent on the value of the dbch_devicetype member, which can be DBT_DEVTYP_DEVICEINTERFACE or DBT_DEVTYP_HANDLE. For more information, see Remarks.

    There is nothing  about this in the Remarks section.

    There are no hyperlinks to DEV_BROADCAST_HANDLE, DEV_BROADCAST_DEVICEINTERFACE, or any other DEV_BROADCAST structure. Why? There is no discussion about when these would be used, or why. The documentation is truly abysmal.

    There is a hyperlink to a really poorly-done example. For example, the code declares a variable of type DEV_BROADCAST_DEVICEINTERFACE, which most certainly does not begin with a DEV_BROADCAST_HDR, although the names of the members are similar. Who could possibly have designed such a bizarre way of dealing with structures? I have no idea.

    The example uses a global HWND variable which is not initialized anywhere in the example, since it clearly as a _tmain function. The code in _tmain simply calls the registration function without initializing the poorly-conceived global variable, does a printf (which makes no sense in a GUI-based app, which this clearly is intended to be, because it has an HWND variable, but it is written as a console app), then it exits, without unregistering the notification, and allowing no time whatsoever for the notification to happen. And, having written an app which has a subroutine based on this code, it actually doesn't work!

    It is hard to imagine a more poorly-constructed example. A program which does nothing, claims to use a window, and is a console app! It is inexplicable how such bad examples exist. However, it seems that no matter how bad you can imagine an example to be, there are even more beyond imagination than this one! (The multithreaded socket example is far worse!)

    RegisterShellHookWindow

    The names of two of the constants are misspelled

    HSHELL_ACTIVATED is erroneously spelled HSHELL_ACTIVATEED
    HSHELL_RUDEAPPACTIVATED is erroneously spelled HSHELL_RUDEAPPACTIVATEED

    The documentation for HSHELL_LANGUAGE, HSHELL_ACCESSIBILITYSTATE and HSHELL_SYSMENU is missing.

    It is also worth pointing out that in intelligently-arranged documentation the list of these HSHELL_ names would be in alphabetical order.

    RegisterWindowMessage

    It is not documented how to obtain the string of the Registered message. This is done by using ::GetClipboardFormatName. This should be explicitly documented in the MSDN.

    (thanks to Giovanni Dicanio for this tip!)

    Note that Registered Window Messages are unique only within desktops, and cannot be sent between desktops using BroadcastSystemMessage. Therefore, a service cannot call RegisterWindowMessage and expect that it will be the same value as an app running in any other desktop session.

    RpcNsBindingLookupBegin 

    This suffers badly from terminal hyperlink failure.

    For example, there is no specification of the values that can appear in the EntryNameSyntax parameter other than RPC_C_NS_SYNTAX_DEFAULT. Nor is there any hyperlink that goes to such a specification.

    There is no detailed specification of the EntryName parameter other than a vague handwave about some Registry key. It is also unclear why it must be an 'unsigned char *' and not, as might be expected, a 'const unsigned char *' or as we might expect in 2009, an LPCTSTR. Is RPC limited to 8-bit names? Checking the header file demonstrates that the documentation is, in fact, erroneous. The correct documentation is:

    RPC_STATUS RPC_ENTRY rpcNsBindingLookupBeginA(
        IN unsigned long EntryNameSyntax,
        IN unsigned char * EntryName,
        IN RPC_IF_HANDLE IfSpec,
        IN UUID * ObjUuid,
        IN unsigned long BindingMaxCount,
        OUT RPC_NS_HANDLE * LookupContext);
    
    RPC_STATUS RPC_ENTRY rpcNsBindingLookupBeginW(
        IN unsigned long EntryNameSyntax,
        IN unsigned short * EntryName,
        IN RPC_IF_HANDLE IfSpec,
        IN UUID * ObjUuid,
        IN unsigned long BindingMaxCount,
        OUT RPC_NS_HANDLE * LookupContext);

    which is at least correct, but raises the question of how anyone could be so stupid as to require unsigned characters for a string, when the sensible definition would have used LPCTSTR and specified the parameter as a const. There is no natural datatype that encodes strings of unsigned characters. Nor, in this case, does it make the slightest sense to have imposed this ridiculous data type on the user.

    In any universe inhabited by intelligent programmers, the definition would have been

    RPC_STATUS RPC_ENTRY rpcNsBindingLookupBegin(
        IN unsigned long EntryNameSyntax,
        IN LPCTSTR EntryName,
        IN RPC_IF_HANDLE IfSpec,
        IN UUID * ObjUuid,
        IN unsigned long BindingMaxCount,
        OUT RPC_NS_HANDLE * LookupContext);

    There is no specification of the RPC_IF_HANDLE structure. Nor is there a hyperlink to this structure definition. More useless handwaving.

    The discussion of the ObjUuid parameter is meaningless. For example, it says that if the UUID is zero, that "compatible binding handles are returned", but does not indicate how they are returned, or where they will be found. The terms "nonzero UUID", "nil UUID" and "nil object UUID" are used without explanation or definition.

    There is a reference to an RPC_NS_HANDLE but no definition is given. Presumably this is an opaque datatype to be used in subsequent calls, but this is not actually made clear.

    RpcServerRegisterIf 

    Another example of documentation that is totally out of touch with any form of reality that exists.

    The function is documented as returning void but the header explicitly states that it returns an RPC_STATUS value.

    It also has a parameter specified as an RPC_MGR_EPV type, but there is no hyperlink or See Also that refers to this type, or suggests what it should be if it is non-NULL. (An examination of the header file indicates that this is a void type, which does not excuse the failure to document it! We still don't know what the structure of an entry point vector should be!)

    The correct documentation of the prototype would have been (as taken from RpcDce.h) is shown below. Note that it uses obsolete and totally silly concepts like __RPC_FAR which make no sense, and have not made sense since 1988, and have never, ever, under any imaginable circumstances, made sense in Win32. But there was no RPC mechanism in Win16, so the appearance of this noise word is totally inexplicable.

    RPCRTAPI
    RPC_STATUS
    RPC_ENTRY
    RpcServerRegisterIf (
                         IN RPC_IF_HANDLE IfSpec,
                         IN UUID __RPC_FAR * MgrTypeUuid OPTIONAL,
                         IN RPC_MGR_EPV __RPC_FAR * MgrEpv OPTIONAL
    );

    SBYTE (MASM) 

    Does not contain a hyperlink to BYTE. Does not contain a hyperlink to DB.

    Does not mention that the allowable range is -128..255 (thus exceeding the allowable range for a signed byte!)

    scanf_s 

    The documentation for this is erroneous. Although it purports to be documentation of _scanf_s, it is written as if it is documentation of scanf, and therefore the examples are nonsesnical.

    For example, it shows an example

    char s[10];
    scanf("%9s"), s, 10);

    The correct code, if written by someone who actually paid attention to what they were documenting, would have been

    char s[10];
    scanf_s("%9s"), s, _countof(s));

    Somehow, documenting scanf_s with an erroneous function scanf, makes no sense.

    The same error is made in the page titled scanf Width Specification, where the example is show as

    char str[21];
    scanf("%20s", str, 21);

    The correct documentation would have been

    char str[21];
    scanf_s("%20s", str, _countof(str));

    An even better example would be

    char str[21];
    scanf_s("%*s", _countof(str) - 1, str, _countof(str));

    It is not at all clear why accurate and precise examples are not used; instead, examples which teach against Best Practice seem to be the choice.

    SDWORD (MASM)

    Contains no hyperlink to DWORD. Does not contain a hyperlink to DD.

    Does not state that the allowable range is -2147483648..4294967295. Does not indicate if floating-point initializers are valid.

    SECURITY_IMPERSONATION_LEVEL 

    This contains incoherent documentation. Consider

    SecurityAnonymous

    The server process cannot obtain identification information about the client, and it cannot impersonate the client. It is defined with no value given, and thus, by ANSI C rules, defaults to a value of 0.

    I have no idea what this blather means. There is a substantial difference between "no value given" (which is pretty unclear as stated) and having a value of 0. I have no idea why ANSI C rules even come into the discussion. The entire sentence describing this is completely meaningless noise! Note also that one of the places this appears is in the CreateToken API, where it is the fourth parameter, so the nonsensical statement is even less relevant in this context!

    A  correct description would be

    The server process cannot obtain identification information about the client, and it cannot impersonate the client. This is defined as the numerical value of 0, so if a structure using this type of value is zeroed before use, and the member of this type is not set to any other value, this is the interpretation of the field.

    SelectClipRgn

    The documentation is misleading and confusing. It says

    The function assumes that the coordinates for the given region are specified in device units.

    But this is not correct. The phrase "device units" suggests that the distance units are in units of MM_TEXT. But it also means the origin and x,y distances will use the coordinate system of MM_TEXT.  The correct statement is

    The function uses a region that is specified in device units, in the device coordinate space; that is, any transformations applied via SetWorldTransform or other transform-matrix operations, or which use mapping modes other than MM_TEXT, are ignored.

    SendMessage

    This states that under Vista UIPI, a thread can only send messages to the message queues of processes of equal or lesser priority. It does not state what happens if this is attempted. Does SendMessage always return 0 under such conditions? If I get a 0 and I'm expecting a 0, and the API was successful, does it change the ::GetLastError value? A suggested "good practice" in the case of an expected cross-process SendMessage might be

    SetLastError(ERROR_SUCCESS);
    LRESULT result = SendMessage(hwndOfOtherProcess, ...);
    if(GetLastError() != ERROR_SUCCESS)
        ... failed
    else
        ... succeeded

    but is this what should be done? Inquiring Minds Wish To Know.

    There is no hyperlink to UIPI at the point where that term is used.

    This description states that marshalling is only done for messages messages in the range of 0 to WM_USER - 1, but there is no hyperlink on the word "marshalling" nor is there any text here explaining what this term means.

    See also PostMessage, PostThreadMessage

    SetCapture

    Once the mouse has been captured, neither WM_NCHITTEST nor WM_SETCURSOR messages will be sent.

    SetCriticalSectionSpinCount 

    Contains vague handwaves about how the storage allocator uses 4000 for a value, but does not suggest what the default value is for a straight InitializeCriticalSection or how the value correlates to processor speed.

    SetGraphicsMode

    There is no method for this in the CDC class; see the description of CDC::SetGraphicsMode.

    In addition to the statement about how graphics output differs, there is another point

    4. The results of GetCharABCWidths will be different from the values returned for GM_COMPATIBLE mode for an otherwise identical DC.

    SetLayout

    There are problems with the documentation and/or header files. The symbol LAYOUT_RTL (which is mentioned in earlier documentation) is not mentioned in later documentation and does not appear in the header file. The symbols LAYOUT_VBH and LAYOUT_BTT are not mentioned in any documentation. If an older symbol is deprecated, it might appear in a gray font, or some other distinctive font, perhaps even a strikeout font, and have a note that it was deprecated and is no longer available.

    SetProcessAffinityMask

    Documentation prior to VS2005 erroneously states that "the value of the process affinity mask must be a proper subset of the mask values obtained by the GetProcessAffinityMask function". I wonder of the author of this had the slightest clue what a "proper subset" is! Under this limitation, it means that once you called SetProcessAffinintyMask, you could only limit the process threads to fewer processors than the processor mask, whereas it is certainly possible to set the process affinity mask to exactly the set of all processors specified by GetProcessAffinityMask, or in fact, the set of all processors in the system, which might be a superset of the current process affinity mask! I believe the use of the phrase "proper subset" is either incorrect because the author of the documentation was clueless about mathematics, and more simply, because the limitation makes no sense whatsoever.

    In VS2005, the word "proper" was deleted, but I believe the documentation is still confusing because there might be readers who think that "subset" means "proper subset" when in fact it does not.

    The correct documentation is

    The process affinity mask must not specify any bits as 1 which are not 1 in the system affinity mask, the value returned in the lpSystemAffinityMask parameter of GetProcessAffinityMask. An attempt to set a process affinity mask that requests more processors than are configured in the system will return a false value, and GetLastError will return ERROR_INVALID_PARAMETER.

    A similar nonsensical statement appears in the description of SetThreadAffinityMask and GetProcessAffinityMask.

    SetTextCharExtra

    The nCharExtra parameter is specified as an int. Presumably it can be negative. This should be mentioned in the (non-existent) Remarks section.

    It says if the function returns, the value is 0x80000000. Why does it not say INT_MIN and require using limits.h to specify the return value. For that matter, why is it specifying an unsigned hex value for what is a signed return value?

    SetScrollInfo

    Although I did not test this, I suspect that setting the range so that the scrollbar disappears will have the same problems as SetScrollRange.

    Note that the documentation is rather obscure about the specification of SIF_DISABLENOSCROLL. It says "Disables the scroll bar instead of removing it, if the scroll bar's new parameters make the scroll bar unnecessary", but it does not actually explain what the criteria might be that would indicate this. Having a scroll range of 0 (not necessarily (0,0) but (nn) for any given value of n) is certainly one reason, but how is the reader supposed to infer this from this description?

    SetScrollRange

    If this function is called from within an OnPaint handler, and it causes the scroll bar to have a net 0 range (e.g., setting it to (0,0)), this will cause the scroll bar to disappear. Unfortunately, when called from the OnPaint hander, it will then cause the uxtheme module to fail with an access fault when it goes to try to paint the now-nonexistent scroll bar. The last call is given as

    The workaround to this is to PostMessage a message to the window using a user-defined message handler, and set the scrollbar range in response to this message.

    SetThreadAffinityMask

    Documentation prior to VS2005 erroneously states "a thread affinity mask must be a proper subset of the process affinity mask for the containing process of a thread". This statement is completely nonsensical (see SetProcessAffinityMask), because it should not be limited to a "proper subset", meaning you could never select a thread to run on all processors available to the process! And dropping the word "proper" still leaves the situation confusing for some readers who might think that "subset" necessarily means "proper subset"..

    The correct documentation is

    The thread affinity mask must not specify any bits as 1 which are not 1 in the current process affinity mask, the value returned in the lpProcessAffinityMask parameter of GetProcessAffinityMask. An attempt to set a thread affinity that selects processors not selected for the process affinity mask will result in an error return, and GetLastError will return ERROR_INVALID_PARAMETER.

    SetThreadName

    This example is weird. It does not explain anything about why it works.

    First, there is a deep question about why the THREADNAME_INFO structure supplied in the example is not part of the Platform SDK. This is clearly a mistake.

    Then, there is a serious question about why the string member is an LPCSTR instead of an LPCTSTR, or the function is called with an LPCSTR parameter instead of an LPCTSTR parameter. It turns out that this is required to be an 8-bit string, but in fact this point should be emphasized. This is because there are already too many places in the MSDN where LPCSTR is used in what are essentially obsolete examples.

    There is no explanation of why the exception code is 0x406D1388, and whether this has any meaning.

    The correct documentation would have an introduction as follows:

    The ability to set the name of a thread in unmanaged code is invoked in a very unusual fashion. An otherwise-undocumented structure must be used. This structure provides the thread name as an 8-bit name, even in a Unicode application. The technique is to raise an exception with a very specific exception ID, and pass it a pointer to this structure. This is all illustrated in the code shown below. The "thread name" is a concept used by the debugger, and it is the debugger that recognizes the unusual exception ID and stores the thread information away for later presentation.

    It is also unclear why the ID used is given as an absolute hex number in the VS2003 documentation but as a #define with an AFX_ prefix (suggesting it is part of the MFC framework, which it clearly is not) in the VS2008 documentation.

    SetupDiGetDeviceInterfaceDetail

    For reasons that make no sense whatsoever, the documentation of this API was not included in the MSDN documentation. This is, of course, incredibly stupid. Almost as stupid as naming the APIs "SetupDi", which makes even less sense, since these are the only ways to access a device via its interface.

    In the online MSDN documentation it tells the versions (badly), gives the header file name, and does not specify the name of the library that must be linked in to resolve the symbol! How, exactly, one is expected to use this with such critical documentation missing is not clear.

    The library required is setupapi.lib. This might be obvious, but there are many libraries whose header files and library files have different names.

    SetWindowText 

    It is undocumented that calling SetWindowText on an MDI child window frame to set its caption will silently truncate the length of the string to 160 characters. There is no known rationale for this restriction, nor is it apparent why it should exist.

    SHBrowseForFolder

    This documentation states that to release the PIDL returned by SHBrowseForFolder, the IMalloc::Free method should be used, and therefore you should call SHGetMalloc to obtain this pointer. If you go to the SHGetMalloc interface description it says

    Not currently supported.

    Remarks

    This function should no longer be used. Use the CoTaskMemFree and CoTaskMemAlloc functions in its place.

    and that's all the documentation that exists! BUT, if you go directly to IMalloc::Free, there is no note saying that "If you are obtaining this interface from SHGetMalloc, note that this protocol has been superseded by the use of CoTaskMemFree" or some other useful indication that you should not be using this interface.

    The examples in SHBrowseForFolder and all other instances where PIDLs are discussed must be updated to reflect this new reality. This includes the general PIDL overview, the discussion of allocating PIDLs, etc.

    Note also the oversights in BROWSEINFO documentation.

    ShellAbout

    Due to a bug in the implementation of ShellAbout, it erroneously tries to write to an LPCTSTR parameter. Therefore, the correct specification, given the bug, should be

    int ShellAbout(
        HWND hWnd,
        LPTSTR szApp,
        LPCTSTR szOtherStuff,
        HICON hIcon);

    The correct solution to this problem would be for Microsoft to fix the code so it works according to the specification.

    [Thanks to David Lowndes for pointing this out in the newsgroup microsoft.public.vc.mfc]

    ShellExecute

    This states, incompletely,

    lpDirectory

    Pointer to a null-terminated string that specifies the default directory.

    This is incomplete. The correct statement is

    Pointer to a null-terminated string that specifies the default directory. If this is NULL, the current working directory of the process that calls ShellExecute will be used.

    There is no See Also reference to ShellExecuteEx.

    ShellExecuteEx

    There is no See Also reference to ShellExecute.

    SHELLEXECUTEINFO

    This documentation is incoherent and confusing. For example,

    lpDirectory

    Address of a null-terminated string that specifies the name of the working directory. If this member is not specified, the current directory is used as the working directory.

    I have no idea what the meaningless term "not specified" means. The  correct documentation is

    Address of a null-terminated string that specifies the name of the working directory. If this member is NULL, the current directory of the calling process is used as the working directory for the new process.

    ShowWindow 

    The documentation states

    The first time an application calls ShowWindow, it should use the WinMain function's nCmdShow parameter as its nCmdShow parameter. Subsequent calls to ShowWindow must use one of the values in the given list, instead of the one specified by the WinMain function's nCmdShow parameter.

    As noted in the discussion of the nCmdShow parameter, the nCmdShow value is ignored in the first call to ShowWindow if the program that launched the application specifies startup information in the structure. In this case, ShowWindow uses the information in the STARTUPINFO structure to show the window. On subsequent calls, the application must call ShowWindow with nCmdShow set to SW_SHOWDEFAULT to use the startup information provided by the program that launched the application. This behavior is designed for the following situations

    Note that, besides the failure to form coherent sentences, there are several other problems with these statements. For example, why should the application specify the nCmdShow parameter the first time if, in fact, whatever parameter is supplied is ignored, and the nCmdShow parameter is used instead? Why is it that SW_SHOWDEFAULT should be used, when there is no specification of what is going to happen if the STARTF_USESHOWWINDOW flag was not set?

    Consider the statement under the description of nCmdShow

    SW_SHOWDEFAULT

    Sets the show state based on the SW_ value specified int he STARTUPINFO structure passed to the CreateProcess function by the program that started the application. Note that this is also implicitly specified

    This should probably read

    SW_SHOWDEFAULT

    The default show value is the value passed as the nCmdShow parameter to WinMain. It can be specified by one of the following means:

    The corrected documentation for the Remarks section should be

    The first time an application calls ShowWindow, whatever parameter supplies will be ignored, and instead, the parameter will be treated as if it was SW_SHOWDEFAULT. See the description under SW_SHOWDEFAULT.  On subsequent calls to ShowWindow, the parameter supplied will be used. To use the nCmdShow parameter passed to WinMain, either the parameter itself must be used, or the SW_SHOWDEFAULT value should be used.

    The rationale of why the parameter is ignored the first time is to allow the nCmdShow parameter to be honored under two common scenarios

    Note that a consequence of this behavior is that an application which creates a window with WS_VISIBLE and later plans to call ShowWindow(SW_HIDE) to hide it, the ShowWindow will use the parameter that would be specified by SW_SHOWDEFAULT. Thus, under such conditions, two calls to ShowWindow would be required to set the desired behavior.

    SleepConditionVariableSRW

    This documentation is remarkably uninformative.

    For example, it appears that it might mean

    SRWLock
    A pointer to the lock. At the time this function is called, the lock must have been acquired with the AcquireSRWLockShared or AcquireSRWLockExclusive API.

    But it is not at all clear what is intended by this function. For example, if I want to block the thread that should wait, why would I know that lock is or is not acquired? And there is no reason I would block the thread that has actually acquired the lock. There appears to be no example of why this function exists or how it is used.

    It does not state what the return value is on a timeout. The expectation is that it will return FALSE and GetLastError will return with something like WAIT_TIMEOUT, but how can we know?

    It does not state that the Flags value can be either 0 or CONDITION_VARIABLE_LOCKMODE_SHARED.

    The documentation is completely unclear as to what is happening. I found an MSDN Magazine article that suggests that if the lock is acquired already, this function "releases" the lock and will not return until there is a timeout or until it can re-acquire the lock under the same condition. I find this whole model completely incomprehensible.

    _splitpath (_wsplitpath, _tsplitpath), _splitpath_s (_wsplitpath_s, _tsplitpath_s)

    The documentation does not state that if you do not care about seeing a particular field, you can use NULL instead of a pointer to a character array. Thus, all of these are valid calls

    _splitpath(path, NULL, NULL, NULL, NULL);   // really boring
    _splitpath(path, NULL, NULL, NULL, ext);    // gets only extension
    _splitpath(path, NULL, NULL, file, NULL);   // gets only file name
    _splitpath(path, drv,  path, NULL, NULL);   // gets only drive and path

    And so on. Note this also extends to _makepath, _wmakepath, _tmakepath, _makepath_s, _wmakepath_s and _tmakepath_s

    Good documentation would say this as part of the parameter specification, e.g.,

    path

    Pointer to a buffer containing the full path name to be split.

    drive

    Pointer to a buffer into which the optional drive letter, followed by a colon (:), will be written. The driver letter may not be present. This parameter may be NULL.

    dir

    Pointer to a buffer into which the optional directory path, including the path delimiters (forward slash, /, or backslash, \) will be written. This parameter may be NULL.

    fname

    Pointer to a buffer into which the base filename (without extension) will be written. This parameter may be NULL.

    ext

    Pointer to a buffer into which the optional file extension, including the leading period (.), will be written. This parameter may be NULL.

    The use of the word "optional" is not only confusing, but it is missing in all places where it would apply.

    In addition, there is no specification of what would happen if the values exceed the limits. Reading the code, however, makes it obvious; the documentation should state (the added text is in italics)

    Each argument is stored ina buffer; the manifest constants _MAX_DRIVE, _MAX_DIR, _MAX_FNAME and _MAX_EXT (defined in STDLIB.H) specify the maximum size necessary for each buffer. The buffers must be at least as long as these constants. If the buffers are shorter, buffer overrun is a potential problem. However, no more than this many characters will be written to each buffer. Note that _MAX_PATH is at least as long as any of the previous symbols.

    It is also worth noting that for Windows programmers, the symbol MAX_PATH is defined to have the same value as _MAX_PATH.

    SQLConfigDataSource

    The documentation is incorrect. It says

    BOOL SQLConfigDataSource(
        HWND hwndParent,
        WORD fRequest, 
        LPCSTR lpszDriver,
        LPCSTR lpszAttributes);

    while the correct documentation is

    BOOL SQLConfigDataSource(
        HWND hwndParent,
        WORD fRequest, 
        LPCTSTR lpszDriver,
        LPCTSTR lpszAttributes);

    The header file defines an API entry point SQLConfigDataSourceW that takes an LPWSTR and defines, when Unicode is selected, SQLConfigDataSource as SQLConfigDataSourceW .

    There is no hyperlink to a list of ODBC functions.

    SQLConfigDriver

    The documentation is incorrect. It says

    BOOL SQLConfigDriver(
        HWND hwndParent,
        WORD fRequest, 
        LPCSTR lpszDriver,
        LPCSTR lpszArgs,
        LPSTR lpszMsg,
        WORD cbMsgMax,
        WORD * pcbMsgOut);

    while the correct documentation is

    BOOL SQLConfigDriver(
        HWND hwndParent,
        WORD fRequest, 
        LPTCSTR lpszDriver,
        LPCTSTR lpszArgs,
        LPTSTR lpszMsg,
        WORD cbMsgMax,
        WORD * pcbMsgOut);

    The header file defines an API entry point SQLConfigDriverW that takes an LPWSTR and defines, when Unicode is selected, SQLConfigDriver as SQLConfigDriverW .

    There is no hyperlink to a list of ODBC functions.

    SQLCreateDataSource

    The documentation is incorrect. It says

    BOOL SQLCreateDataSource(
        HWND hwnd,
        LPSTR lpszDS);

    while the correct documentation is

    BOOL SQLCreateDataSource(
        HWND hwnd,
        LPCTSTR lpszDS);

    Note that the string should correctly be declared as const, which is what is in the header file but the documentation declares it as non-const.

    The header file defines an API entry point SQLCreateDataSourceW that takes an LPCWSTR and defines, when Unicode is selected, SQLCreateDataSource as SQLCreateDataSourceW .

    There is no hyperlink to a list of ODBC functions.

    SQLGetAvailableDrivers

    This function is not documented, but it has both ANSI and Unicode versions.

    SQLGetInstalledDrivers 

    The documentation is incorrect. It says

    BOOL SQLGetInstalledDrivers(
        LPSTR lpszBuf,
        WORD cbBufMax,
        WORD * pcbBufOut);

    but this is erroneous. The correct documentation is

    BOOL SQLGetInstalledDrivers(
        LPTSTR lpszBuf,
        WORD cbBufMax,
        WORD * pcbBufOut);

    The header file actually defines an API entry point SQLGetInstalledDriversW that takes an LPWSTR, and defines, when Unicode is selected, SQLGetInstalledDrivers as SQLGetTranslatorW.

    There is no hyperlink to a list of ODBC functions.

    SQLGetPrivateProfileString

    The documentation is incorrect. It says

    BOOL SQLGetePrivateProfileString(
        LPCSTR lpszSection,
        LPCSTR lpszEntry,
        LPCSTR lpszDefault,
        LPCSTR retBuffer,
        INT cbRetBuffer
        LPCSTR lpszFileName);

    while the correct documentation is

    BOOL SQLGetPrivateProfileString(
        LPCTSTR lpszSection,
        LPCTSTR lpszEntry,
        LPCTSTR lpszDefault,
        LPTSTR retBuffer,
        INT cbRetBuffer,
        LPCTSTR lpszFileName);

    Note that the retBuffer is erroneously specified as a const parameter, but in the header file it is non-const, as it should be.

    The header file defines an API entry point SQLGetPrivateProfileStringW that takes LPCWSTR/LPWSTR parameters, and defines, when Unicode is selected, SQLGetPrivateProfileString as SQLGetPrivateProfileStringW .

    There is no hyperlink to a list of ODBC functions.

    SQLGetTranslator 

    The documentation is incorrect. It says

    BOOL SQLGetTranslator(
        HWND hwndParent,
        LPSTR lpszName,
        WORD dbNameMax, 
        WORD * pcbNameOut,
        LPSTR lpszPath,
        WORD cbPathMax,
        WORD * pcbPathOut,,
        DWORD * pvOption);

    while the correct documentation is

    BOOL SQLGetTranslator(
        HWND hwndParent,
        LPTSTR lpszName,
        WORD dbNameMax, 
        WORD * pcbNameOut,
        LPTSTR lpszPath,
        WORD cbPathMax,
        WORD * pcbPathOut,,
        DWORD * pvOption);

    The header file defines an API entry point SQLGetTranslatorW that takes LPWSTR parameters and defines, when Unicode is selected, SQLGetTranslator as SQLGetTranslatorW.

    There is no hyperlink to a list of ODBC functions.

    SQLInstallDriver 

    The documentation is incorrect. It says

    BOOL SQLGetTranslator(
        HWND hwndParent,
        LPSTR lpszName,
        WORD dbNameMax, 
        WORD * pcbNameOut,
        LPSTR lpszPath,
        WORD cbPathMax,
        WORD * pcbPathOut,,
        DWORD * pvOption);

    while the correct documentation is

    BOOL SQLGetTranslator(
        HWND hwndParent,
        LPCTSTR lpszName,
        WORD dbNameMax, 
        WORD * pcbNameOut,
        LPTSTR lpszPath,
        WORD cbPathMax,
        WORD * pcbPathOut,,
        DWORD * pvOption);

    The header file defines an API entry point SQLInstallDriver that takes LPCWSTR/LPWSTR parameters and defines, when Unicode is selected, SQLInstallDriver as SQLInstallDriverW.

    There is no hyperlink to a list of ODBC functions.

    SQLInstallDriverEx

    The documentation is incorrect. It says

    BOOL SQLInstallDriverEx(
        LPCSTR lpszDriver,
        LPCSTR lpszPathIn,
        LPSTR lpszPathOut,
        WORD cbPathOutMax
        WORD * pcbPathOut
        WORD fRequest,
        LPDWORD lpdwUsageCount);

    while the correct documentation is

    BOOL SQLInstallDriverEx(
        LPCTSTR lpszDriver,
        LPCTSTR lpszPathIn,
        LPTSTR lpszPathOut,
        WORD cbPathOutMax
        WORD * pcbPathOut
        WORD fRequest,
        LPDWORD lpdwUsageCount);    

    The header file defines an API entry point SQLInstallDriverExW that takes LPCWSTR/LPWSTR parameters and defines, when Unicode is selected, SQLInstallDriverEx as SQLInstallDriverExW.

    There is no hyperlink to a list of ODBC functions.

    SQLInstallDriverManager

    The documentation is incorrect. It says

    BOOL SQLInstallDriverManager(
        LPSTR lpszPath,
        WORD cbPathMax
        WORD * pcbPathOut);

    while the correct documentation is

    BOOL SQLInstallDriverEx(
        LPTSTR lpszPath,
        WORD cbPathMax
        WORD * pcbPathOut);

    The header file defines an API entry point SQLInstallDriverManagerW that takes an LPWSTR and defines, when Unicode is selected, SQLInstallDriverManager as SQLInstallDriverManagerW.

    There is no hyperlink to a list of ODBC functions.

    SQLInstallerError

    The documentation is incorrect. It says

    BOOL SQLInstallerError(
        WORD iError
        DWORD * pfErrorCode,
        LPSTR lpszErrorMsg,
        WORD cbErrorMsgMax
        WORD * pcbErrorMsg);

    while the correct documentation is

    BOOL SQLInstallerError(
        WORD iError
        DWORD * pfErrorCode,
        LPTSTR lpszErrorMsg,
        WORD cbErrorMsgMax
        WORD * pcbErrorMsg);

    The header file defines an API entry point SQLInstallerErrorW that takes an LPWSTR and defines, when Unicode is selected, SQLInstallerError as SQLInstallerErrorW.

    There is no hyperlink to a list of ODBC functions.

    SQLInstallODBC

    This is undocumented but both the ANSI and Unicode versions exist.

    SQLInstallTranslator

    Although deprecated, the header file defines an API entry point SQLInstallTranslatorW that takes an LPWSTR and defines, when Unicode is selected, SQLInstallTranslator as SQLInstallTranslatorW.

    There is no hyperlink to a list of ODBC functions.

    SQLInstallTranslatorEx 

    The documentation is incorrect. It says

    BOOL SQLInstallTranslatorEx(
        LPCSTR lpszTranslator,
        LPCSTR lpszPathIn,
        LPSTR lpszPathOut,
        WORD cbPathOutMax
        WORD * pcbPathOut
        WORD fRequest,
        LPDWORD lpdwUsageCount);

    while the correct documentation is

    BOOL SQLInstallTranslatorEx(
        LPCTSTR lpszTranslator,
        LPCTSTR lpszPathIn,
        LPTSTR lpszPathOut,
        WORD cbPathOutMax
        WORD * pcbPathOut
        WORD fRequest,
        LPDWORD lpdwUsageCount);    

    The header file defines an API entry point SQLInstallTranslatorW that takes LPCWSTR/LPWSTR parameters and defines, when Unicode is selected, SQLInstallTranslatorEx as SQLInstallTranslatorExW.

    There is no hyperlink to a list of ODBC functions.

    SQLPostInstallerError

    The documentation is incorrect. It says

    RETCODE SQLPostInstallerError(
        DWORD fErrorCode,
        LPSTR szErrorMsg);

    while the correct documentation is

    RETCODE SQLPostInstallerError(
        DWORD fErrorCode,
        LPTSTR szErrorMsg);

    The header file defines an API entry point SQLPostInstallerErrorW that takes an LPWSTR and defines, when Unicode is selected, SQLPostInstallerError as SQLPostInstallerErrorW.

    There is no hyperlink to a list of ODBC functions.

    SQLReadFileDSN

    The documentation is incorrect. It says

    BOOL SQLReadFileDSN(
        LPCSTR lpszFileName,
        LPCSTR lpszAppName,
        LPCSTR lpszKeyName,
        LPSTR lpszString,
        WORD cbString,
        WORD * pcbString);

    while the correct documentation is

    BOOL SQLInstallTranslatorEx(
        LPCTSTR lpszFileName,
        LPCTSTR lpszAppName,
        LPCTSTR lpszKeyName,
        LPTSTR lpszString,
        WORD cbString,
        WORD * pcbString););    

    The header file defines an API entry point SQLInstallTranslatorW that takes LPCWSTR/LPWSTR parameters and defines, when Unicode is selected, SQLInstallTranslatorEx as SQLInstallTranslatorExW.

    There is no hyperlink to a list of ODBC functions.

    SQLRemoveDriver

    The documentation is incorrect. It says

    BOOL SQLRemoveDriver(
        LPCSTR lpszDriver,
        BOOL fRemoveDSN,
        LPWORD lpdwUsageCount);

    while the correct documentation is

    BOOL SQLRemoveDriver(
        LPCTSTR lpszDriver,
        BOOL fRemoveDSN,
        LPWORD lpdwUsageCount);

    The header file defines an API entry point SQLRemoveDriverW that takes an LPCWSTR and defines, when Unicode is selected, SQLRemoveDriver as SQLRemoveDriverW.

    There is no hyperlink to a list of ODBC functions.

    SQLRemoveDSNFromIni

    The documentation is incorrect. It says

    BOOL SQLRemoveDSNFromIni(
        LPCSTR lpszDSN);

    while the correct documentation is

    BOOL SQLRemoveDSNFromIni(
        LPCTSTR lpszDSN);

    The header file defines an API entry point SQLRemoveDSNFromIniW that takes an LPCWSTR and defines, when Unicode is selected, SQLRemoveDSNFromIni as SQLRemoveDSNFromIniW.

    There is no hyperlink to a list of ODBC functions.

    SQLRemoveTranslator

    The documentation is incorrect. It says

    BOOL SQLRemoveTranslator(
        LPCSTR lpszTranslator,
        LPWORD lpdwUsageCount);

    while the correct documentation is

    BOOL SQLRemoveTranslator(
        LPCTSTR lpszTranslator,
        LPWORD lpdwUsageCount);

    The header file defines an API entry point SQLRemoveTranslatorW that takes an LPCWSTR and defines, when Unicode is selected, SQLRemoveTranslator as SQLRemoveTranslatorW .

    There is no hyperlink to a list of ODBC functions.

    SQLValidDSN 

    The documentation is incorrect. It says

    BOOL SQLValidDSN(
        LPCSTR lpszDSN);

    while the correct documentation is

    BOOL SQLValidDSN(
        LPCTSTR lpszDSN);

    The header file defines an API entry point SQLValidDSNW that takes an LPCWSTR and defines, when Unicode is selected, SQLValidDSN as SQLValidDSNW .

    There is no hyperlink to a list of ODBC functions.

    SQLWriteDSNToIni 

    The documentation is incorrect. It says

    BOOL SQLWriteDSNToIni(
        LPCSTR lpszDSN,
        LPCSTR lpszDriver);

    while the correct documentation is

    BOOL SQLWriteDSNToIni(
        LPCTSTR lpszDSN,
        LPCTSTR lpszDriver);

    The header file defines an API entry point SQLWriteDSNToIniW that takes an LPCWSTR and defines, when Unicode is selected, SQLWriteDSNToIni as SQLWriteDSNToIniW .

    There is no hyperlink to a list of ODBC functions.

    SQLWriteFileDSN

    The documentation is incorrect. It says

    BOOL SQLWriteFileDSN(
        LPCSTR lpszFileName,
        LPCSTR lpszAppName,
        LPCSTR lpszKeyName,
        LPCSTR lpszString);

    while the correct documentation is

    BOOL SQLWriteFileDSN(
        LPCTSTR lpszFileName,
        LPCTSTR lpszAppName,
        LPCTSTR lpszKeyName,
        LPCTSTR lpszString);

    The header file defines an API entry point SQLWriteFileDSNW that takes an LPCWSTR parameters and defines, when Unicode is selected, SQLWriteFileDSN as SQLWriteFileDSNW .

    There is no hyperlink to a list of ODBC functions.

    SQLWritePrivateProfileString

    The documentation is incorrect. It says

    BOOL SQLWritePrivateProfileString(
        LPCSTR lpszSection,
        LPCSTR lpszEntry,
        LPCSTR lpszString,
        LPCSTR lpszFileName);

    while the correct documentation is

    BOOL SQLWritePrivateProfileString(
        LPCTSTR lpszSection,
        LPCTSTR lpszEntry,
        LPCTSTR lpszString,
        LPCTSTR lpszFileName);

    The header file defines an API entry point SQLWritePrivateProfileStringW that takes LPCWSTR parameters and defines, when Unicode is selected, SQLWritePrivateProfileString as SQLWritePrivateProfileStringW .

    There is no hyperlink to a list of ODBC functions.

    SQWORD (MASM)

    Contains no hyperlink to QWORD. Does not contain a hyperlink to DQ.

    Although SWORD assigns a signed value according to its documentation, and you might expect that an SQWORD would be a signed QWORD, the documentation apparently erroneously states that it initializes 8 unsigned bytes of storage for each initializer.

    STARTUPINFO, STARTUPINFOEX 

    The meaning of the STARTUPINFO.wShowWindow field is inadequate. It says

    wShowWindow

    If the dwFlags specifies STARTF_USESHOWWINDOW, this member can be any of the SW_ constants defined in winuser.h. Otherwise, this member is ignored.

    For GUI processes, wShowWindow specifies the default value the first time ShowWindow is called. The nCmdShow parameter of ShowWindow is ignored. In subsequent calls to ShowWindow, the wShowWindow member is used if the nCmdShow parameter of ShowWindow is SW_SHOWDEFAULT.

    It is not clear why winuser.h is even mentioned here. The statement is not actually correct, anyway, because the value SW_SHOWDEFAULT is not actually usable in this context. The corrected documentation should read

    wShowWindow

    If the dwFlags specifies the STARTF_USESHOWWINDOW flag, this member can be any value that can supplied to ShowWindow, except for SW_SHOWDEFAULT. Otherwise, this member is ignored. This parameter is supplied as the nCmdShow parameter to WinMain. If the dwFlags does not specify the STARTF_USESHOWWINDOW flag, this member is ignored, and the value supplied to WinMain will be <<description here>>.

    For GUI processes, the first time ShowWindow is called, the explicit parameter supplied will be ignored and it will be treated as if the call had been for SW_SHOWDEFAULT. The value supplied as the nCmdShow parameter to WinMain will be used. In subsequent calls to ShowWindow, to use the parameter value supplied to WinMain, the SW_SHOWDEFAULT flag must be explicitly specified.

    StringCchCopy

    This has a length parameter which is erroneously specified as

    cchDest
    [in] Size of the destination buffer, in characters.
    This value must equal the length of pszSrc, plus 1 to account for the copied source string and terminating null character. The maximum number of characters allowed is STRSAFE_MAX_CCH.

    This documentation is erroneous. The correct documentation is

    cchDest
    [in] Size of the destination buffer, in characters
    This value must be not less than the length of pszSrc plus 1 (to account for the copied string and terminating null character), and no greater than the length of pszDest. The maximum number of characters allowed is STRSAFE_MAX_CCH.

     

    strtok _tcstok, wcstok

    The documentation is obscure and confusing. It should state


    strToken

    String containing the token or tokens. This will be a pointer to the string for the first use of the string and NULL to obtain subsequent tokens from the string. This must point to a writeable string, and not a string literal.

    Return Value

    Returns a pointer to the next token found in strToken. Returns NULL when no more tokens are found. Each call modifies strToken by substituting a NUL character for each delimiter that is encountered.


    Note that NULL is not a character value, it is a pointer. Lower-case "null character" is acceptable, as is the formal name, NUL (which has one L, not two, and is the official name of the character whose value is 0). But there is no such thing as a "NULL character".

    In regards to the security note, what is the security risk involved here, since no string copies are done?

    The VS7 (and earlier) documentation is incorrect in that it states that this uses "a static variable", which it does not. To use a static variable would mean this call would not be thread-safe, and there would be no way to correctly use it in a multithreaded environment. The documentation fails to account for the implementation, which is uses a thread-local variable to hold this state. The implementation is correct, but the documentation is wrong. This documentation error was corrected in the VS8 documentation.

    STRUCT (MASM)

    As with almost all other documentation for the assembler, this is completely and utterly useless. It says, in its entirety:

    name STRUCT [[alignment]] [[, NONUNIQUE]]
       fielddeclarations
    name ENDS

    Of course, it would be really useful if something was said about what the alignment does, what NONUNIQUE means, and what a fielddeclaration was and how to use it. But, hey, that would be, like, totally useful, and we can't have that! It sets a bad precedent; next thing we know, users will expect real documentation!

    SWORD (MASM)

    Does not contain a hyperlink to WORD.

    States that it initializes a signed word, but allows values which are outside the range -32768..32767 to be declared, without issuing a warning.

    Does not mention that the allowable range of values is -32768..65535, which of course makes no sense whatsoever.

    Makes you wonder why there is a separate declaration of a signed word value if it allows illegal values. No rationale is given.

    TBYTE (MASM) 

    Does not contain a hyperlink to DT.

    It does not mention the allowable integer range. Does not specify the allowable number of floating point digits for a long double. Does not state that this allows the implementation of the long double type of C/C++, or that the C/C++ compilers do not themselves implement the long double type.

    This can be used to define 10-byte floating point types, the equivalent of what the C/C++ compiler would have done if it supported the long double data type.

    A floating point number can specify <<value here>> digits of precision, and the allowable range for a floating point number is 3.37×10-4392..1.18×104392.

    TCITEM

    Like most such data structures, the documentation is confusing and misleading.

    For example, the descriptions

    TCIF_IMAGE

    The iImage member is valid

    TCIF_PARAM

    The lParam member is valid

    TCIF_STATE

    The dwState member is valid

    TCIF_TEXT

    The pszText member is valid.

    These are nonsensical descriptions. The correct descriptions are shown below. However, I've added text in italics where the actual specifications are unclear and unknowable.

    TCIF_IMAGE

    When inserting or setting an item, this indicates that the iImage member contains the index into the image list of the image to be displayed. If this flag is not set during creation, the assumed value is -1, indicating no image is set. If this flag is not set when modifying an item, the current image selection is not changed. When retrieving item information, this causes the iImage member to be filled in with the current image list index. If no image is selected, this will be set to the value -1 for inserting or setting an item, and will contain -1 after retrieving information about an item. If this flag is not set, the value of the iImage member is not defined after completing a retrieval.

    TCIF_PARAM

    When inserting or setting an item, this indicates that the lParam member contains information to be set. If this flag is not set during creation, the lParam value is assumed to be 0. If this flag is not set when modifying an item, the current lParam value which was already in place is not changed. When retrieving item information, this causes the lParam member to be filled in with the current value set for the item. If this flag is not set on retrieval, the value of the lParam member is undefined upon completing the retrieval.

    TCIF_STATE

    When inserting an item, this flag is ignored; the state cannot be set at creation time. When setting an item, this indicates that the dwState and dwStateMask members contain valid information. The existing state in the item will be changed in accordance with the state/statemask settings (see Remarks). When retrieving information about an item, the dwStateMask determines what information will be retrieved. [Note: is dwState completely changed but only those parts specified by dwStateMask are returned, or is only the subfield specified by the dwStateMask actually modified?]. If this flag is not set for retrieval, the contents of the the dwStateMask member are ignored, and the contents of the dwState member are undefined upon completion of the retrieval.

    TCIF_TEXT

    When inserting or setting an item, this indicates that the pszText member points to a NUL-terminated string which is the text to be associated with the item. The contents of the cchTextMax member are ignored. When retrieving information about an item, this indicates that the pszText member is a pointer to a buffer to be filled in; the length of the buffer, in characters, is specified by the cchTextMax member. Upon completion of the retrieval, the text will be placed in the buffer referenced by pszText. [It is not specified if cchTextMax is modified to indicate the actual number of characters retrieved, not including the terminating NUL character. It is not specified what happens if the buffer is too small to hold the string. Does it truncate silently? Does it return an error code? Who knows?]. If this flag is not set for retrieval, the contents of the pszText and cchTextMax members are ignored. [Are they really ignored, or do they have to be NULL, 0?]

    Remarks

    [Need to say something meaningful here about the state and state mask]

    _tcstok

    See strtok.

    Terminating a Process

    This contains an egregiously incorrect statement. Down near the end of the article, it says

    During process startup and DLL initialization routines, new threads can be created, but they do not being execution until DLL initialization is finished for the process.

    This will not work, because the process will deadlock. What happens is that the CreateThread call causes a DllMain/DLL_THREAD_ATTACH notification to be generated. The loader lock is holding the DllMain function locked; CreateThread cannot finish until the DLL processes the DLL_THREAD_ATTACH event, and this event cannot be processed until the lock is released, which cannot happen until CreateThread returns, which it can't do because it can't acquire the lock. Using DisableThreadLibraryCalls does nothing, because that flag is not checked until after the lock is acquired.

    The same error appears in the CreateThread documentation.

    timeSetEvent

    This does not state that the callback occurs in a separate thread. Therefore, there are limitations, such as

    _tmakepath

    see _makepath

    _tmakepath_s

    see _makepath

    ToolTip Control Reference 

    This contains a nonsensical example. In the section titled "Creating ToolTip Controls" the code shown is a  CreateWindowEx call whose first parameter is NULL. This is complete nonsense. The first parameter to CreateWindowEx is a DWORD, and therefore cannot be NULL, which is a pointer. It should have been 0.

    Tree Control

    Editing tree control labels

    There had once been a KB article on how to edit labels in a property page, but it has since disappeared. The problem is that the bug that existed in earlier versions of MFC may still exist. The sad thing about the article is that it had so many errors that it actually introduced new errors! The correct code, adapted from Win32 Programming, is

    class CMyPage : public CPropertyPage {...}
    BOOL CMyPage::PreTranslateMessage(MSG * pMsg)
       {
        if(pMsg->message == WM_KEYDOWN &&
           (pMsg->wParam == VK_RETURN ||
            pMsg->wParam == VK_ESCAPE))
           { /* special key */
            static const TCHAR editclassname[] = _T("edit");
            TCHAR focusname[_countof(editclassname) + 1];
            HWND focus = ::GetFocus();
            ::GetClassName(focus, focusname, _countof(focusname));
            if(lstrcmpi(focusname, editclassname) == 0)
               { /* edit control */
                if(::GetWindowLong(focus, GWL_STYLE) & ES_WANTRETURN)
                   { /* ES_WANTRETURN */
                    ::TranslateMessage(pMsg);
                    ::DispatchMessage(pMsg);
                    return TRUE;
                   } /* ES_WANTRETURN */
               } /* edit control */
    
            if(::SendMessage(focus, WM_GETDLGCODE, 0, 0) &
                  (DLGC_WANTALLKEYS | DLGC_WANTCHARS | DLGC_WANTMESSAGE))
                     { /* deal with it here */
                      ::TranslateMessage(pMsg);
                      ::DispatchMessage(pMsg);
                      return TRUE;
                     } /* deal with it here */
           } /* special key */
        return CPropertyPage::PreTranslateMessage(pMsg);
       }

    _tsplitpath

    see _splitpath

    _tsplitpath_s

    see _splitpath

    TVITEM

    Why does the index for this reference CTreeView::GetItem, which then has a hyperlink to TVITEM? Why is there such a silly reference. It should link directly to TVITEM!

    TVN_SELCHANGING

    This is a bit confusing when compared to the documentation for TVN_SELCHANGED. For example, do we know for certain that stateMask is valid? Given we know it is not valid for TVN_SELCHANGED, an explicit statement to the effect of its validity would be useful.

    TVN_SINGLEEXPAND

    "Pointer to an NMTREEVIEW structure that contains information about this notification"

    Gee, golly, wow! Perhaps a bit more documentation would be useful here?

    The Return section states

    Return TVNRET_DEFAULT to allow the default behavior to occur. To modify the default behavior, return:

    TVNRET_SKIPOLD Skip default processing of the item being unselected.
    TVNRET_SKIPNEW Skip default processing of the item being selected.

    Wouldn't it be nice to know what the meaning of "default processing" is?

    .TYPE (MASM) 

    The entire documentation of this is shown here in its entirety.

    See OPATTR.

    I'm sure, really, really sure, there must be something useful this operator does, but apparently it is in the same category of State Secret that OPATTR itself is.

    I keep wondering if Microsoft has considered hiring even marginally competent documentation writers! Didn't anybody ever notice this?

    TYPE (MASM) 

    The entire documentation of this is:

    TYPE expression

    Returns the type of
    expression.

    That's it! INCOMPETENT!!!! WHO WOULD ALLOW THIS INSANITY TO OCCUR???

    Correspondent "japeth" on microsoft.public.masm posts a technique for determining if you are assembling a 64-bit or 32-bit executable.

    IS_32 EQU 0FF04h
    IS_64 EQU 0FF08h
    
    ENV equ TYPE PROC
    
    IF ENV EQ IS_32
    ...32-bit stuff
    ENDIF
    IF ENV EQ IS_64
    ...64-bit stuff
    ENDIF

    Now how one interprets these random integers would be an essential part of any documentation done by anyone with intelligence higher than a slime mold. Why isn't it in the documentation?

    (Later) I'm sorry about the remark about slime molds, it was uncalled for. Several slime molds have now emailed me about defamation of character.

    union initializers

    Here we are in 2008, 17 years after Win32 has been released. What do we have in the C discussion of union initializers? An example from 16-bit Windows!

    // initializers_and_unions.cpp
    // compile with: /W1
    struct Point
    {
    unsigned x;
    unsigned y;
    };
    
    union PtLong
    {
     long  l;
     Point pt;
    };
    
    int main()
    {
     PtLong ptOrigin;
     PtLong ptCurrent = ptOrigin;   // C4700
    }
    Initialize the union with a brace-enclosed initializer for the first member
    PtLong ptCurrent = { 0x0a000aL };
    

    How many things can you find completely ridiculously wrong about this example?

    Let's start with the annotation: "compile with: /W1". Is there a living, breathing programmer today who compiles with less than /W3? Most of us try to use /W4! But here's an example that insists you compile using a level of error checking that probably won't even detect misplaced semicolons (OK, I'm stretching the point, but just a little bit...).

    Then it declares a Point structure of two 32-bit integers, overlaps the first of them with a long, and assigns a value 0x0a000aL to it. Now why is the L appended? All integers are by default 32-bit, so adding L is syntactic noise that serves no useful purpose. Then the example clearly assigns the numerical value 655370 to the x component, leaving the y component uninitialized. What is the point of this?

    Now, had it been written

    struct Point
    {
     unsigned short x;
     unsigned short y;
    };

    the example has some vague chance of making sense, although it is not at all clear why anyone in their right mind would want to store a point that only had 16-bit unsigned coordinates.

    Perhaps the example could be made more realistic?

    Note that if you write

    struct Point16 {
        unsigned short x;
        unsigned short y;
    };
    
    union PtLong {
        long L;
        Point16 pt;
    };
    class SomeClassName {
        public:
            static const PtLong SomeValue = { 0x0a00aL };
    };

    the C++ compiler chokes completely; this is not a valid initializer for a class declaration; perhaps it would be useful to show how to initialize a union that is part of a class definition? Or state that this is not possible.

    But in any case, the example is incorrect as written and completely silly as rewritten. Didn't anyone notice that we are living in a 32-bit world?

     

    UNREFERENCED_PARAMETER

    This undocumented feature allows you to suppress error message C4100, "unreferenced formal parameter". It is used extensively in the platform SDK and is defined in winNT.h. It is not documented as part of the C4100 error message documentation. It is not documented at all.

    UpdateAllViews

    Due to a very flawed concept at design time, the third parameter is specified as a CObject *. In a sane world, this would have been specified as an LPVOID. The number of times I have seen people feel that they can only pass a CObject *, and feel forced to create some weird class derived from CObject just to pass information, is horrendous. In a world that has pretensions of sanity, there would be a second, overloaded method of UpdateAllViews defined that would take an LPVOID parameter as the pHint.

    The correct documentation should read

    pHint

    Any value of the programmer's choice, cast to a CObject *, which provides additional information about the modification. The value need not be a derived class of CObject *, but can be any pointer-sized value whatsoever.

    _USE_MATH_DEFINES

    This is a symbol which needs to be declared before <math.h> is included. It allows a number of typical numeric constants to be defined. There is a serious anomaly between a Debug and a Release version of an MFC app.

    I had a header file that said

    #define _USE_MATH_DEFINES
    #include <math.h>
    __inline double DEGREES_TO_RADIANS(double t) { return ( t / 360.0) * 2.0 * M_PI;}

    This compiles fine in the Debug build, but in the Release build, it reports that M_PI is an undefined symbol. Why is this? It turns out that in the release version, the file ATLComTime.h contains the lines

    #ifndef _DEBUG
    #define ATLCOMTIME_INLINE inline
    #include <atlcomtime.inl>
    #endif

    and atlcomtime.inl contains the declaration

    #include <math.h>

    This changes the fundamental behavior of a build. This represents erroneous code on the part of Microsoft. A Release build should build identically to a Debug build (except for the optimizations and other features of Release build) with no source changes required.. But here, the code which compiles under the Debug configuration will not build under Release configuration without a source change!

    I solved this by adding the following code to get the header file as shown

    #pragma once
    #if (defined(_INC_MATH) && !defined(_USE_MATH_DEFINES)) || (defined(_INC_MATH) && defined(_USE_MATH_DEFINES) && !defined(M_PI))
    #error "You must do #define _USE_MATH_DEFINES before math.h is included the first time
    // Note that in release mode, math.h is included when it is NOT included in the debug version
    // put the
    // #define _USE_MATH_DEFINES
    // in stdafx.h before the #include <afxwin.h>
    #endif
    
    #define _USE_MATH_DEFINES
    #include <math.h>
    
    __inline double DEGREES_TO_RADIANS(double t) { return ( t / 360.0) * 2.0 * M_PI;}

    UuidFromString

    The definition is erroneous. It uses "unsigned char", which is stupid because strings are necessarily signed character sequences, but it also suggests there is no Unicode version of this API, which is false. It also uses the absurd "FAR" notation, which is unbelievably silly and has been obsolete for over 20 years (FAR went away when the first line of Win32 was written in 1988).

    Due to terminal irresponsibility, there is no Unicode-aware definition of the string parameter. Thus, the code as written cannot possibly work in a Unicode application. It is necessary to add the definition

    #ifdef UNICODE
    #define RPC_TSTR RCP_WSTR
    #else
    #define RPC_TSTR RPC_CSTR
    #endif

    The corrected definition is

    RPC_STATUS RPC_ENTRY StringToUuid(
        __inopt RPC_TSTR StringUuid,
        __out UUID * Uuid)

    Note that for reasons that seem to make no sense other than slovenliness, the StringUuid parameter is not indicated as a const, which is a clear oversight, since there is no reason to require a non-const string here. And it seems unusual that the formal API definition actually says the input parameter is optional, allowing a NULL pointer, but the documentation does not state what will happen if NULL is specified!

    The same error is made in the UuidToString.

    UuidToString

    The definition is erroneous. It uses "unsigned char", which is stupid because strings are necessarily signed character sequences, but it also suggests there is no Unicode version of this API, which is false. It also uses the absurd "FAR" notation, which is unbelievably silly and has been obsolete for over 20 years (FAR went away when the first line of Win32 was written in 1988).

    Due to terminal irresponsibility, there is no Unicode-aware definition of the string parameter. Thus, the code as written cannot possibly work in a Unicode application. It is necessary to add the definition

    #ifdef UNICODE
    #define RPC_TSTR RCP_WSTR
    #else
    #define RPC_TSTR RPC_CSTR
    #endif

    The correct definition is

    RPC_STATUS RPC_ENTRY UuidToString(
        __in const UUID * Uuid,
        __out_deref RPC_TSTR * StringUuid)

    The same error is made in the StringToUuid.

    VARARGS.H  

    As far as I can tell, the documentation for VARARGS.H was put in by a sadist. It is completely content-free. The sole value of this text can only be to annoy the reader. The "copy code" button copies a prototype of a variable-argument function, but there is not a single line of code showing how to actually use this feature to write a function of a variable number of arguments. I am not sure there is a point to this piece of content-free documentation other than to waste space. There are no hyperlinks to any of the associated code that would be critical to understand, such as va_arg, va_start, or va_end. There is not even the slightest hint of the names of these functions anywhere in that hopelessly useless example of the failure of the documentation writers' profession.

    VERIFY

    The ASSERT macro is missing an absolutely critical piece of information in its Remarks section:

    The ASSERT macro will reset the value found by ::GetLastError to 0 (ERROR_SUCCESS). Therefore, if the last error value is needed, it must be obtained before the ASSERT macro appears, because otherwise, in debug mode, this critical piece of information will be lost. For example, note the following two incorrect examples and the correct example which follows them

    VERIFY(AnyAPICall(...));
    // Note that if the API call fails and the programmer hits "Retry" to enter the debugger,
    // the last error code (err,hr) will show 0 (ERROR_SUCCESS or S_OK)
    BOOL result;
    VERIFY(result = AnyAPICall(...));
    if(!result)
       { 
         MyReportError(GetLastError());  // WILL ALWAYS REPORT ERROR_SUCCESS IN DEBUG MODE!
       }

    Correct code (note that VERIFY cannot be used if the value returned by GetLastError is required!)

    BOOL result = AnyAPICall(...);
    if(!result)
        {
         DWORD error = GetLastError();
         ASSERT(FALSE);
         MyReprotError(error);
        }

    The same problem exists with ASSERT.

    VerQueryValue

    The parameters are erroneously specified in some versions of the documentation as

    BOOL VerQueryValue(      

        const LPVOID pBlock,     LPTSTR lpSubBlock,     PUINT lplpBuffer, // THIS IS WRONG!     PUINT puLen ); 

    whereas the actual header file reveals that the correct parameters are

    BOOL
    APIENTRY
    VerQueryValueA(
            const LPVOID pBlock,
            LPSTR lpSubBlock,
            LPVOID * lplpBuffer,
            PUINT puLen
            );
    BOOL
    APIENTRY
    VerQueryValueW(
            const LPVOID pBlock,
            LPWSTR lpSubBlock,
            LPVOID * lplpBuffer,
            PUINT puLen
            );

    It is extremely unclear why the parameter type is specified as LPSTR or LPWSTR; in an intelligent world, these would have been specified as LPCSTR and LPCWSTR.  There is no sane reason these should not be constant, but that represents an actual bug in the header files.

    vfw.h

    This header file does not contain nor is referenced by adequate documentation. Due to fundamental design errors in the creation of this file and the related AVIRIFF.h file, you can get serious compilation errors if you include them in the wrong order. It needs to have this information added:

    "If this file is included with AVIRIFF.h, then vfw.h must be included first. Due to an error in the way these files were constructed, vfw.h defines symbols which are either not defined in AVIRIFF.h or would result in "macro redefinition" errors if AVIRIFF.h is included first. This is due to erroneous attempts to duplicate symbols instead of using a common header file that includes all necessary information."

    For a detailed discussion, see AVIRIFF.h.

    VirtualAllocEx 

    This should contain, and does not contain, hyperlinks to ReadProcessMemory and WriteProcessMemory.

    VirtualAllocExNuma 

    See also my essay on using VirtualAllocExNuma in the NUMA Explorer.

    This was apparently written by the same beginner who wrote the other NUMA examples. It contains several particularly horrendous examples of poor style.

    For example, in starts out using swscanf and assuming the application is Unicode. Who, in their right mind, ever uses the antiquated scanf-class functions for anything that actually matters? The simple code would have been to write

    AllocationSize = _tcstoul(argv[1], NULL, 16);

    which is simpler and doesn't involve retro PDP-11 programming techniques, and is independent of the mode in which the app has been compiled.

    But this raises a deeper question. Why is the size given on the command line in terms of bytes, expressed in hexadecimal? Wouldn't it make more sense to have this parameter interpreted as megabytes, expressed in decimal? Or maybe in pages, in decimal? How about a natural interface for the user!

    It also uses void wmain as the name of the main function. The correct and compilation-mode-independent declaration should be

    int _tmain(int argc, _TCHAR * argv[])

    This represents good modern practice.

    Next, it compares BOOL results to TRUE, always a bad sign, and one of the red flags that says "whoever wrote this is clueless".

    if(TRUE != GetNumaHighestNodeNumber(...))

    Huh? Where, in the documentation, does it say that this API returns TRUE if it succeeds? It clearly states "The result is nonzero if this function succeeds". It does not say "The result is TRUE if this function succeeds".  And why compare to a Boolean literal? Amateur. The correct code is

    if(!GetNumaHighestNodeNumber(...))

    This failure mode appears multiple times in the example, and is inexcusable. MSDN examples must represent best practice, and comparing Boolean values to Boolean literals is not, by any rationale, "best practice".

    Also, having explicitly written swscanf for wide-character output, why are all the output functions written in 8-bit ANSI? Hello, localization anyone?

    A goto? Get real. This indicates a failure to structure the code properly. In means the programmer is writing PDP-11 code without thinking about the right factoring to avoid the need for a goto, then saying, later, "Oops! I forgot to delete that stuff!" and kludging up a way to free it, but this forces a goto. Sad, really. Look at the code to see how blindingly stupid this is (only the relevant parts of the code are shown):

    void wmain(...)
    {
           ... goto Exit;          // [1]
    
           ... goto Exit;          // [2]
    
          PVOID Buffers = ...;     // [3]
    
          ... goto Exit;           // [4]
    
          ... goto Exit;           // [5]
    
    Exit:
          if(Buffers != NULL)      // [6]

    Note that the goto statements at [1] and [2] bypass the initialization of Buffers at [3], so what happens when we get to [6] from [1] or [2]? What is the value of Buffers? Given the initialization has been skipped, the answer is undefined, so the code is erroneous as written! Who taught this clown to program? This is the result of poor structuring. It is also a classic example of what Doug Harrison refers to as something coded in the "Fanciful" language.

    A better factoring would be to allocate the buffer, call a separate function to perform the computation, and delete the buffer upon return. Instead of kludgy, antiquated goto statements that introduce errors, simply execute a return from the subroutine. No matter where you return from, the caller will delete the buffer, in all cases.

    Given this is written in C++, it would not even require refactoring if it were written as

    class BufferInfo {
          public:
             BufferInfo() { Buffer = NULL; }
             virtual ~BufferInfo {if(Buffer != NULL)
                                               VirtualFree(Buffer, 0, MEM_RELEASE);
                                          }
             PVOID Buffer;
    };
    std::vector<BufferInfo> Buffers;
    Buffers.resize(NumberOfProcessors);

    Then the C++ destructors would automatically handle this. Since the code is already in C++, there is no reason to not use the basic C++ library! It seems silly to throw out the important parts of C++.

    Why are AllocationSize and PageSize global variables? There is no need to make these global; it doesn't even make sense. For example, AllocationSize is only used in one function, wmain!

    The DumpRegion function takes a BOOL parameter, and the same stupid error, where the parameter is directly compared to TRUE, is repeated. Hasn't anyone ever taught these people how to program?

    It prints GetLastError codes out in hex, in spite of the fact that in the header file winerror.h these are nearly always given as decimal. But that's OK, let's make sure it is hard for the user to discover the reason for the error...

    Since a very restricted number of error codes appear in hex, the correct behavior, given the programmer is too lazy to have written a FormatMessage subroutine, is to show both the decimal and hex versions. Also, it is best to call ::GetLastError exactly once, and store the value in a variable, which aids in debugging:

    DWORD err = ::GetLastError();
    _tprintf(_T("<name of operation here> failed, error code %d (0x%08x")\n"), err, err);

    Why do some error messages end with \r\n (which is incorrect) but others don't end with even a \n?

    What is the purpose of emitting an empty string: puts("")?

    Given this is clearly written in C++ (the declarations appear at places other than the head of the file), why is it using malloc instead of new?

    It is clear this code was never compiled; the statements

    BOOL IsValid = WsInfo[i].u1.VirtualAttributes.Valid;
    DWORD Node   = WsInfo[i].u1.VirtualAttributes.Node;

    are invalid because the PSAPI_WORKING_SET_EX_INFORMATION field does not have a "u1" member.

    The correct lines would have been

    BOOL IsValid = WsInfo[i].VirtualAttributes.Valid;
    DWORD Node   = WsInfo[i].VirtualAttributes.Node;

    This example is so horrible I had to rewrite it so it looked like someone other than a beginner had written it. See my rewrite. I decided that since it is already written in C++, there was no reason to revert to the obsolete and inappropriate malloc call, so I rewrote it using std::vector because that makes more sense, and eliminates the need for a goto, eliminates the need for weird loops to free things, eliminates the need to ZeroMemory, and so on.

    _vsnprintf, _vsnwprintf

    This contains the inexplicable comment

    Security note: Ensure that format is not a user-defined string. For more information, see Avoiding Buffer Overruns.

    What could this possibly mean? Does it mean you can only use strings defined by Microsoft? As stated, the comment makes no sense. Quite possibly it might have meant

    Security note: The string, as supplied, must be one which corresponds properly to the argument list provided, in terms of number of arguments and their types. It is the responsibility of the programmer to ensure that this formatting string is correct in that regard.

    This at least states the problem correctly, and removes the ambiguous reference to "user", which could mean lots of different things, such as "the user of the compiler", "the user of Visual Studio", "the user of the application", or something else for which the meaningless and confusing word "user" might be applied.

    It is also not clear how any error could cause a buffer overflow, because these library calls are specified as checking the buffer length and not overflowing it. Could it be that someone cut-and-pasted a meaningless comment from some other place, such as _vsprintf? It might be more obvious if the See Also had a link to vsprintf, since it already has links to printf, sprintf and fprintf.

    _vsprintf, _vswprintf 

    This contains a meaningless comment:

    Security note:...Also, ensure that format is not a user-defined string.

    It is not clear what a "user-defined" string is. Does it mean "the user of the compiler", "the user of Visual Studio" or "the user of the application"? What is meant by the ambiguous term "user"?

    WaitForMultipleObjects, WaitForMultipleObjectsEx, WaitForSingleObject, WaitForSingleObjectEx

    There is a table of return values, and a sentence AFTER the table that talks about WAIT_FAILED. Why is this not an entry in the table itself? Why is it a separate sentence and not part of the table of return values?

    wcstok

    See strtok.

    WindowFromPoint 

    This does not specify the Point parameter should be in screen coordinates.

    WinMain 

    The nCmdShow parameter is not adequately specified. The correct documentation should appear as

    nCmdShow

    Specifies how the window is to be shown. This value can either be explicitly supplied by the creator of the process, by setting the wShowWindow field in the STARTUPINFO block specified by CreateProcess, as well as setting the STARTF_USESHOWWINDOW flag in the dwFlags field of the STARTUPINFO block. This would also be specified as the nShowCmd to ShellExecute, or as the nShow field of the SHELLEXECUTEINFO structure of ShellExecuteEx. If this was not explicitly specified by the caller of CreateProcess, <<needs explanation of default behavior here>>.

    WINVER

    The WINVER documentation is slightly coherent, although its relationship to the _WIN32_WINNT seems to be that they are expected to be identical, and therefore there is a serious question as to why there are two different symbols representing the same thing that must have the same value. Would it not make sense to consolidate these? The meaning of _WIN32_WINDOWS is also confusing, especially since it needs to be the same

    The table is badly organized and should read as shown below.

    Note that the new discussion about the use of NTDDI_VERSION is essentially completely incoherent.

    Note that in a sane world, the management of this declaration in stdafx.h would be handled by a property page, where silly values like 0x400 would not be visible at all to the user; it might have a set of radio buttons to select "What version of Windows will this application run on?" Other silly values like WIN32_IE would also be handled by a set of check boxes (the only reason the IE version matters is the incompetence at Microsoft that released commctrl.h with versions of IE instead of being part of a development release!)

    WINVER _WIN32_WINNT _WIN32_WINDOWS Product release
    0x0400 not used 0x0400 Windows 95
    0x0400 not used Windows NT 4.0
    0x0410 not used 0x0410 Windows 98
    0x0500 not used 0x0500 Windows Me
    0x0500 not used Windows 2000
    0x0501 0x0501 not used Windows XP
    0x0502 0x0502 not used Windows Server 2003
    0x0600 0x0600 not used Windows Vista

    WM_COMPAREITEM 

    The documentation erroneously states

    The system sends the WM_COMPAREITEM message to determine the relative position of a new item in the sorted list of an owner-drawn combo box or list box. Whenever the application adds a new item, the system sends this message to the owner of a combo box or list box created with the CBS_SORT or LBS_SORT style.

    This documentation is erroneous. The correct documentation is

    The system sends the WM_COMPAREITEM message to determine the relative position of a new item in the sorted list of an owner-drawn combo box or list box. Whenever the application adds a new item, the system sends this message to the owner of a combo box or list box created with the CBS_SORT or LBS_SORT style, unless that control as the CBS_HASSTRINGS or LBS_HASSTRINGS style.

    In addition, the following comments have inadvertently been omitted from the Remarks

    Remarks

    The only permissible values that can be returned are the specified values of -1, 0 and 1. Note that functions such as strcmp, wcscmp, _tcscmp, strcmpi, wcscmpi, tcscmpi, strcoll, wcscoll, _tcscoll, and several other comparison functions are not specified as returning these values; they return arbitrary negative or arbitrary positive integers, which will result in erroneous ordering decisions. Note that ::CompareString and ::CompareStringEx and ::CompareStringOrdinal return enumeration types which are not -1, 0 and 1. Therefore, if any of these functions are used, it is up to the program to convert the results to -1, 0, or 1 as appropriate.

    WM_CTLCOLORBTN 

    This documentation erroneously suggests that this message is sent for check boxes or radio buttons; it is not. It is not. Group boxes, check boxes, and radio buttons receive WM_CTLCOLORSTATIC messages. Apparently nobody has looked at this documentation since the days of 16-bit Windows. It does not mention that if the HBRUSH returned is NULL, any other changes made to the HDC are discarded.

    WM_CTLCOLOREDIT 

    This does not mention that if the HBRUSH returned is NULL, any other changes made to the HDC are discarded. At least it gets the part about disabled and read-only controls correct.

    WM_CTLCOLORSTATIC 

    This fails to mention that it is sent for group boxes, radio buttons, check boxes, disabled edit controls, and read-only edit controls. It does not mention that if the HBRUSH returned is NULL, any other changes made to the HDC are discarded.

    WM_DELETEITEM 

    The documentation erroneously states

    This message is sent to the owner of a list box or combo box when the list box or combo box is destroyed or when items are removed by the LB_DELETESTRING, LB_RESETCONTENT, CB_DELETESTRING, or CB_RESETCONTENT message. Windows sends a WM_DELETEITEM message for each deleted item. Windows sends the WM_DELETEITEM message for any deleted list box or combo box item with nonzero item data.

    This is wrong in so many ways it is hard to imagine how documentation this erroneous could persist.

    The correct documentation is

    This message is reliable only for owner-draw list boxes and owner-draw combo boxes. It must be ignored in non-owner-draw controls. For a non-owner-draw controls, due to a serious implementation error in the innards of CB_DELETESTRING and LB_DELETESTRING, it will be called for non-owner-draw controls, but not for every element, and therefore it cannot be depended upon in this context. It will not be sent, for a non-owner-draw controls, for either the CB_RESETCONTENT, LB_RESETCONTENT or DestroyWindow situations. It will not be sent when CB_DELETESTRING or LB_DELETESTRING in a non-owner-draw control deletes the last remaining item, and must therefore be considered unreliable for non-owner-draw controls.

    The only way this message can be used reliably is for owner-draw controls.

     

    WM_DRAWCLIPBOARD 

    See Using the Clipboard.

    WM_KICKIDLE

    This is an otherwise undocumented message, which is actually quite important. You need to be able to handle this message if you are trying to do background idle processing in a dialog-based app, the equivalent of the CWinApp::OnIdle handling for a regular app.


    WM_KICKIDLE Notification

    The WM_KICKIDLE message is sent to a dialog-based app window when there the message queue is empty and idle messages have not been suppressed.

    Syntax

      WM_KICKIDLE
        WPARAM wParam: (flags)
        LPARAM lParam: idle count

    Parameters

    wParam

    A set of flags. Normally 0, but for a dialog box, the flag MSGF_DIALOGBOX is set

    lParam

    The idle count. This is set to 0 each time a new message is processed. It is incremented each time the WM_KICKIDLE message is sent when the thread's message queue is empty. See Remarks.

    Return Value

    Zero to terminate idle processing for this idle event. Nonzero to allow idle processing to continue.

    Remarks

    WM_KICKIDLE is sent by the default message pump when the thread's message queue is empty.

    Notification Requirements

    Minimum DLL Version None
    Header afxpriv.h
    Minimum operating systems Windows 95, Windows NT 3.1

    #include <afxpriv.h>
    ON_MESSAGE(WM_KICKIDLE)
    
    LRESULT CMyDialog::OnKickIdle(WPARAM, LPARAM)
       {
        LRESULT result = 0;  // assume termination of idle processing
        // ... may change result to nonzero
        return result;
       }

    WM_MEASUREITEM 

    The documentation is incomplete. A key part of the description of the MEASUREITEMSTRUCT should say

    When the style of the control is CBS_OWNERDRAWFIXED, there will be two WM_MEASUREITEM messages sent when the control is created. The first of these will have an itemID field equal to ((UINT)-1), and will be used to determine the size of the edit control when the style is CBS_SIMPLE or CBS_DROPDOWN, and the size of the control itself when the style is CBS_DROPDOWNLIST. The second will be sent with an itemID equal to 1, and will be used to determine the size of the elements in the ListBox portion of the control. Note that the itemID will be 1, even though there is not an element in location 1.

    When the style of the control is CBS_OWNERDRAWVARIABLE, there will be one WM_MEASUREITEM message sent when the control is created. This message will have an itemID field equal to ((UINT)-1) and will be used to determine the size of the edit control when the style is CBS_SIMPLE or CBS_DROPDOWN, and the size of the control itself when the style is CBS_DROPDOWNLIST. Whenever a string is inserted into the control, using CB_ADDSTRING, CB_INSERTSTRING, LB_ADDSTRING or LB_INSERTSTRING, a new WM_MEASUREITEM will be issued. In such cases, the itemID will be the index of the newly-added element in the control, and there will be an item in the specified location. If the style of the control includes CBS_HASSTRINGS or LBS_HASSTRINGS, then the itemData will be NULL. If the style does not include CBS_HASSTRINGS or LBS_HASSTRINGS, the itemData will be the parameter supplied as the LPARAM.

    WM_NCHITTEST

    This message is not sent if there is mouse capture.

    Note that there has been an incompatible change in the specification of the ON_WM_NCHITTEST Message Map entry and the prototype used for the CWnd::OnNcHitTest method, which will generate a compiler error if you upgrade from an earlier version of Visual Studio.

    From Kero:

    "HTSYSMENU In a window menu or in a Close button in a child window."

    But can you find an example of such Close button ? :-)

    When MDI child isn't maximized, its Close button exist as a part of HTCLOSE area.

    When MDI child is maximized, we just see a special item of main MDI frame menu as faked "Close button", and it is a part of HTMENU area, not HTSYSMENU area !

    A standard technique for disabling window moving is to return HTNOWHERE when the superclass has returned HTCAPTION. This does not work with Aero enabled on Vista. It is not clear which other WM_NCHITTEST  codes fail in the same way, or what else might be wrong.

    WM_POWERBROADCAST 

    The documentation does not state that this message is only sent to top-level windows, e.g., those windows found by EnumWindows.

    WM_PSD_ENVSTAMPRECT 

    The lParam is specified as a "pointer to a structure that contains the coordinates, in pixels, of the envelope-stamp rectangle". What kind of structure? Presumably an LPRECT or LPCRECT, but "a structure" is not really informative.

    WM_PSD_FULLPAGERECT

    The lParam is specified as a "pointer to a structure that contains the coordinates, in pixels, of the sample page". What kind of structure? Presumably an LPRECT or LPCRECT, but "a structure" is not really informative.

    WM_PSD_GREEKTEXTRECT 

    The lParam is specified as a "pointer to a structure that contains the coordinates, in pixels, of the Greek text rectangle". What kind of structure? Presumably an LPRECT or LPCRECT, but "a structure" is not really informative.

    WM_PSD_MINMARGINRECT

    The lParam is specified as a "pointer to a structure that contains the coordinates, in pixels, of the minimum margin rectangle". What kind of structure? Presumably an LPRECT or LPCRECT, but "a structure" is not really informative.

    WM_PSD_MARGINRECT 

    The lParam is specified as a "pointer to a structure that contains the coordinates, in pixels, of the margin rectangle". What kind of structure? Presumably an LPRECT or LPCRECT, but "a structure" is not really informative.

    WM_PSD_YAFULLPAGERECT 

    The lParam is specified as a "pointer to a structure that contains the coordinates, in pixels, of the sample page". What kind of structure? Presumably an LPRECT or LPCRECT, but "a structure" is not really informative.

    WM_RBUTTONDOWN

    Does not contain a hyperlink to WM_CONTEXTMENU.

    WM_RBUTTONUP

    Does not contain a hyperlink to WM_CONTEXTMENU.

    WM_SETCURSOR

    This message is not sent if there is mouse capture.

    _wmakepath

    See _makepath

    _wmakepath_s

    See _makepath

    WNDCLASS structure

    This description includes the statement, for lpfnWndProc, that "you must use the CallWindowProc function to call the window procedure". This doesn't seem to make sense, since the programmer would never, ever want to call this window procedure. A more correct statement would be "You normally would never call this function yourself. It is called by the system in response to DispatchMessage calls. In the extremely unlikely event that you would feel compelled to call this function directly, don't do it."

    Does the hInstance member have meaning any longer? We know why this was required in Win16, but is it really required in Win32? If it is required, explain its purpose.

    WORD (Assembly directive)

    Does not contain a hyperlink to the SWORD directive. Does not contain a crosslink to the DW directive.

    Does not specify that the initializer values can be in the range -32768..65535.

    WritePrivateProfileString

    The two forms, WritePrivateProfileStringA and WritePrivateProfileStringW, deal with the format of the arguments, not with the format of the text file into which the data is written. Independent of the format of the input arguments, the profile file is maintained in ANSI mode, unless you create initially an empty profile file with the Unicode Byte Order Mark (BOM), UFFFE, as the first two bytes. Note that because the x86 is a little-endian machine, the low-order byte must be the first physical byte in the file. This feature is not documented adequately. For exquisite detail about how all this works, see http://blogs.msdn.com/michkap/archive/2006/09/15/754992.aspx. It is not at all obvious from the line

    If the file was created using Unicode characters, the function writes Unicode characters in the file. Otherwise the function writes ANSI characters.

    that you must create the Unicode-format file outside the API call; you cannot create a Unicode-format profile file just by using the WritePrivateProfileStringW API if the file does not already exist. If the file does not exist, an ANSI file is created. even from a Unicode application, even if the file did not already exist. It also does not suggest that the file should start with the Unicode BOM.

    WriteProcessMemory 

    Does not contain a hyperlink to VirtualAllocEx, and should.

    Writing a Control Handler Function

    This is a poorly coded function. Like other functions in this series, it uses a single global SERVICE_STATUS structure, which is exceptionally poor style. Not just a little bit poor style, exceptionally poor style. This is not thread-safe, and therefore it would be a total and complete disaster if the service actually used multiple threads and the individual threads needed to deal with status notifications. It should be replaced with a function which takes the fields as parameters and uses strictly a local (stack) variable to hold the SERVICE_STATUS structure, which it initializes from scratch each time. This code looks like the programmer learned C programming on the PDP-11 in 1975 and never matured beyond that. These code examples should illustrate best practice, not some kludge tossed together over lunch.

    Making SvcDebugOut take an LPSTR and use 8-bit characters (and OutputDebugStringA) means the messages cannot be localized, and introduces a bizarre mix of 8-bit and Unicode characters, which introduces the problems of development, maintenance, and portability if the service is originally written as ANSI and then compiled as Unicode. Such gratuitous inconsistencies are extremely poor style.

    Given the code for SvcDebugOut does not appear on this page, a hyperlink of the calls (all of the calls, not just some of them) to the page that has the code would be appropriate. Putting it in a piece of prose which is not a direct link to the exact definition, not putting the function names in bold face so they stand out, is very poor documentation style.

    When the SERVICE_CONTROL_STOP is received, the correct response is to send SERVICE_STOP_PENDING, and not SERVICE_STOPPED. The SERVICE_STOPPED notification should not be sent until the service has actually stopped, right before the ServiceMain function returns.

    Everyone who writes an example seems to consistently get the SERVICE_CONTROL_INTERROGATE handler dead wrong. You can't just fall through. Note that the "current state" should not be whatever accidental value is found in some random non-thread-safe global variable, but computed based on a computed state which is determined at the time the interrogation happens. The concept of a single global variable in a multithreaded environment is a bit strange.

    The quality of this code should be embarrassing. It is not a "best practice" example.

    Writing a Service Program's main function

    This is poorly coded. It does not use Unicode-aware literals (the use of _T( ) is omitted, even though the variables are declared in some cases as type LPTSTR). The example function SvDebugOut is written in terms of LPSTR, which is silly in this era; it should be LPTSTR. It uses the CHAR datatype and the obsolete and unsafe sprintf function (not even in the VS2005 documentation does it use the proper VS2005 sprintf_s function!). It should use the Unicode-aware function _stprintf_s. It calls OutputDebugStringA when there is no reason to revert to 8-bit characters for any of this! The messages cannot be localized, and introduces a bizarre mix of 8-bit and Unicode characters, which introduces the problems of development, maintenance, and portability if the service is originally written as ANSI and then compiled as Unicode.

    Writing a ServiceMain Function

    This is poorly-coded for the same reason as "Writing a Service Program's main function". It uses 8-bit characters where it should be coded Unicode-aware.

    It talks about how ExitThread should not be used, but the code should return "because returning allows for cleanup of the memory allocated for the arguments". The rationale of never using ExitThread is sensible, but the rationale makes no sense, because no space is allocated for the arguments, and in any case the process is about to stop. Anyone with two functioning neurons would recognize the rationale was bogus, and therefore might be tempted to use ExitThread even though this never makes sense.

    It shows a

    #include <windows.h>

    as if that makes sense to place in a program, and not in a precompiled header file. This will confuse programmers who will think that this should be written.

    It shows initializing a data structure even before the service control handler is registered. This suggests that such initialization is a precondition of RegisterServiceCtrlHandler, which is not true.

    This is a poorly coded function. Like other functions in this series, it uses a single global SERVICE_STATUS structure, which is exceptionally poor style. Not just a little bit poor style, exceptionally poor style. This is not thread-safe, and therefore it would be a total and complete disaster if the service actually used multiple threads and the individual threads needed to deal with status notifications. It should be replaced with a function which takes the fields as parameters and uses strictly a local (stack) variable to hold the SERVICE_STATUS structure, which it initializes from scratch each time. This code looks like the programmer learned C programming on the PDP-11 in 1975 and never matured beyond that.

    It shows allowing SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE even before the service has started; I believe these should not be enabled until the service has achieved SERVICE_RUNNING state.

    It is not clear why it compares MyServiceStatusHandle to (SERVICE_STATUS_HANDLE)0 when normally simply comparing it to NULL is considered sufficient.

    Why SvcDebugOut is not written as a varargs-style function so it can accept a formatting string and variable number of arguments escapes me. These examples should be thorough, not cobbled-together excuses for good programming.

    The use of a single, global, MyServiceStatus requires that all the fields be guaranteed to be set correctly each time SetServiceStatus is called; using a function MySetServiceStatus that takes arguments and fills in a local status block each time, from scratch, completely, is far more robust and reliable.

    Vague comments like

    // Initialization code goes here

    should have a lot more explanation in the text, such as a discussion that there may be many SERVICE_START_PENDING notifications during the initialization, and that blocking calls with potentially unbounded wait times (including various kinds of I/O, network activities including DNS name resolution, and the like) are a Really Bad Idea.

    It should also point out that it is a common practice to create one or more threads using _beginthread or AfxBeginThread (as appropriate) whose responsibility is to implement the service.

    The whole discussion of RegisterWaitForSingleObject seems to be a red herring. A simpler approach is to simply CreateEvent and WaitForSingleObject with an INFINITE timeout, and using the SERVICE_STOP event to set this event.

    Why does the MyServiceInitialization have a DWORD return type, which is always 0, and return the error code via a DWORD * when it seems screamingly obvious that the correct and simple approach is to simply have the function call the (aforementioned missing) function that sends the status information?

    Why are the arguments used as meaningless expressions? (Sure, the idea is to avoid unreferenced parameter warnings, but didn't the author ever hear of the UNREFERENCED_PARAMETER macro? And if not the author, where was the adult supervision?)

    Vague comments like

    // This is where the service does its work

    should be replaced by meaningful explanations, such as

    // At this point, the service is running
    // If the entire service is implemented in this thread, this will have a loop that
    // will not terminate until the SERVICE_STOP notifications causes the thread to exit
    // Most commonly, what goes here is a WaitForSingleObject on an Event Object
    // which is set via SetEvent to allow this thread to proceed, shut down the 
    // worker threads that implement the service, and then return

    Overall, this is probably the worst example I have ever seen about how to write a service. Most Web sites, and reliable books like Reeves and Brain, give good examples.

    _wsplitpath

    See _splitpath

    _wsplitpath_s

    See _splitpath0

    .XMM (MASM)

    The online help page states, in apparent complete seriousness, "Enables assembly of Internet streaming SIMD extension instructions". Surprise. I had no idea that the instructions were Internet streaming instructions; they are generally referred to as "multimedia" streaming instructions, since the Internet has, as far as any of us can tell, no relevance whatsoever to this instruction set.


    I'm not the only one who sees this

    I'm not the only one who noticed that there are problems. Here's a really good description, reprinted with the permission of its author, Doug Harrison. I've done a bit of reformatting to use fonts and separate the various parts, but it is otherwise verbatim:


    On Mon, 13 Apr 2009 12:50:13 -0700, brander <brander@noreply.com> wrote:

    >The point being that I originally used what was suggested at http://msdn.microsoft.com/en-us/library/aa365740(VS.85).aspx (into an __int64). Post release, it was discovered that it wasn't working properly for large file sizes.

    >Which led to the 32/64 bit confusion and the post.

    Many MSDN examples are written in a language called "Fanciful". It's up to you to figure out their intent and translate them into a real language. Fanciful somewhat resembles C, and this makes things both easier and harder. It's easier because it seems familiar to you, and it's harder because it requires you to be familiar with many nuances of C and Windows programming. So, the only way to approach a Fanciful example is to assume that the design and implementation are both wrong, and that the example may not even remotely demonstrate what it is claimed to do. However, it's not all bad news. There is a web site called "Connect" that lets you report Fanciful examples to Microsoft, and they do fix them. Here's proof:

    Original article:
    http://msdn2.microsoft.com/en-us/library/8w5bsb4f(VS.80).aspx

    My feedback:
    https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=300428

    New article:
    http://msdn.microsoft.com/en-us/library/8w5bsb4f.aspx

    This is much better. They translated the original Fanciful example into the second language of MSDN examples, "Almost". An Almost program demonstrates what it is supposed to demonstrate, and it works under some conditions, perhaps even most conditions, but it still contains flaws that must not be copied. Here, the major flaw is the use of _beginthread. (The minor flaw is the almost ubiquitous failure to check return codes, which, ironically, may both save the example and ensure it doesn't get translated into the third and most rarely used language of MSDN examples, "Robust".) The problem is that _beginthread automatically closes the thread handle when the thread ends, similar to how a CWinThread object auto-deletes itself. This makes the subsequent use of WaitForMultipleObjects invalid, because (a) There is a race condition between the thread ending, the handle being closed, and WFMO being called, and (b) It is undefined for a handle to be closed while WFMO is waiting on it.

    In all seriousness, I think two things have been demonstrated, (a) The language (C or C++) is hard, and (b) The platform (Windows) is hard. It takes a fair amount of experience in both to combine the two and come up with something that truly works. MS does not have their most experienced people writing MSDN examples, so mistakes should be expected. There's just so much of it though, and some of it is so bad, that it's hard not to have a little fun with it from time to time. The lesson, however, is to report the issues you find on Connect. It does work:

    https://connect.microsoft.com/default.aspx

    --
    Doug Harrison
    Visual C++ MVP


    Change Log

    Date

    Count Description

    16-Dec-07

    48 First release

    17-Dec-07

    49 Added GetFullPathName
    19-Dec-07 50 Added CCheckListBox::GetCheck and CCheckListBox::SetCheck
    23-Dec-07 52 Added CreateWindowEx, updated CreateWindow, added LVCOLUMN
    29-Dec-07 55 Added note about OLE interfaces
    3-Jan-08 56 Added note about BITMAP
    11-Jan-08 59 Added note about DISP_E_EXCEPTION, DISP_E_TYPEMISMATCH, COleDispatchDriver::~COleDispatchDriver
    25-Jan-08 61 Added note that CSimpleStringT::GetBuffer documentation is incorrect, also CIEXYZ
    29-Jan-08 62 Added note on EMRTEXT
    31-Jan-08 69 Added note on EMRGRADIENTFILL, EMRMODIFYWORLDTRANSFORM, EMRPOLYPOLYGON, EMRPOLYPOLYGON16, EMRPOLYPOLYLINE, EMRPOLYPOLYLINE16, EMRSELECTOBJECT
    9-Feb-08 82 Added note on PALETTEENTRY, GradientFill, BITMAPINFO, CIEXYZTRIPLE, EMREOF, EMRSELECTPALETTE, EMRSETICMPROFILEA, EMRSETICMPROFILEW, EMRCOLORMATCHTOTARGET EMRPOLYDRAW, EMRPOLYDRAW16, CreateNamedPipe, DrawAnimatedRects, UNREFERENCED_PARAMETER
    10-Feb-08 84 Added note on UpdateAllViews, CArchive::Write
    11-Feb-08 85 Submitted by Mikel Luri Fendandez: CPathT::IsDirectory
    86 Submitted by Giovanni Dicanio: CFileDialog
    89 Added notes about FAR and PASCAL, and confusion of ClipCursor on Vista
    14-Feb-08 90 Added CImage::LoadFromResource
    15-Feb-08 92 Added CException::GetErrorMessage; revised CSimpleStringT::GetBuffer to indicate incoherent parameter description; added GetThreadTimes
    18-Feb-08 93 Added timeSetEvent
    3-Mar-08 99 Added SetTextCharExtra, CDC::SetTextCharExtra, Writing a Service Program's main function, Writing a ServiceMain function, Writing a Control Handler Function, Creating a Multithreaded Service
    4-Mar-08 100 Added ShellAbout
    8-Mar-08 103 Added PostMessage, SendMessage, PostThreadMessage
    10-Mar-08 106 Added strtok, wcstok, _tcstok
    12-Mar-08 107 Added GetDeviceCaps
      Added alpha-letter "fast index"
    108 Added /IGNORE:nnnn (linker)
    14-Mar-08 109 Added CDC::GetTextExtent
    15-Mar-08 110 Added LOGFONT
    25-Mar-08 112 Added CDC::SelectClipRgn, SelectClipRgn
    26-Mar-08 113 Added description of a problem using _USE_MATH_DEFINES.
    30-Mar-08 114 Added comment on CB_SETITEMDATA
    2-Apr-08 118 Added _ATL_VER, _MFC_VER , _MSC_VER, NTDDI_VERSION
    5-Apr-08 119 Added CreateFont
    8-Apr-08 130 Added __COUNTER__, __FUNCDNAME__, __FUNCSIG__, __FUNCTION__, __FILE__, _CPPLIB_VER, _MSVC_RUNTIME_CHECKS, _DEBUG, _MT, _DLL, GetVersionEx
    21-Apr-08 131 Added reference to my essay on _CrtMemCheckpoint.
    23-Apr-08 133 Added discussion of Enable3dControls, Enable3DControlsStatic
    25-Apr-08 134 Added CListBox::InitStorage (submitted by Robert Zimmerman)
    27-Apr-08 138 Added CDC::SetGraphicsMode, CDC::SetWorldTransform, CDC::ModifyWorldTransform, CMutex
    28-Apr-08 140 Added GetCharacterPlacement, GCP_RESULTS
    1-May-08 142 Added LOGBRUSH, SetLayout
    4-May-08 144 Added GetCharABCWidths, SetGraphicsMode, added to CDC::SetGraphicsMode
    6-May-08 146 Added SetScrollRange, SetScrollInfo
    15-May-08 147 Added discussion about the failure to properly specify GetStyle and ModifyStyle in the article on "Tree Control Styles"
    22-May-08 148 Added CDocument::AddView
    25-May-08 149 Added CImage::BitBlt
    26-May-08 151 Added CDC::DPtoLP and DPtoLP
    27-May-08 152 Added NETRESOURCE
    23-Jun-08 154 Added HeapCompact and _heapmin
    24-Jun-08 162 Added CCriticalSection; also added more information to CMutex; added discussion of the CSingleLock Class, CSingleLock::CSingleLock, CSingleLock::~CSingleLock, CSingleLock::Lock, CSingleLock::Unlock, CSemaphore, CEvent
    25-Jun-08 163 Added IsCharSpace
    27-Jun-08 164 Added discussion of errors in SHBrowseForFolder and observe that these errors apply to all discussions of PIDLs
    5-Jul-08 165 Added discussion of CImage::Load.
    10-Jul-08   Added some more information to CreateNamedPipe
    166 Added CHtmlEditCtrlBase::QueryStatus
    11-Jul-08 167 Added CToolBar::GetItemID
    16-Jul-08 168 Added SetThreadNameInfo discussion.
    17-Jul-08 169 Added DrawText/DrawTextEx discussion.
    23-Jul-08 170 Added CTime
    25-Jul-08 173 Added discussion of ShellExecute, ShellExecuteEx, SHELLEXECUTEINFO
    27-Jul-08 175 Added GetImageEncoders, ImageCodecInfo
    30-Jul-08 179 Added AVIStreamSetFormat, AVIMakeCompressedStream, Bitmap::FromHBITMAP, Gdip*  
    31-Jul-08 181 Added LOGPALETTE, CreateDIBSection
    3-Aug-08 182 Added GetTokenInformation
    4-Aug-08 184 Added GetClipboardData, mmioOpen
      I was sent an interesting piece of history about the BITMAP, which I've included with the permission of the author.
    5-Aug-08   Changes to GetTokenInformation based on comments by Alf P. Steinbach, Giovanni Dicanio, and Doug Harrison
    6-Aug-08 189 Added discussion of CStdioFile::ReadString and CStdioFile::WriteString, CStdioFile::CStdioFile, CFile::CFile
    7-Aug-08 192 Added a discussion of NMTREEVIEW, TVN_SELCHANGING, TVN_SINGLEEXPAND
    8-Aug-08 193 Added discussion of BITMAPINFOHEADER
    21-Aug-08 202 Added discussion of /ANALYZE in VS2008. Added AVIIF_TWOCC, and the corresponding remark in AVIOLDINDEXENTRY. Added AVIPALETTEENTRY, problems caused by the erroneous interacting definitions in AVIRIFF.h and vfw.h, such as the duplicate symbol problem with AVIIF_KEYFRAME and AVIIF_LIST. Documented the strange representation of AVISTREAMHEADER in a RIFF file.
      Added more information to the BITMAP and BITMAPINFOHEADER discussions.
    213 Added CBitmap::GetBitmapBits, CFile::Seek, CImageList::Create, GetDIBits, ICQUALITY_DEFAULT, MsgWaitForMultipleObjectsEx, TVITEM, WaitForMultipleObjects, WaitForMultipleObjectsEx, WaitForSingleObject, WaitForSingleObjectEx
    216 Added discussion of CFile::modeRead, CFile::modeWrite and CFile::modeReadWrite and the general lack of quality of the documentation of these options
    24-Aug-08 221 Added discussion to the CFindReplaceDialog overview, CFindReplaceDialog::Create, CFindReplaceDialog::FindNext, CFindReplaceDialog::IsTerminating, MessageBeep
    28-Aug-08 Added additional discussion about the parameter type to the DrawText/DrawTextEx discussion. (Thanks to Giovanni Dicanio for pointing out an ongoing discussion in another newsgroup)
    29-Aug-08 224 Added discussion of the CStringT class members page, also ICImageCompress, ICImageDecompress
    30-Aug-08 231 Added discussion of ICOpen, ICClose, ICCompress, ICCompressBegin, CSpinButtonCtrl, CWnd::OnHScroll, CWnd::OnVScroll
    4-Sep-08 233 Added discussion of the absurdity of having weird names like CFont::CreateFont and CFont::CreateFontIndirect when CFont::Create is all that is required. Makes me wonder of the MFC designers had really understood the concept of "overloading"
      Added more to the discussion of LOGFONT, like why one index entry goes to a completely useless page and another goes to an API, CreateFontIndirect, and why there are inadequate See Also links
    234 The LVS_EX_* symbols do not appear as properties, nor do they appear in the index. Searching for them is the only way to find them.
    5-Sep-08 238 Added discussion of NM_DBLCLK, NMITEMACTIVATE, CListCtrl::SortItems, Class View
    9-Sep-08 243 Discuss serious defect in _beginthread example code, lack of any useful information on  InitializeCriticalSectionAndSpinCount; added discussions of CWnd::SetFont, CWnd::GetFont, CWnd::OnSetCursor
    15-Sep-08 244 Show that the example in the C/C++ documentation for union initializers is incorrect as written and silly if corrected.
    16-Sep-08 245 While not strictly an MSDN documentation error, there is a documentation error in the Excel help which should be recorded; I was using Excel to float some trial computations before committing to real C/C++ code. COS (Excel) has a serious (but obvious) error.
    19-Sep-08 246 Contributed from microsoft.public.vc.mfc: KB166129: How to programmatically print to a non-default printer in MFC. Thanks to Charlie Brown for the posting.
    24-Sep-08 Added yet another annotation to CSimpleStringT::GetBuffer. Exactly what exception does it throw?
    27-Sep-8 247 Clarified MessageBox documentation.
    13-Oct-08 248 Correspondent Jim Beveridge (author of Multithreading Applications in Win32) submitted new items or additions to existing items: CSimpleStringT::ReleaseBuffer, CWnd::CalcWindowRect, EXPORTS, GradientFill
    250 Added descriptions of discrepancy in different releases for CFont::CreatePointFont, CFont::CreatePointFontIndirect
    15-Oct-08 251 Added discussion of the error C2248: CObject::CObject
    19-Oct-08 252 Added simple and clear, readable, documentation on ON_CONTROL_REFLECT_EX; need to add more on other reflection handlers
    20-Oct-08   Updated information on _ATL_VER and _MSC_VER (thanks to David and Giovanni)
    21-Oct-08   Further update to _ATL_VER and an update to _MFC_VER (thanks to David Scambler)
    22-Oct-08 253 Added a discussion of ExtTextOut
    31-Oct-08 254 Added a discussion of CFontDialog::GetCurrentFont
    255 Added discussion of CHOOSEFONT omissions
    1-Nov-08 256 Added discussion of erroneous error message from CFontDialog::DoModal and other problems, which also exist in the ChooseFont API
    257 Why is there no CWnd::DeferWindowPos?
    8-Nov-08 258 Added note about CStringT::CStringT being able to do an implicit LoadString.
    259 Added discussion of StringCchCopy
    18-Nov-08 265 Added note about failed cross-references on CDC::GetCurrentBitmap, CDC::GetCurrentBrush, CDC::GetCurrentFont, CDC::GetCurrentObject, CDC::GetCurrentPalette, CDC::GetCurrentPen
    20-Nov-08 268 Added discussion of the fundamental design error in ftell; added discussion of the effects of this bug on CStdioFile::GetLength and CStdioFile::GetPosition
    24-Nov-08 269 Added discussion of QueryDosDevice
    27-Nov-08 270 (DDK) Added discussion of a documentation error in IoCreateDevice. Thanks to Don Burn for verifying this is an error.
    28-Nov-08 273 Added discussion of missing cross-references in CWnd::Invalidate, CWnd::InvalidateRect, and CWnd::InvalidateRgn
    276 Added discussion of missing cross-references in HDN_BEGINTRACK, HDN_ENDTRACK, HDR_TRACK
    1-Dec-08 277 Added discussion about problems with CreateDC.
    2-Dec-08 278 The documentation of CButton::GetState is slovenly.
    3-Dec-08 279 There is an omission of several critical details in the discussion of GetWindowThreadProcessId.
    13-Dec-08 280 Added comments on serious errors in WM_DELETEITEM documentation and in the implementation itself
    14-Dec-08 281 Added comments about serious omissions in WM_COMPAREITEM documentation
    286 Added comments missing about LB_ADDSTRING, LB_INSERTSTRING, CB_ADDSTRING, CB_INSERTSTRING, and WM_MEASUREITEM
    18-Dec-08 288 Added definition of FIXED which is otherwise undocumented, and MAT2, which uses it.
    26-Dec-08 292 Added discussion of HWND_MESSAGE, its effect on GetClassName, observations on EnumChildWindows and in particular the interaction with HWND_DESKTOP. A Wave of the Flounder Fin to correspondent Kero for this information.
    29-Dec-08 296 Added discussion of ShowWindow and WinMain. Also omissions in the description of STARTUPINFO and STARTUPINFOEX
    6-Jan-09 319 Added information about the error in SQLInstallODBC, SQLCreateDataSource, SQLConfigDataSource, SQLConfigDriver, SQLGetAvailableDrivers, SQLGetInstalledDrivers, SQLGetPrivateProfileString, SQLGetTranslator, SQLInstallDriver, SQLInstallDriverEx, SQLInstallDriverManager, SQLInstallerError, SQLInstallTranslator, SQLInstallTranslatorEx, SQLPostInstallerError, SQLReadFileDSN, SQLRemoveDriver, SQLRemoveDSNFromIni, SQLRemoveTranslator, SQLValidDSN, SQLWriteDSNToIni, SQLWriteFileDSN, SQLWritePrivateProfileString documentation.
    320 Added note about RegisterWindowMessage being unique within desktops
    322 Added note about the use of RegisterWindowMessage with BroadcastSystemMessage, BroadcastSystemMessageEx
    11-Jan-09 324 Added notations about WM_RBUTTONDOWN, WM_RBUTTONUP
    326 Added entries for KernelIoControl, OEMIoControl; a Wave of the Flounder Fin to Reiß for submitting these
    16-Jan-09 328 Added discussion of the problems with EM_GETCUEBANNER and EM_SETCUEBANNER
    17-Jan-09 329 Added note on missing description of CEdit::GetCueBanner. A Wave of the Flounder Fin to Giovanni Dicanio for pointing this out.
    20-Jan-09 330 Added documentation for DEFINE_GUID that is actually useful to anyone who wants to define a GUID.
    22-Jan-09 332 Added discussion of CDialog::Create and CDialog::CreateIndirect. Noted serious errors in documentation
    336 Added discussion of CreateDialog, CreateDialogIndirect, CreateDialogIndirectParam, CreateDialogParam and noted serious errors in the documentation
    23-Jan-09 337 Added note about missing hyperlink in PAGESETUPDLG description.
    338 Pointed out that a long-standing serious documentation error I reported in 1996 still exists for the PagePaintHook function
    344 Documented slovenly description of the LPARAM of WM_PSD_ENVSTAMPRECT, WM_PSD_FULLPAGERECT, WM_PSD_GREEKTEXTRECT, WM_PSD_MINMARGINRECT, WM_PSD_MARGINRECT and WM_PSD_YAFULLPAGERECT
    345 Added note concerning RealGetWindowClass and comparing it to GetClassName. Another Wave of the Flounder Fin to Kero.
    5-Feb-09 346 Added note concerning TCITEM and its poor documentation.
    350 Added note that CListBox::SetSel, CListBox::GetCurSel, CListBox::GetSelItems and CListBox::SetCurSel are all missing important hyperlinks. Also commented on universally poor examples presented in many descriptions.
    6-Feb-09 351 Added note about erroneous documentation of ON_MESSAGE macro
    8-Feb-09 352 Added information about proper use of map::erase in loops. A Wave of the Flounder Fin to Doug Harrison for this information.
    12-Feb-09   Added additional information about the documentation failures of SetScrollInfo.
    14-Feb-09   Added additional information about the error C2248: CObject::CObject trying to access a protected assignment operator, caused by using an MFC collection class
    7-Mar-09 353 Noted that CDC:DrawIconEx does not exist, and should.
    8-Mar-09 354 Pointed out the complete uselessness of the entry for VARARGS.H.
    9-Mar-09 355 Pointed out documentation failures for RpcNsBindingLookupBegin
    10-Mar-09 356 Pointed out documentation failures for RpcServerRegisterIf
    12-Mar-09 357 Pointed out documentation failure for NMTTDISPINFO.
    358 Pointed out documentation failures in the article ToolTip Control Reference.
    22-Mar-09 359 Pointed out documentation failures for IEnumVARIANT.
    360 Pointed out incredibly serious documentation failures for _beginthread, _beginthreadex.
    2-Apr-09   Added commentary to the discussion of CListBox::GetSelItems because it uses a ridiculous parameter name for the count and refers to a dead concept, "long pointer"
      Added discussion to CListBox::InitStorage because it is ill-defined what happens. The use of randomly-selected numbers like 256 and 10 without rationale is also poor documentation style.
    4-Apr-09 361 Horrid example of coding practice given in the example for GetNumaProcessorNode gets the put-down it deserves.
    362 Major documentation failure of GetNumaProximityNode
    363 Horrid example of coding practice given in the description of GetLogicalProcessorInformation
    364 Incredibly sloppy documentation for AllocateUserPhysicalPagesNuma does not even include hyperlinks to related material
    365 Horrid example of coding practice in VirtualAllocExNuma gets the treatment it deserves
    15-Apr-09   Added a really funny and dead-on accurate note from Doug Harrison
    20-Apr-09 366 Added discussion of the failures of documentation of EnterCriticalSection.
    367 Added a discussion of the failures of documentation of SetCriticalSectionSpinCount
    22-Apr-09 368 Yet another example of amateur (and really poor) C programming, in Doug's "Fanciful" language, gets a rewrite for the GetLogonSid subroutine example.
    23-Apr-09 369 Added information to help explain how to use GetModuleFileName.
    24-Apr-09 372 Pointed out missing hyperlinks in VirtualAllocEx, ReadProcessMemory, and WriteProcessMemory
    28-Apr-09 373 Noted inadequate documentation of the BROWSEINFO structure.
    376 Added discussion of failure caused by changing the return type of the ON_WM_NCHITTEST handler CWnd::OnNcHitTest. A workaround is shown. Also made a note about the error message it causes, C2440: OnNcHitTest
    29-Apr-09 377 Added note that WindowFromPoint does not specify that the Point parameter should be in screen coordinates
    30-Apr-09   Added note about error in WM_NCHITTEST. Thanks to Kero for pointing this out
    1-May-09   Added more details to the documentation errors of _beginthread
      Upon looking in detail at the _beginthread example code it has so many deep problems that it needed a serious rewrite to be remotely acceptable as an example of multithreaded programming.
    2-May-09 379 Observe that the documentation of CWinApp::InitInstance and CWinApp::ExitInstance are hopelessly outdated, irrelevant, confusing, misleading, and outright erroneous. Apparently this documentation has not been looked at since it was written from MFC 1.0 on 16-bit Windows.
    381 Added documentation and clarified issues for CWinThread class and CWinThread::InitInstance
    7-May-09 382 Found serious undocumented behavior of CWnd::PreSubclassWindow (thanks to MVP AliR for supplying an example case!)
    18-May-09 383 Numerous serious problems with the description of CreateService.
    19-May-09 384 SetWindowText will silently truncate the text when it is setting an MDI window caption. This failure to work correctly is undocumented.
    20-May-09 385 DisableThreadLibraryCalls documentation is confusing and misleading.
    25-May-09 386 Meaningless statement in _vsnprintf, _vsnwprintf documentation must be clarified.
    26-May-09 390 There is some meaningless gibberish which pretends to be documentation describing nonexistent API calls FreeHeap, AllocateHeap and RegisterCallback. The definition of the Client Callback Function Prototype Callback Function for RegisterCallback also has errors. The documentation is confusing, misleading, and erroneous.
    391 The description of one of the values of SECURITY_IMPERSONATION_LEVEL contains completely nonsensical gibberish.
    3-Jul-09 392 Added clarification to AfxThrowInvalidArgException. This probably applies to all the AfxThrow...Exception calls.
    18-Jul-09 396 Added descriptions about inadequacies of CWnd::OnCtlColor, WM_CTLCOLORBTN, WM_CTLCOLOREDIT and WM_CTLCOLORSTATIC
    21-Jul-09 399 Failure about starting threads in DllMain appears in Terminating a Process; inadequate documentation in DllMain. Documented failure in CreateThread documentation.
    404 Failures of documentation of AcquireSRWLockExclusive, AcquireSRWLockShared, InitializeSRWLock, ReleaseSRWLockExclusive, and ReleaseSRWLockShared
    22-Jul-09 405 Comments on incomprehensible SleepConditionVariableSRW
    406 Pointed out inadequacy of both the implementation and the documentation for EncodePointer.
    29-Jul-09 407 Documentation of the callback hook function for _CrtSetAllocHook is erroneous.
    31-Jul-09   Fixed error in description of CWnd::OnNcHitTest; had previously attributed the incompatible change to VS2008, but the change actually occurred in VS2005. Thanks to MLuri for the correction.
    2-Aug-09 408 Example for __noop is potentially confusing and does not reflect current best practice for conditional compilation.
      Put subindex in for C
    17-Aug-09 409 Added notes that WM_NCHITTEST and CWnd::OnNcHitTest do not work properly. Thanks to Giovanni DiCanio for his work in discovering this anomaly.
       410 Discuss problems with CWnd::OnDeviceChange.
    19-Aug-09 411 The description of memcpy_s has problems.
    20-Aug-09 413 Pointed out errors in UuidFromString and UuidToString
    414 Missing information on SetupDiGetDeviceInterfaceDetail
    415 Confusing information in RegisterDeviceNotification
    417 Pointed out serious oversights in the documentation of ASSERT and VERIFY, both of which represent serious bugs in their implementation.
    21-Aug-09 421 Confusing and misleading documentation for _bstr_t::Attach, _bstr_t::Assign, _bstr_t::GetAddress and _bstr_t::GetBSTR
    22-Aug-09 422 Created a link to Michael Kaplan's blog on the undocumented Unicode-file-format feature of WritePrivateProfileString.
    5-Sep-09 423 The PUBLIC directive for MASM lacks critical hyperlinks
    424 The description of the MASM option /Zi refers to "Generates CodeView information" as if a modern reader would have a clue as to what "CodeView" was.
    10-Sep-09 425 Problems with the documentation of the extern keyword in C++
    21-Sep-09 426 Pointed out several egregious errors in the description of CFileDialog::CFileDialog.
    427 Pointed out that BASED_CODE, which correctly claims to be obsolete, must be expunged from all examples.
    428 There seems to be some problems with large bitmaps (> 2GB) under Win64.
    30-Sep-09   Added more information about mmioOpen
    429 Added information about HMONITOR
    15-Oct-09 430 Observe that the code for handling WM_DRAWCLIPBOARD was incorrect and amateurish when first written, and it has not been reviewed or revised for Best Practice in nearly 25 years!
    431 The documentation for OpenClipboard is erroneous; it suggests that GetLastError will return useful information. A tip of the Flounder Fin to "boom333" for pointing this out.
    21-Oct-09 432 Point out serious error in definition of DeviceIoControl.
    28-Oct-09 433 Point out oversight in WM_POWERBROADCAST documentation.
    31-Oct-09 434 Documentation of scanf_s is erroneous.
    6-Nov-09 440 Many errors and omissions about the documentation of Microsoft Macro Assembler, including basic rules of syntax, Operator +, Operator -, syntax of identifiers, syntax of hexadecimal constants, and details of the OPTION directive.
    443 Dmitry Kovalenko submitted a report that ITypeInfo::ReleaseFuncDesc, ITypeInfo::ReleaseTypeAttr and ITypeInfo::ReleaseVarDesc are documented with the wrong return type.
    8-Nov-09 447 The DB, DD, DQ, and DW MASM documentation represent sad jokes on the reader.
    9-Nov-09 451 The .DATA, .DATA?, .CODE and .CONST MASMs have gibberish for documentation.
    460 The MASM documentation for BYTE, SBYTE, TBYTE, WORD, SWORD, QWORD, DWORD and SDWORD is inadequate. The documentation for SQWORD is incorrect. The DT documentation is another sad joke. If I want bad humor, I'll read a bad humorist. I expect professional documentation to be detailed and correct.
    10-Nov-09 462 Say something about the syntax of string values and the syntax of initializers in MASM.
    14-Nov-09 463 Tried to infer what STRUCT (MASM) does and how to use it
    30-Nov-09 464 Added discussion of problems of #pragma optimize documentation.
    465 Observe that the cl /QIPF_ documentation is incoherent, confusing, and possibly obsolete.
    8-Dec-09 466 Documentation of RegGetValue does not suggest that REG_EXPAND_SZ strings are expanded.
    9-Dec-09 467 KB104111 has a serious error
    10-Dec-09 468 There is a serious error in the declaration of CHOOSECOLOR and the explanation makes no sense at all.
    17-Dec-09 469 The documentation of .FPO in the Microsoft assembler is confusing
    18-Dec-09 471 Failures of documentation of Microsoft Macro Assembler OPATTR and .TYPE. In other contexts this would be seen as humorous, as in recusrive (adj.) see recursive, but in documenting software which is supposed to be used, it isn't so funny. So far, this is now the first-place winner in The Worst Possible MASM Documentation contest.
    472 Strange designation of the .XMM directive, referring to the instructions it enables. Internet streaming SIMD? There was only a very brief period when this name was in vogue, and obviously the designers realized this was a remarkably stupid name and immediately changed it. It is now known as SSE.
    20-Dec-09 473 The documentation for EXTERN in the Microsoft Macro Assembler exhibits another low in documentation quality Not as bad as OPATTR and .TYPE, but a strong contender for a second-place entry.
    21-Dec-09 474 Just when you think it can't possibly get worse: PROTO (MASM). Like much of the other documentation for MASM, it appears to be a placeholder for the documentation that will be written Real Soon Now.
    475 LOCAL (MASM) seems to be perhaps 10% documented, which makes it infinitely more documented than PROTO, OPATTRand .TYPE, which are 0% documented.
    11-Jan-10 477 Serious, serious failures in the mciSendCommand API. Not only is the documentation obsolete; the API cannot possibly work correctly for MCI_STATUS:MCI_STATUS_LENGTH in a 32-bit file system (it assumes that the longest file length will fit into 32 bits!) This fundamental design flaw is not documented.
    14-Jan-10 479 Observe that the documentation of the MASM @Cpu and @Interface variables is an insult to intelligent readers everywhere. It is almost as if the goal is to annoy the end users.
    481 The documentation of ECHO and %OUT (MASM) is written so that each is defined in terms of the other, but the definitions are inconsistent.
    17-Jan-10   With the recent demise of www.geocities.com, some links were broken. A special Wave Of The Flounder Fin to Kero for sending me the updated links at www.geocities.ws.
    21-Jan-10 482 Documented two minor points of the TYPE operator in MASM. Another example of complete and total irresponsbility in documentation.
    17-Jun-11 483 Documented an error in CSettingsStore::CSettingsStore. Thanks to Mikel Luri for submitting this one, including showing the source code which contradicts the documentation.
     

    [Dividing Line Image]

    The views expressed in these essays are those of the author, and in no way represent, nor are they endorsed by, Microsoft.

    Send mail to newcomer@flounder.com with questions or comments about this web site.
    Copyright © 2007-2010 FlounderCraft Ltd./The Joseph M. Newcomer Co. All Rights Reserved
    Last modified: July 05, 2011