Undocumented Windows Release Notes -- v. 1.00 (June 15, 1992)
-------------------------------------------------------------

    This is the READ.ME file for the disk to accompany the book
_Undocumented Windows: A Programmer's Guide to Reserved Windows API
Functions_ by Andrew Schulman, David Maxey, and Matt Pietrek
(Addison-Wesley, 1992, ISBN 0-201-60834-0, 700 pages, $39.95).

    This file describes the example programs on the disk, and includes
last-minute corrections and addenda to the book:

        1. INCLUDES SOURCE!
        2. Contents of the disk
        3. Building the example programs
        4. Microsoft C/C++ 7.0 instructions
        5. Non-WINIO programs
        6. Differences between book and disk
        7. Corrections to Chapter 5: KERNEL
        8. Corrections to Chapter 6: USER
        9. Corrections to Chapter 8: GDI

    The programs on the disk were tested using:

        Windows 3.0 Standard and Enhanced mode, retail and debug
        Windows 3.1 Standard and Enhanced mode, retail and debug

    Some of the programs were also tested under WIN-OS/2 (Windows 3.0
Standard mode running on OS/2 2.0).

    None of the programs will run under Windows 3.0 Real mode.  All
the programs require protected-mode Windows.

    The following compilers were used:

        Borland C++ 3.0
        Microsoft C 6.0
        Microsoft C/C++ 7.0
    
    Some of the programs on disk contains corrections to the versions
printed in the book.  If you have found a problem in a program
printed in the book, please check this file and the contents of this
disk first.

    If you do find any problems in the book or the disk, or have any
questions or suggestions, please contact Andrew Schulman:

        CompuServe (CIS): 76320,302
        via Internet:     76320.302@compuserve.com

    The authors can also be reached via mail c/o Addison-Wesley, but
electronic mail is greatly prefered and will receive a response,
whereas paper mail may or may not receive a response:

        Undocumented Windows
        c/o Addison-Wesley Publishing Co.
        Route 128
        Reading MA 01867



1. INCLUDES SOURCE!
   ----------------

    In several places, the book says that no source is included for
the WINIO library, or for the Windows programs in Chapter 4, because
there's no room.  We forgot we were using PKZIP, and so have plenty
of room!  SOURCE.ZIP contains the source files for:
        
        WINIO library (winio.c, wmhandlr.c, argcargv.c, winheap.c, based.c;
            winheap.c and based.c aren't really used in this book but
            they are part of the library)
        SNOOP (snoop.c, i3handlr.asm, snoop.def)
        WISPY (wispy.c, wispy.def)
        CALLFUNC (callfunc.c)
        EXEUTIL (exeutil.awk)

    Actually, what we meant by "no room" is that there's no room in
the book to _explain_ the source.  Right now, you're on your own with
SOURCE.ZIP.  We'll be improving it considerably, adding comments, and
explaining it in detail in a forthcoming book, _The DOS Programmer's
Guide to Windows_.  There will be more utilities in another
forthcoming book, titled something like _Son of Undocumented Windows_
or _Dirty Windows_.  (Hey, if you can come up with a better title,
let us know!)

    Use this source code for own experiments.  Like everything else
on this disk, all this material is copyright of the authors.  If you
are interested in redistributing the source, you _must_ contact the
authors at the one of the addresses above.  Of course, you are
invited to incorporate small pieces of code from the book and/or disk
into your program, in binary form.  


2. Contents of the disk
   --------------------

    The example source code and utility programs for _Undocumented
Windows_ are stored on this disk in .ZIP files.  Each .ZIP file
contains the material for one chapter in the book, with the exception
of WINIO.ZIP which contains the WINIO library used throughout.
PKUNZIP (licensed from PKWARE) is included on the disk to unzip the
files.  

    INSTALL.BAT will set up everything in a new directory,
C:\UNDOCWIN, with the following subdirectory structure:

        \UNDOCWIN
            \CHAP1
            \CHAP2
            ... etc...
            \WINIO
                BC
                MSC6
                MSC7
            \SOURCE

    This requires about two megabytes of free disk space.

    To copy only selected files, or to install elsewhere than
C:\UNDOCWIN, edit INSTALL.BAT (hey, this is a book for programmers;
we figure you can edit a batch file).  PKUNZIP can be used to
extract selected files from any of the .ZIPs.

    The disk contains the following types of files:

    o   General-purpose .H include files such as chap5\handles.h,
        chap6\userobj.h, and chap8\gdiobj.h.  All files have been
        treated with extern "C", so they should work with C++ as
        well as with C.

    o   Small-model and medium-model .LIB files for using WINIO
        with Borland C++ 3.0 (should work with 3.1 too), Microsoft
        C 6.0, and Microsoft C/C++ 7.0.  

    o   .C files for all the sample programs.

    o   .EXE files for utility programs discussed in Chapters 2-4,
        and for the more useful of the sample programs from Chapters
        5-10.  For example, there is ATOMWALK.EXE because that's a
        useful program in its own right, but no GP.EXE because it isn't.


    The disk contains the following files:

read.me         this file
pkunzip.exe     PKWARE's unzip utility
install.bat     user-unfriendly install
chap1.zip       example programs from chapter 1:
                    freeres.c
                    freeres.exe
chap2.zip       utilities and data files from chapter 2:
                    mapwin.exe
                    winfunc.imp
                    win30.imp
                    exedump.exe
                    exeutil.exe
                    winfunc.dat
                    win30.dat
                    win21.dat
                    undocwin.dat
chap3.zip       utilities from chapter 3:
                    resdump.exe
                    extract.exe
chap4.zip       utilities and WINIO examples from chapter 4:
                    callfunc.exe
                    wispy.exe
                    snoop.exe
                    wm_undoc.dat
                    wm_all.dat
                    sh.c
                    sh.exe
                    basemov2.c
                    basemov2.exe
                    taskwlk2.c
                    taskwlk2.exe
                    numtasks.c
                    numtasks.exe
chap5.zip       KERNEL examples from chapter 5:
                    handles.h
                    handles.c
                    testhand.c
                    testhand.exe
                    taskdb.h
                    taskq.h
                    modtable.h
                    psp.h
                    wintask.c
                    wintask.exe
                    winmod.c
                    winmod.exe
                    stealcdr.c
                    stealcdr.exe
                    atomwalk.c
                    atomwalk.exe
                    taskwalk.c
                    taskwalk.exe
                    modwalk.c
                    modwalk.exe
                    basemove.c
                    basemove.exe
                    countmem.c
                    countmem.exe
                    nullseg.c
                    nullseg.exe
                    local.c
                    local.exe
                    kill.c
                    kill.exe
                    gp.c
                    setsbase.c
                    semtest.c
                    thhook.c
                    taskq.c
                    sellimit.c
                    systres.c
                    exeptr.c
                    meminfo.c
                    walksegs.c
                    break.c
                    selarray.c
                    codedat2.c
                    codedata.c
chap6.zip       USER examples from chapter 6:
                    userobj.h
                    userwalk.c
                    userwalk.exe
                    dragdrop.h
                    ddclient.c
                    ddclient.exe
                    ddserver.c
                    ddserver.exe
                    cursicon.h
                    bcstmsg.c
                    cascchld.c
                    checkord.c
                    ciconind.c
                    cpnlinfo.c
                    dtpattrn.c
                    dtwlpapr.c
                    dumpicon.c
                    endmenu.c
                    fillwind.c
                    fprtname.c
                    getnextq.c
                    gridgran.c
                    gtmsevnt.c
                    intwpos.c
                    kbdevent.c
                    oemlayer.c
                    paintrct.c
                    evnthook.asm
                    setehook.c
                    snapwind.c
                    stsystmr.c
                    swtothis.c
                    syserrbx.c
                    tilechld.c
                    xcstods.c
chap7           ** Not present ** Don't be alarmed - there are no
                utilities or source presented in the Undocumented
                Windows Messages chapter.
chap8.zip       GDI examples from chapter 8:
                    gdiobj.h
                    gdiwalk.c
                    gdiwalk.exe
                    checkord.c
                    rsin.c
                    fastwfrm.c
chap9.zip       SYSTEM example from chapter 9:
                    systimer.c
chap10.zip      TOOLHELP examples from chapter 10:
                    winwalk.c
                    winwalk.exe
                    th_asm.asm
                    coroner.c
                    coroner.def
                    coroner.mak
                    coroner.rc
                    coroner.ico
                    xcptrept.c
                    coroner.exe
                    coroner.h
winiobc.zip     WINIO libraries, header files, etc. for Borland C++:
                    winio.h
                    wmhandlr.h
                    swindos.lib
                    mwindos.lib
                    windos.def
                    windos.ico
                    windos.rc
                    windos.res
                    winioapp.mak
                    winiobc.bat
winioms6.zip    WINIO libraries, header files, etc. for Microsoft C 6.0:
                    winio.h
                    wmhandlr.h
                    swindos.lib
                    mwindos.lib
                    windos.def
                    windos.res
                    windos.ico
                    windos.rc
                    winioapp.nmk
                    winioms.bat
winioms7.zip    WINIO libraries, header files, etc. for Microsoft C/C++ 7.0:
                    winio.h
                    wmhandlr.h
                    argcargv.c
                    swindos.lib
                    mwindos.lib
                    windos.def
                    windos.res
                    windos.ico
                    windos.rc
                    winioapp.nmk
                    winioms.bat
source.zip      Source code for WINIO, SNOOP, WISPY, CALLFUNC, EXEUTIL:
                    winio.c
                    wmhandlr.c
                    argcargv.c
                    based.c
                    winheap.c
                    callfunc.c
                    wispy.c
                    wispy.def
                    snoop.c
                    snoop.def
                    i3handlr.asm


3. Building the example programs
   -----------------------------

    Tools are provided to make the building of the example programs
provided on the disk, and programs that you write yourself, as easy
as possible while still leaving open some avenues for customization.

    Makefiles are provided for Microsoft C 6.00/7.00 and Borland C++
3.0. The WINIOMS.BAT file builds a WINIO program using MS C 6.00 via
NMK and the WINIOAPP.NMK makefile (there are different versions for
Microsoft C 6.0 and Microsoft C/C++ 7.0; see below), and WINIOBC.BAT
builds a WINIO program using Borland C++ 3.0 via MAKE and
WINIOAPP.MAK. Usage is the same for all batch files:

        WINIOxx app-name [M] [extra-files]

where: M specifies build using medium model, rather than small
       extra-files is an optional list of additional component object
            files required to build the program. The object files
            should be specified without the .OBJ extension. 

    To build the DumpIcon example in Chapter 6 using Microsoft C
6.00, for example, you only need to run:

        WINIOMS DUMPICON

    The resultant small model executable will include debug symbols,
and will have been linked using the default definitions file
WINDOS.DEF.  It will also inlude the default WINIO resource file
WINDOS.RES, which contains only the class icon for WINIO
applications.

    Specifying M on the command line after the application name will
cause it to be built using medium model. None of the example programs
on the disk require medium model.

    To build the example program in the SetEventHook entry in Chapter
6, in which the assembler file EVNTHOOK.ASM is an additional
component, using Borland tools, you would type:

        WINIOBC SETEHOOK EVNTHOOK 

    You can modify the batch files to generate executables without
debug symbols, or you can use a utility such as Borland's TDSTRIP.EXE
that will remove the symbol table from an executable. The utility
program executables on the disk do not contain symbol tables.
However, all the WINIO libraries do.

    If you require additional resources within the RES file, or just
wish to use a custom icon for a particular program, create a resource
file with the same name as the program, with a .RES extension. It
will be found and used automatically by the build process. Ensure
that your resource file contains an icon resource with the name
WINIO_ICON, since this will be loaded as the WINIO class icon.

    In the same way, if you need to allocate more stack space for a
particular program, or change segment descriptions, or just give it a
specific module name, create a definitions file with the same name as
the program, with a .DEF extension. In the same way as for the
resource file, it will be found and used automatically by the build
process. Ensure that your definitions file contains the entry
WMHANDLER_WNDPROC in the EXPORTS section.

    Note that the makefile do not mention either COMMDLG.LIB, used by
WINIO when processing the &File | &Save buffer... menu option, or
TOOLHELP.LIB, used by a number of the sample programs. Both of these
libraries have been integrated into the version of the WINIO
libraries that appear on the disk.

    Note also that the makefiles assume a flat directory structure.
They must be edited if the WINDOS libraries and/or header files are
to be kept in a separate directory from the program source files.

    If you don't want to use this batch/makefile mechanism, you can
follow the instructions for building WINIO programs that appear in
Appendix A.  It is much simpler to build these (or any!) Windows
programs with Borland C++ than with Microsoft C.  With Borland C++,
all you need is:

        bcc -WS -DWINIO -2 foo.c swindos.lib
        rc -t windos.res foo.exe

    (The rc -t flag was accidentally dropped from the command line
shown in Appendix A of the book.  This flag makes the executable
protected-mode ONLY, so you don't have to worry about someone trying
to run the program in 3.0 real mode.)

    With Microsoft C 6.0 or Microsoft C/C++ 7.0, you will need to
link separately.  Using the WINDOS.DEF file on the disk (note that
some of the many cl switches shown here were omitted from the version
printed in Appendix A of the book):

        cl -G2sw -Oais -Zpe -c foo.c
        link /nod foo,,,swindos slibcew libw,windos.def
        rc -t windos.res foo.exe


4. Microsoft C/C++ 7.0 instructions
   --------------------------------

    There are a few special aspects to building WINIO programs with
Microsoft C/C++ 7.0 (MSC7).  As noted in the book, MSC7 comes with a
QuickWin (QW) library which is somewhat similar to WINIO.  As also
noted, QW is good for porting DOS code to Windows, but not for
building programs that use the Windows API.  One nice thing about QW
is that it does give you main() rather than WinMain().  The problem
is that QW's main() conflicts with WINIO's main().  Since QW's main()
is built right into the MSC7 library (such as SLIBCEW.LIB) that you
link with, we had to resort to a kludge:

    When using MSC7 to build WINIO programs, main is defined to be
winio_main.  WINIO.H contains the following:

        #if defined(_MSC_VER) && (_MSC_VER >= 700)
        #define main    winio_main
        #endif

    Since WINIO.H takes care of this automatically, you simply need to be
aware of this difference when debugging.  Instead of starting at main,
your program will start at winio_main.

    Furthermore, you must ensure that WINIO's WinMain() gets linked
instead of QW's.  The file ARGCARGV.C is provided in the MSC7
subdirectory for WINIO.  Compiling and linking with this as will do
the trick.  The batch/makefile mechanism takes care of all this.  If
you are compiling and linking with MSC7 "by hand," then do the
following:

        cl -G2sw -Oais -Zpe -c foo.c argcargv.c
        link /nod foo argcargv,,,swindos slibcew libw,windos.def
        rc -t windos.res foo.exe

    In addition, we couldn't locate the environ variable in MSC7.
This is probably our own stupidity, in MSC7 you get winio_main(argc,
argv) but NOT winio_main(argc, argv, envp).  Nobody ever uses envp,
so this should be no big deal.


5. Non-WINIO programs
   ------------------

    A few programs in the book do not use WINIO:

Chapter 1 -- FREERES (uses MessageBox)
Chapter 5 -- KILL (a program added at the last minute, it has no output
    at all; it just kills off a specified task)
Chapter 10 -- CORONER (comes with the usual paraphernalia need to build
    a non-WINIO Windows program)


6. Differences between book and disk
   ---------------------------------

    There are some differences between the sample code printed in the
book and the sample code that appears on this disk.

    Source files for the example programs have in almost all cases
been modified to include a header with build instructions, and to
present a uniform "About" box.

    A number of changes were made throughout for compatibility with
Microsoft C:

    The FP_SEG() and FP_OFF() macros in Microsoft's version of DOS.H
want lvalues.  For example, in CODEDATA.C (Chapter 5), the following:

        data_sel = FP_SEG((void far *) data);

was changed to:

        void far *fpdata = (void far *) data;
        // ...
        data_sel = FP_SEG(fpdata);

    Of course, we could also have used SELECTOROF() and OFFSETOF()
(or HIWORD(), LOWORD()) from WINDOWS.H.

    Microsoft C does not have the MK_FP() macro.  We could have used
MAKELP() from WINDOWS.H, but to make the code look slightly less alien
to DOS programmers, we wanted MK_FP().  In the book, MK_FP() appears
in almost every file that uses it.  Totally bogus!  We just moved
MK_FP() into WINIO.H:

        #ifndef MK_FP
        #define MK_FP(s,o) ((void far *) (((DWORD) (s) << 16) | (o)))
        #endif

    Much of the code in Chapter 5 uses function pointer to Windows API
functions that look like this:

        DWORD FAR PASCAL (*SomeAPIFunction)(HANDLE h);

    Unfortunately, Microsoft C is not happy with this.  It wants the
following (note the location of the opening parenthesis), which does also
work with Borland C++:

        DWORD (FAR PASCAL *SomeAPIFunction)(HANDLE h);

    Some undocumented Windows API functions that are available both in
the 3.0 and 3.1 versions of Windows itself, are not available in the 3.1
version of the SDK's import library (LIBW.LIB).  Where the source code
printed in the book might have something like:

        extern DWORD FAR PASCAL SomeAPIFunction(HANDLE h);

this will in some cases have been changed in the source code on disk to:

        DWORD (FAR PASCAL *SomeAPIFunction)(HANDLE h);
        // ...
        SomeAPIFunction = GET_PROC("SOMEMODULE", "SOMEAPIFUNCTION");

where GET_PROC() is something like:

        #define GET_PROC(modname, funcname) \
            GetProcAddress(GetModuleHandle(modname), (funcname))

    ANSI C makes SomeAPIFunction(x) equivalent to (*SomeAPIFunction)(x),
so the actual function call does not need to be changed, even when
calling through a function pointer.

    Some functions that were undocumented in 3.0 have been documented
in 3.1.  Horray!  Unfortunately, in two cases (SetSelectorBase() and
SetSelectorLimit()), Microsoft thinks these functions have return
values, whereas we think the return values are so useless as to be
void.  Microsoft's WINDOWS.H wins out though, so something printed in 
the book shows:

        void FAR PASCAL SetSelectorBase(WORD sel, DWORD base);
        void FAR PASCAL SetSelectorLimit(WORD sel, DWORD limit);

this has been changed on disk to:

        WORD FAR PASCAL SetSelectorBase(WORD sel, DWORD base);
        WORD FAR PASCAL SetSelectorLimit(WORD sel, DWORD limit);

    This way, the program will compile whether you are using a 3.0
or 3.1 version of WINDOWS.H.  Just ignore the return value.


7. Corrections to Chapter 5: KERNEL
   --------------------------------

Introduction:
    The implementation of hInstance_from_hModule() (usually called
    through the macro GetModuleDgroup()) has been changed:  in the
    text a long way to get an hInstance from an hModule is discussed,
    but then a short way is presented in the code.  The long way
    directly examines the Module Database, whereas the short way
    uses LoadLibrary().  In some last-minute testing, the short way
    just didn't seem as reliable, so HANDLES.C uses the long way.

Instance Data (NULL Segment):
    It turns out that Microsoft's name for this is Task Header (see
    the 3.1 SDK _Guide to Programming_, Chapter 16).

    The version of the NULLSEG.C sample program on disk has been
    changed from the version printed in the book:  we were
    forgetting to call HandleToSel() on hDgroup; this would cause
    the program to fail under 3.0.

KillTask:
    There is no Windows KillTask() function, even though a prototype
    for one appears in WINEXP.H.  The book notes that the ToolHelp
    TerminateApp() function can be used.  A handy sample program,
    KILL.C, appears on the disk along with KILL.EXE; this program is
    just a wrapper around TerminateApp().

Task Database:
    As noted in the book, TDB.INC in the 3.1 DDK shows a number of
    fields related to threads. It is not clear whether or not these
    are actually used in Windows.  However, the Task Database bears
    a striking resemblance to the Per-Task Data Area (PTDA) in OS/2
    (see Deitel and Kogan, _The Design of OS/2_, p. 121), so it is
    possible that at one point this structure was shared between
    OS/2 and Windows, thereby explaining the presence of the thread
    fields.  In other words, maybe they're used in OS/2 but not
    in Windows?

Task Queue:
    The discussion of the Task Queue indicates that the default number
    of messages held in a Task Queue can be changed with an obscure
    WIN.INI setting.  Unfortunately, we overlooked a _documented_ way
    to resize the Task Queue:  SetMessageQueue().

    The status flags shown for offsets 42h and 44h in the Task Queue
    are for 3.0 _only_.  The flags changed for 3.1, and are documented:
    see GetQueueStatus() in the 3.1 SDK and in the 3.1 version of
    WINDOWS.H. For example, where we show QS_PNTWAITING as 0x0010,
    WINDOWS.H shows QS_PAINT as 0x0020.  Ours is accurate for Windows
    3.0, and WINDOWS.H is of course accurate for 3.1.  Interestingly,
    the flags in 3.1 seem mostly to have been shifted left one from 3.0.

THHOOK
    The THHOOK.C sample program has been changed.  As noted in the
    book, the SelTableLen and SelTableStart fields are at different
    locations in 3.0 and 3.1.  However, this was not reflected in   
    the THHOOK.C sample program.  At the last minute, these fields
    were removed from THHOOK.C.  See the Selector Table entry in
    Chapter 5 for sample code to access SelTableLen and SelTableStart
    in 3.0 as well as 3.1.

WaitEvent/PostEvent:
    The SEMTEST.C sample program has been changed.  The task doing
    WaitEvent() can't yield to other others.  Unfortunately, the
    WINIO puts() function yields.  The task doing WaitEvent() no
    longer calls puts().

WinExec:
    The book notes that WinExec() returns an hInstance.  It should
    also have pointed out that often an hTask is more useful than
    an hInstance.  The hInstance returned from WinExec() can be
    turned into an hTask, using the hTask_from_hInstance() function
    from HANDLES.C:
        hTask = hTask_from_hInstance(WinExec("foo.exe", SW_NORMAL));


8. Corrections to Chapter 6: USER
   ------------------------------

BroadcastMessage:
    This function is _not_ exported in Windows 3.1.

Drag and Drop:
    The DDSERVER.C program on disk has been modified slightly to cure 
    a couple of minor bugs.

    DDCLIENT.C is slightly changed from the way it appears in the book
    because the DRAGOBJ_ constants (from DRAGDROP.H) have been split out. 
    The DDCLIENT.C in the text incorrectly assumed that DRAGOBJ_PROGRAM, 
    for example, included the DRAGOBJ_EXTERNAL flag.

GetQueueStatus:
    This function has been _documented_ in 3.1.  Horray!  However, the
    meaning of the returned flags changed between 3.0 and 3.1.  See
    the discussion of the Task Queue offsets 42h and 44h (Chapter 5) above.
    
__GP:
    For information on the __GP entry exported in the 3.1 version of
    USER, see __GP in Chapter 5 (KERNEL)

Keybd_Event:
    The KBDEVENT.C example program now uses GetProcAddress to obtain
    the address of Keybd_Event, instead of trying to find it in the
    import library. Note also that the Alt-Tab keystrokes that it
    generates will switch into, and eventually switch out of a full
    screen DOS app, although it may take many seconds.

SetDeskWallpaper:
    The DTWLPAPR.C example program causes a RIP code "Obsolete function
    SetDeskWallpaper() called" to be generated in 3.1 debug version.
    In 3.1 retail, this function performs no action.

SetGridGranularity:
    The GRIDGRAN.C example program now stays in a loop prompting for
    and setting grid granularity, instead of exiting after the first time.

SetInternalWindowPos/GetInternalWindowPos:
    The INTWPOS.C example program causes a RIP code "Obsolete function
    GetInternalWindowPos() called" to be generated in 3.1 debug version.
    In 3.1 retail, this function works properly.


9. Corrections to Chapter 8: GDI
   -----------------------------

Introduction:
    The logic of the example code that purports to allow for the
    different sizes of the retail and debug GDI object header structures
    is reversed. The code should instead show:

        {
        LPGDIOBJ31DBG lphdr;
        LPDC lpdc;

        lphdr = (LPGDIOBJ31DBG) MK_FP(default_gdi_heap_seg, address_of_dc);

        if ((GetVersion == 0x0a03) && GetSystemMetrics(SM_DEBUG))
            (BYTE FAR *) lpdc = (BYTE FAR *) lphdr +
                sizeof(GDIOBJ31DBG) - sizeof(GDIOBJHDR);
        else
            lpdc = (LPDC) lphdr;

        /* Use lphdr for access to object header structure fields, and
            lpdc for access to DC fields */
        }

    The BITMAPOBJ structure in GDIOBJ.H lists a field 'hdc'. This is
    an error. The field has been renamed h0E for its offset and the fact
    that it contains an unknown global memory handle.

BitmapBits:
    The book notes that BitmapBits is not exported in 3.1.  What it
    fails to note is that the entry point used by BitmapBits in 3.0
    -- GDI.46 -- is in 3.1 used (overloaded) by __GP.  Aargh!

DeviceColorMatch:
    Apparently, this function has to do with DIBs.  Given an RGB and
    some other parameters, the function tried to find a match to a
    logical palette.  It possibly returns a physical palette index.

FixupBogusPublisherMetafile:
    Apparently, the offending Publisher is _not_ Ventura.  It's
    Microsoft Publisher!

GetKerningPairs:
    The first argument is an HDC, not an HFONT.

__GP:
    For information on the __GP entry exported in the 3.1 version of
    GDI, see __GP in Chapter 5 (KERNEL)

LVBUnion:
    This function should be "LVBUnion" not "LvbUnion".  LVB (which
    also appears in the rectLVB field in the DC) apparently stands
    for "logical video buffer."

ScaleExt:
    This function is _not_ exported in 3.1.

=== END ===
