/*++
 *
 *  WOW v1.0
 *
 *  Copyright (c) 1991, Microsoft Corporation
 *
 *  WUCLASS.C
 *  WOW32 16-bit User API support
 *
 *  History:
 *  Created 07-Mar-1991 by Jeff Parsons (jeffpar)
--*/


#include "precomp.h"
#pragma hdrstop

MODNAME(wuclass.c);

extern HANDLE ghInstanceUser32;
extern WORD   gUser16hInstance;

/*++
    BOOL GetClassInfo(<hInstance>, <lpClassName>, <lpWndClass>)
    HANDLE <hInstance>;
    LPSTR <lpClassName>;
    LPWNDCLASS <lpWndClass>;

    The %GetClassInfo% function retrieves information about a window class. The
    <hInstance> parameter identifies the instance of the application that
    created the class, and the <lpClassName> parameter identifies the window
    class. If the function locates the specified window class, it copies the
    %WNDCLASS% data used to register the window class to the %WNDCLASS%
    structure pointed to by the <lpWndClass> parameter.

    <hInstance>
        Identifies the instance of the application that created the class. To
        retrieve information on classes defined by Windows (such as buttons or
        list boxes), set hInstance to NULL.

    <lpClassName>
        Points to a null-terminated string that contains the name of the
        class to find. If the high-order word of this parameter is NULL, the
        low-order word is assumed to be a value returned by the
        %MAKEINTRESOURCE% macro used when the class was created.

    <lpWndClass>
        Points to the %WNDCLASS% structure that will receive the class
        information.

    The return value is TRUE if the function found a matching class and
    successfully copied the data; the return value is FALSE if the function did
    not find a matching class.

    The %lpszClassName%, %lpszMenuName%, and %hInstance% members of the
    %WNDCLASS% structure are <not> set by this function. The menu name is
    not stored internally and cannot be returned. The class name is already
    known since it is passed to this function. The %GetClassInfo% function
    returns all other fields with the values used when the class was
    registered.
--*/

ULONG FASTCALL WU32GetClassInfo(PVDMFRAME pFrame)
{
    ULONG       ul;
    PSZ psz2,   pszClass;
    WNDCLASS    t3;
    register    PGETCLASSINFO16 parg16;
    WORD        w;
    HINSTANCE   hInst;
    PWC         pwc = NULL;
    PWNDCLASS16 pwc16;
    CHAR        szAtomName[WOWCLASS_ATOM_NAME];

    GETARGPTR(pFrame, sizeof(GETCLASSINFO16), parg16);
    GETPSZIDPTR(parg16->f2, psz2);

    if ( HIWORD(psz2) == 0 ) {
        pszClass = szAtomName;
        GetAtomName( (ATOM)psz2, pszClass, WOWCLASS_ATOM_NAME );
    } else {
        pszClass = psz2;
    }

    // map hInst user16 to hMod user32
    if(parg16->f1 == gUser16hInstance) {
        hInst = ghInstanceUser32;
    }
    else {
        hInst = HMODINST32(parg16->f1);
    }

    ul = GETBOOL16(GetClassInfo(hInst, pszClass, &t3));

    // This fine piece of hackery mimicks the difference between the class list 
    // search algorithms in Win3.1 & SUR.  Essentially SUR checks the private,
    // public, and global class lists while Win3.1 only checks the private and
    // global lists.  Note we are striving for Win3.1 compatibility here -- not
    // always the logical thing!  Finding an existing *stale* class breaks some
    // apps!  Bug #31269    a-craigj, GerardoB
    // Restrict this hack to PageMaker 50a for now
    if(CURRENTPTD()->dwWOWCompatFlagsEx & WOWCFEX_FAKECLASSINFOFAIL) {
    if(ul && hInst) {
        
        // if this class wasn't registered by this app, AND it's not a global
        // class, then it must have come from the public list and this app 
        // wouldn't know about it on Win3.1 -- so lie and say it doesn't exist!
        // Note: The high word is the *hModule* which is what Win3.1 AND NT save
        //       with the class internally (not the hInstance!)
        if((HIWORD(t3.hInstance) != HIWORD(hInst)) && !(t3.style & CS_GLOBALCLASS)) {
            WOW32WARNMSGF(0,("\nWOW:GetClassInfo force failure hack:\n   class = '%s'  wc.hInstance = %X  app_hInst = %X\n\n", pszClass, t3.hInstance, hInst));
            ul = 0;
        }
    }
    }

    if (ul) {

        //
        // If the class is a 'standard' class, replace the class proc
        // with a thunk proc
        //

        GETVDMPTR(parg16->f3, sizeof(WNDCLASS16), pwc16);
        STOREWORD(pwc16->style,          t3.style);
        if (!_stricmp(pszClass, "edit")) {
            STOREWORD(pwc16->style, (FETCHWORD(pwc16->style) & ~CS_GLOBALCLASS));
        }

        STOREDWORD(pwc16->vpfnWndProc,  0);

        // if this class was registered by WOW
        if ( (DWORD)t3.lpfnWndProc & WNDPROC_WOW ) {
            pwc16->vpfnWndProc = (VPWNDPROC)((DWORD)t3.lpfnWndProc & WNDPROC_MASK);

            //
            // if the actual selector had the high bit on then we turned off
            // bit 2 of the selector (the LDT bit, which will always be on)
            //

            if (!((DWORD)pwc16->vpfnWndProc & WOWCLASS_VIRTUAL_NOT_BIT31)) {
                pwc16->vpfnWndProc |= (WNDPROC_WOW | WOWCLASS_VIRTUAL_NOT_BIT31);
            }

            // we need to subtract out our WOW DWORDS for WOW classes
            // (see GCW_CBCLSEXTRA notes in RegisterClass())
            STORESHORT(pwc16->cbClsExtra, t3.cbClsExtra - (2 * sizeof(DWORD)));

        } else {
            pwc16->vpfnWndProc = GetThunkWindowProc((DWORD)t3.lpfnWndProc, pszClass, NULL, NULL);
            STORESHORT(pwc16->cbClsExtra, t3.cbClsExtra);
        }

#ifdef OLD_WAY
        if (parg16->f1 ||
            !(pwc16->vpfnWndProc = GetThunkWindowProc((DWORD)t3.lpfnWndProc, pszClass, NULL, NULL))) {

            pwc = FindClass16(pszClass, (HINST16)parg16->f1);
            STOREDWORD(pwc16->vpfnWndProc,   pwc->vpfnWndProc);
        }
#endif

        STORESHORT(pwc16->cbWndExtra,    t3.cbWndExtra);

        // Win3.1 copies the hInst passed in by the app into the WNDCLASS struct
        // unless hInst == NULL, in which case they copy user's hInst 
        if((!parg16->f1) || (t3.hInstance == ghInstanceUser32)) {
            w = gUser16hInstance;
        } else {
            w = VALIDHMOD(t3.hInstance);
            if(w != BOGUSGDT) {
                w = parg16->f1;
            }
        }
        STOREWORD(pwc16->hInstance, w);
        w = GETHICON16(t3.hIcon);        STOREWORD(pwc16->hIcon, w);
        w = GETHCURSOR16(t3.hCursor);    STOREWORD(pwc16->hCursor, w);
        w = ((ULONG)t3.hbrBackground > COLOR_ENDCOLORS) ?
                GETHBRUSH16(t3.hbrBackground) : (WORD)t3.hbrBackground;
        STOREWORD(pwc16->hbrBackground, w);

        // These are strange assignments.  We don't keep the class name or
        // menu name in 16-bit memory.  For class name, USER32 just returns
        // the value which is passed as the second parameter, which works
        // MOST of the time; we do the same.  For the menu name, USER32 just
        // returns the value which was passed when the class was registered.
        // There are some situations where these psz's might go out of scope
        // and no longer be valid when the application attempts to use them.
        // IF YOU EVER FIND AN APPLICATION WHICH GETS THE WRONG
        // THING AND IT FAILS BECAUSE OF IT, SOME NASTY HACKING WILL HAVE
        // TO BE DONE AND IT SHOULD BE DONE IN USER32 ALSO...
        // -BobDay
        //
        if ( pwc = FindClass16(pszClass, (HINST16)parg16->f1)) {
            STOREDWORD(pwc16->vpszMenuName,  pwc->vpszMenu);
        } else {
            STOREDWORD(pwc16->vpszMenuName, 0 );
        }

        STOREDWORD(pwc16->vpszClassName, parg16->f2);

        FLUSHVDMPTR(parg16->f3, sizeof(WNDCLASS16), pwc16);
        FREEVDMPTR(pwc16);
    }
    FREEPSZIDPTR(psz2);
    FREEARGPTR(parg16);
    RETURN(ul);
}


/*++
    LONG GetClassLong(<hwnd>, <nIndex>)
    HWND <hwnd>;
    int <nIndex>;

    The %GetClassLong% function retrieves the long value specified by the
    <nIndex> parameter from the %WNDCLASS% structure of the window specified by
    the <hwnd> parameter.

    <hwnd>
        Identifies the window.

    <nIndex>
        Specifies the byte offset of the value to be retrieved. It can also
        be the following value:

    GCL_WNDPROC
    Retrieves a long pointer to the window function.
    GCL_MENUNAME
    Retrieves a long pointer to the menu name.

    The return value specifies the value retrieved from the %WNDCLASS%
    structure.

    To access any extra four-byte values allocated when the window-class
    structure was created, use a positive byte offset as the index specified by
    the <nIndex> parameter. The first four-byte value in the extra space is at
    offset zero, the next four-byte value is at offset 4, and so on.
--*/

ULONG FASTCALL WU32GetClassLong(PVDMFRAME pFrame)
{
    ULONG ul, flag;
    INT iOffset;
    HWND hwnd;
    register PWW pww;
    register PWC pwc;
    register PGETCLASSLONG16 parg16;

    GETARGPTR(pFrame, sizeof(GETCLASSLONG16), parg16);

    // Make sure Win32 didn't change offsets for GCL constants

#if (GCL_WNDPROC != (-24) || GCL_MENUNAME != (-8))
#error Win16/Win32 GCL constants differ
#endif

    // Make sure the 16-bit app is requesting allowable offsets

    iOffset = INT32(parg16->f2);
    WOW32ASSERT(iOffset >= 0 ||
        iOffset == GCL_WNDPROC ||
        iOffset == GCL_MENUNAME);

    hwnd = HWND32(parg16->f1);

    switch (iOffset) {
        case GCL_WNDPROC:
            {
                DWORD   dwProc32;

                dwProc32 = GetClassLong(hwnd, iOffset);

                if ( dwProc32 & WNDPROC_WOW ) {
                    if ( HIWORD(dwProc32) == WNDPROC_HANDLE ) {
                        //
                        // Class has a window proc which is really a handle
                        // to a proc.  This happens when there is some
                        // unicode to ansi transition or vice versa.
                        //
                        pww = FindPWW( hwnd, WOWCLASS_UNKNOWN);
                        if ( pww == NULL ) {
                            ul = 0;
                        } else {
                            ul = GetThunkWindowProc(dwProc32,NULL,pww,hwnd);
                        }
                    } else {
                        //
                        // Class already has a 16:16 address
                        //
                        ul = dwProc32 & WNDPROC_MASK;

                        //
                        // if the actual selector had the high bit on then we turned off
                        // bit 2 of the selector (the LDT bit, which will always be on)
                        //
                        if (!(ul & WOWCLASS_VIRTUAL_NOT_BIT31)) {
                            ul |= (WNDPROC_WOW | WOWCLASS_VIRTUAL_NOT_BIT31);
                        }
                    }
                } else {
                    //
                    // Class has a 32-bit proc, return an allocated thunk
                    //
                    pww = FindPWW(hwnd, WOWCLASS_UNKNOWN);
                    if ( pww == NULL ) {
                        ul = 0;
                    } else {
                        ul = GetThunkWindowProc(dwProc32,NULL,pww,hwnd);
                    }
                }
            }
            break;

        case GCL_MENUNAME:
            if ((pww = FindPWW(hwnd, WOWCLASS_UNKNOWN)) &&
                (WOWCLASS_WIN16 == pww->iClass) &&
                (pwc = FindPWC(hwnd))) {
                ul = pwc->vpszMenu;
            } else {
                ul = 0;
            }
            break;

        case GCL_CBCLSEXTRA:
            ul = GetClassLong(hwnd, GCL_CBCLSEXTRA);

            // verify that this class was registered by WOW
            // (see GCW_CBCLSEXTRA notes in thunk for RegisterClass())
            if(GetClassLong(hwnd, GCL_WNDPROC) & WNDPROC_WOW) { 

                // get the bozo flag
                iOffset = ul - sizeof(DWORD);
                flag    = GetClassLong(hwnd, iOffset);

                // if bozo app flag is set, get the value the app saved
                // otherwise good little apps get cbClsExtra
                if(flag) {
                    iOffset -= sizeof(DWORD);
                    ul = GetClassLong(hwnd, iOffset);
                }
                else {
                    ul -= (2 * sizeof(DWORD)); // account for our WOW DWORDs
                }
            }
            break;

        default:
            ul = GetClassLong(hwnd, iOffset);
            break;
    }

    FREEARGPTR(parg16);
    RETURN(ul);
}


/*++
    WORD GetClassWord(<hwnd>, <nIndex>)
    HWND <hwnd>;
    int <nIndex>;

    The %GetClassWord% function retrieves the word that is specified by the
    <nIndex> parameter from the %WNDCLASS% structure of the window specified by
    the <hwnd> parameter.

    <hwnd>
        Identifies the window.

    <nIndex>
        Specifies the byte offset of the value to be retrieved. It can also
        be one of the following values:

    GCL_CBCLSEXTRA
        Tells how many bytes of additional class information you have. For
        information on how to access this memory, see the following "Comments"
        section.

    GCL_CBWNDEXTRA
        Tells how many bytes of additional window information you have. For
        information on how to access this memory, see the following<>Comments
        section.

    GCL_HBRBACKGROUND
        Retrieves a handle to the background brush.

    GCL_HCURSOR
        Retrieves a handle to the cursor.

    GCL_HICON
        Retrieves a handle to the icon.

    GCL_HMODULE
        Retrieves a handle to the module.

    GCL_STYLE
        Retrieves the window-class style bits.

    The return value specifies the value retrieved from the %WNDCLASS%
    structure.

    To access any extra two-byte values allocated when the window-class
    structure was created, use a positive byte offset as the index specified by
    the <nIndex> parameter, starting at zero for the first two-byte value in the
    extra space, 2 for the next two-byte value and so on.
--*/

ULONG FASTCALL WU32GetClassWord(PVDMFRAME pFrame)
{
    ULONG  ul, flag;
    HWND   hwnd;
    INT    iOffset;
    register PGETCLASSWORD16 parg16;

    GETARGPTR(pFrame, sizeof(GETCLASSWORD16), parg16);

    // Make sure Win32 didn't change offsets

#if (GCL_HBRBACKGROUND != (-10) || GCL_HCURSOR != (-12) || GCL_HICON != (-14) || GCL_HMODULE != (-16) || GCL_CBWNDEXTRA != (-18) || GCL_CBCLSEXTRA != (-20) || GCL_STYLE != (-26))
#error Win16/Win32 class-word constants differ
#endif

    // Make sure the 16-bit app is requesting allowable offsets
    // (It's just assertion code, so it doesn't have to be pretty! -JTP)

    iOffset = INT32(parg16->f2);
    WOW32ASSERT(iOffset >= 0 ||
        iOffset == GCL_HBRBACKGROUND ||
        iOffset == GCL_HCURSOR ||
        iOffset == GCL_HICON ||
        iOffset == GCL_HMODULE ||
        iOffset == GCL_CBWNDEXTRA ||
        iOffset == GCL_CBCLSEXTRA ||
        iOffset == GCL_STYLE ||
        iOffset == GCW_ATOM);

    hwnd = HWND32(parg16->f1);

    switch(iOffset) {
        case GCL_HBRBACKGROUND:
            ul = GetClassLong(hwnd, iOffset);
            if (ul > COLOR_ENDCOLORS)
                ul = GETHBRUSH16(ul);
            break;

        case GCL_HCURSOR:
            ul = GETHCURSOR16((HAND32)GetClassLong(hwnd, iOffset));
            break;

        case GCL_HICON:
            ul = GETHICON16((HAND32)GetClassLong(hwnd, iOffset));
            break;

        case GCL_HMODULE:
            ul = GetGCL_HMODULE(hwnd);
            break;

        case GCL_CBWNDEXTRA:
        case GCL_STYLE:
            ul = GetClassLong(hwnd, iOffset);
            break;

        case GCL_CBCLSEXTRA:
            ul = GetClassLong(hwnd, GCL_CBCLSEXTRA);

            // verify that this class was registered by WOW
            // (see GCW_CBCLSEXTRA notes in thunk for RegisterClass())
            if(GetClassLong(hwnd, GCL_WNDPROC) & WNDPROC_WOW) { 

                // get the bozo flag
                iOffset = ul - sizeof(DWORD);
                flag    = GetClassLong(hwnd, iOffset);

                // if bozo app flag is set, get the value the app saved
                // otherwise good little apps get cbClsExtra
                if(flag) {
                    iOffset -= sizeof(DWORD);
                    ul = GetClassWord(hwnd, iOffset);
                }
                else {
                    ul -= (2 * sizeof(DWORD)); // account for our WOW DWORDs
                }
            }
            break;


        default:
            ul = GetClassWord(hwnd, iOffset);
            break;
    }

    FREEARGPTR(parg16);
    RETURN(ul);
}


/*++
    BOOL RegisterClass(<lpWndClass>)
    LPWNDCLASS <lpWndClass>;

    The %RegisterClass% function registers a window class for subsequent use in
    calls to the %CreateWindow% function. The window class has the attributes
    defined by the contents of the structure pointed to by the <lpWndClass>
    parameter. If two classes with the same name are registered, the second
    attempt fails and the information for that class is ignored.

    <lpWndClass>
        Points to a %WNDCLASS% structure. The structure must be filled with
        the appropriate class attributes before being passed to the function.
        See the following "Comments" section for details.

    The return value specifies whether the window class is registered. It is
    TRUE if the class is registered. Otherwise, it is FALSE.

    The callback function must use the Pascal calling conventions and must be
    declared %FAR%.

    Callback Function:

    BOOL FAR PASCAL <WndProc>(<hwnd>, <wMsg>, <wParam>, <lParam>)
    HWND <hwnd>;
    WORD <wMsg>;
    WORD<wParam>;
    DWORD<lParam>;

    <WndProc> is a placeholder for the application-supplied function name. The
    actual name must be exported by including it in an %EXPORTS% statement in
    the application's module-definition file.

    <wMsg>
        Specifies the message number.

    <wParam>
        Specifies additional message-dependent information.

    <lParam>
        Specifies additional message-dependent information.

    The window function returns the result of the message processing. The
    possible return values depend on the actual message sent.
--*/

ULONG FASTCALL WU32RegisterClass(PVDMFRAME pFrame)
{
    ULONG ul;
    WNDCLASS t1;
    VPSZ    vpszMenu;
    PSZ pszMenu;
    PSZ pszClass;
    register PREGISTERCLASS16 parg16;
    CHAR    szAtomName[WOWCLASS_ATOM_NAME];
    WC      wc;

    GETARGPTR(pFrame, sizeof(REGISTERCLASS16), parg16);

    GETWNDCLASS16(parg16->vpWndClass, &t1);

    // Fix up the window words for apps that have hardcode values and did not
    // use the value from GetClassInfo when superclassing system proc.
    // Some items have been expanded from a WORD to a DWORD bug 22014
//    t1.cbWndExtra = (t1.cbWndExtra + 3) & ~3;

    // Some apps call SetClassWord()and SetClassLong() with the GCW_CBCLSEXTRA
    // offset specified and clobber the cbClsExtra in the class structure on 
    // Win3.1 and retreive the value later (Wall Street Analyst, PC Anywhere are
    // known culprits).  Win'95 allows this for apps whose WinVer is less than
    // 4.0.  NT can't allow it without exposing the kernel to problems.  What 
    // we do is add 2 extra DWORDs of class extra bytes to any classes that are
    // created by WOW. We use the 1st DWORD to save the value the app passed to
    // us and the 2nd DWORD as a flag that specifies that the bozo app did this.
    // When an app calls GetClassXXXX(GWC_CBCLSEXTRA) we test the flag and 
    // return the appropriate value -- either cbClsExtra or the bozo value.
    t1.cbClsExtra += (2 * sizeof(DWORD));

    vpszMenu = (VPSZ)t1.lpszMenuName;
    if (HIWORD(t1.lpszMenuName) != 0) {
        GETPSZPTR(t1.lpszMenuName, pszMenu);
        t1.lpszMenuName = pszMenu;
    }

    if (HIWORD(t1.lpszClassName) == 0) {
        pszClass = szAtomName;
        GetAtomName( (ATOM)t1.lpszClassName, pszClass, WOWCLASS_ATOM_NAME);
    } else {
        GETPSZPTR(t1.lpszClassName, pszClass);
    }

    t1.lpszClassName = pszClass;

    ul = 0;

    wc.vpszMenu = vpszMenu;
    wc.vpfnWndProc = (VPWNDPROC)t1.lpfnWndProc;

    t1.lpfnWndProc = (WNDPROC)((DWORD)t1.lpfnWndProc | WNDPROC_WOW);

    //
    // FEATURE-O-RAMA
    //
    // if the selector already has the high bit on then turn off bit 2
    // of the selector (the LDT bit, which should always be on).  we
    // need a way to not blindly strip off the high bit in our wndproc.
    //

    if ((DWORD)wc.vpfnWndProc & WNDPROC_WOW) {
        WOW32ASSERT((DWORD)t1.lpfnWndProc & WOWCLASS_VIRTUAL_NOT_BIT31);
        (DWORD)t1.lpfnWndProc &= ~WOWCLASS_VIRTUAL_NOT_BIT31;
    }

    // Validate hbrBackground, because apps can pass an invalid handle.
    // The GetGDI32 returns a non-null value even if h16 is invalid.
    //
    // if hbrBackground is not valid we set it to NULL if the apps
    // ExpWinVer is < 3.1. This behaviour is identical to WIN31 behaviour.
    //
    // We need to do this validation here because USER32 will fail
    // RegisterClass() if hbrBackground is not valid.
    //
    // known culprits: QuickCase:W (that comes with QcWin)
    //                 class "iconbutton".

    if ((DWORD)t1.hbrBackground > (DWORD)(COLOR_ENDCOLORS) &&
        GetObjectType(t1.hbrBackground) != OBJ_BRUSH) {
           if ((WORD)W32GetExpWinVer((HANDLE) LOWORD(t1.hInstance) ) < 0x030a)
                t1.hbrBackground = (HBRUSH)NULL;
    }

    ul = GETBOOL16((pfnOut.pfnRegisterClassWOWA)(&t1, (DWORD *)&wc));

    if (!ul) {
        LOGDEBUG(LOG_ALWAYS,("WOW: RegisterClass failed (\"%s\")\n", (LPSZ)pszClass));
        // WOW32ASSERT(ul);
    }

    FREEPSZPTR(pszClass);
    FREEPSZPTR(pszMenu);
    FREEARGPTR(parg16);
    RETURN(ul);
}


/*++
    LONG SetClassLong(<hwnd>, <nIndex>, <dwNewLong>)
    HWND <hwnd>;
    int <nIndex>;
    DWORD <dwNewLong>;

    The %SetClassLong% function replaces the long value specified by the
    <nIndex> parameter in the %WNDCLASS% structure of the window specified by
    the <hwnd> parameter.

    <hwnd>
        Identifies the window.

    <nIndex>
        Specifies the byte offset of the word to be changed. It can also
        be one of the following values:

    GCL_MENUNAME
        Sets a new long pointer to the menu

    GCL_WNDPROC
        Sets a new long pointer to the window function.

    <dwNewLong>
        Specifies the replacement value.

    The return value specifies the previous value of the specified long
    integer.

    If the %SetClassLong% function and GCL_WNDPROC index are used to set a
    window function, the given function must have the window-function form and
    be exported in the module-definition file. See the %RegisterClass% function
    earlier in this chapter for details.

    Calling %SetClassLong% with the GCL_WNDPROC index creates a subclass of the
    window class that affects all windows subsequently created with the class.
    See Chapter 1, Window Manager Interface Functions, for more information on
    window subclassing. An application should not attempt to create a window
    subclass for standard Windows controls such as combo boxes and buttons.

    To access any extra two-byte values allocated when the window-class
    structure was created, use a positive byte offset as the index specified by
    the <nIndex> parameter, starting at zero for the first two-byte value in the
    extra space, 2 for the next two-byte value and so on.
--*/

ULONG FASTCALL WU32SetClassLong(PVDMFRAME pFrame)
{
    ULONG ul, flag;
    INT iOffset;
    PSZ pszMenu;
    register PWC pwc;
    register PSETCLASSLONG16 parg16;

    GETARGPTR(pFrame, sizeof(SETCLASSLONG16), parg16);

    // Make sure Win32 didn't change offsets for GCL constants

#if (GCL_MENUNAME != (-8) || GCL_WNDPROC != (-24))
#error Win16/Win32 GCL constants differ
#endif

    // Make sure the 16-bit app is requesting allowable offsets

    iOffset = INT32(parg16->f2);

    WOW32ASSERT(iOffset >= 0 ||
                iOffset == GCL_WNDPROC ||
                iOffset == GCL_MENUNAME);

    ul = 0;

    switch (iOffset) {
        case GCL_WNDPROC:
            {
                DWORD   dwWndProc32Old;
                DWORD   dwWndProc32New;
                PWW     pww;

                // Look to see if the new 16:16 proc is a thunk for a 32-bit proc.
                dwWndProc32New = IsThunkWindowProc(LONG32(parg16->f3), NULL );

                if ( dwWndProc32New != 0 ) {
                    //
                    // They are attempting to set the window proc to an existing
                    // 16-bit thunk that is really just a thunk for a 32-bit
                    // routine.  We can just set it back to the 32-bit routine.
                    //
                    dwWndProc32Old = SetClassLong(HWND32(parg16->f1), GCL_WNDPROC, (LONG)dwWndProc32New);
                    SETWC(HWND32(parg16->f1), GCL_WOWvpfnWndProc, 0);
                } else {
                    //
                    // They are attempting to set it to a real 16:16 proc.
                    //
                    LONG l;

                    l = LONG32(parg16->f3);

                    //
                    // FEATURE-O-RAMA
                    //
                    // if the selector already has the high bit on then turn off bit 2
                    // of the selector (the LDT bit, which should always be on).  we
                    // need a way to not blindly strip off the high bit in our wndproc.
                    //

                    if (l & WNDPROC_WOW) {
                        WOW32ASSERT(l & WOWCLASS_VIRTUAL_NOT_BIT31);
                        l &= ~WOWCLASS_VIRTUAL_NOT_BIT31;
                    }

                    dwWndProc32Old = SetClassLong(HWND32(parg16->f1), GCL_WNDPROC, l | WNDPROC_WOW );
                    SETWC(HWND32(parg16->f1), GCL_WOWvpfnWndProc, LONG32(parg16->f3));
                }

                if ( dwWndProc32Old & WNDPROC_WOW ) {
                    if ( HIWORD(dwWndProc32Old) == WNDPROC_HANDLE ) {
                        //
                        // If the return value is a handle, then just thunk it.
                        //
                        pww = FindPWW(HWND32(parg16->f1), WOWCLASS_UNKNOWN);
                        if ( pww == NULL ) {
                            ul = 0;
                        } else {
                            ul = GetThunkWindowProc(dwWndProc32Old, NULL, pww, HWND32(parg16->f1));
                        }
                    } else {
                        //
                        // Previous proc was a 16:16 proc
                        //
                        ul = dwWndProc32Old & WNDPROC_MASK;

                        //
                        // if the actual selector had the high bit on then we turned off
                        // bit 2 of the selector (the LDT bit, which will always be on)
                        //
                        if (!(ul & WOWCLASS_VIRTUAL_NOT_BIT31)) {
                            ul |= (WNDPROC_WOW | WOWCLASS_VIRTUAL_NOT_BIT31);
                        }
                    }
                } else {
                    //
                    // Previous proc was a 32-bit proc, use an allocated thunk
                    //
                    pww = FindPWW(HWND32(parg16->f1), WOWCLASS_UNKNOWN);
                    if ( pww == NULL ) {
                        ul = 0;
                    } else {
                        ul = GetThunkWindowProc(dwWndProc32Old, NULL, pww, HWND32(parg16->f1));
                    }

                }
            }
            break;

        case GCL_MENUNAME:
            if (pwc = FindPWC(HWND32(parg16->f1))) {
                ul = pwc->vpszMenu;
                GETPSZPTR(parg16->f3, pszMenu);
                SETWC(HWND32(parg16->f1), GCL_WOWvpszMenu, parg16->f3);
                SetClassLong(HWND32(parg16->f1), GCL_MENUNAME, (LONG)pszMenu);
                FREEPSZPTR(pszMenu);
            }
            break;


        case GCL_CBCLSEXTRA:
            // apps shouldn't do this but of course some do!
            // (see GCW_CBCLSEXTRA notes in thunk for RegisterClass())
            WOW32WARNMSG(0, ("WOW:SetClassLong(): app changing cbClsExtra!"));

            // only allow this to be set by classes registered via WOW
            if(GetClassLong(HWND32(parg16->f1), GCL_WNDPROC) & WNDPROC_WOW) { 

                ul = WORD32(parg16->f3);

                // set the bozo flag & save the value the app passed us
                iOffset =  GetClassLong(HWND32(parg16->f1), GCL_CBCLSEXTRA);
                iOffset -= sizeof(DWORD);
                flag    =  SetClassLong(HWND32(parg16->f1), iOffset, 1);
                iOffset -= sizeof(DWORD);
                ul      =  SetClassLong(HWND32(parg16->f1), iOffset, ul);

                // SetClassLong() returns the "previous" value.
                // if this is the first time the app pulls this stunt it will
                // get back the original cbClsExtra, otherwise it will get back
                // the value it stored here on the previous call.
                if(!flag) {
                    ul = iOffset; // the WOW DWORDs have already been subtracted
                }
            }
            else {
                ul = 0;  // no can do for non-WOW classes
            }
            break;

        default:
            ul = SetClassLong(HWND32(parg16->f1), iOffset, LONG32(parg16->f3));
            break;
    }

    FREEARGPTR(parg16);
    RETURN(ul);
}


/*++
    WORD SetClassWord(<hwnd>, <nIndex>, <wNewWord>)
    HWND <hwnd>;
    int <nIndex>;
    WORD <wNewWord>;

    The %SetClassWord% function replaces the word specified by the <nIndex>
    parameter in the %WNDCLASS% structure of the window specified by the <hwnd>
    parameter.

    <hwnd>
        Identifies the window.

    <nIndex>
        Specifies the byte offset of the word to be changed. It can also
        be one of the following values:

    GCL_CBCLSEXTRA
        Sets two new bytes of additional window-class data.

    GCL_CBWNDEXTRA
        Sets two new bytes of additional window-class data.

    GCL_HBRBACKGROUND
        Sets a new handle to a background brush.

    GCL_HCURSOR
        Sets a new handle to a cursor.

    GCL_HICON
        Sets a new handle to an icon.

    GCL_STYLE
        Sets a new style bit for the window class.

    <wNewWord>
        Specifies the replacement value.

    The return value specifies the previous value of the specified word.

    The %SetClassWord% function should be used with care. For example, it is
    possible to change the background color for a class by using %SetClassWord%,
    but this change does not cause all windows belonging to the class to be
    repainted immediately.

    To access any extra four-byte values allocated when the window-class
    structure was created, use a positive byte offset as the index specified by
    the <nIndex> parameter, starting at zero for the first four-byte value in
    the extra space, 4 for the next four-byte value and so on.
--*/

ULONG FASTCALL WU32SetClassWord(PVDMFRAME pFrame)
{
    ULONG  ul, flag;
    HWND   hwnd;
    INT    iOffset;
    register PSETCLASSWORD16 parg16;

    GETARGPTR(pFrame, sizeof(SETCLASSWORD16), parg16);

    // Make sure Win32 didn't change offsets

#if (GCL_HBRBACKGROUND != (-10) || GCL_HCURSOR != (-12) || GCL_HICON != (-14) || GCL_CBWNDEXTRA != (-18) || GCL_CBCLSEXTRA != (-20) || GCL_STYLE != (-26))
#error Win16/Win32 GCW constants differ
#endif

    // Make sure the 16-bit app is requesting allowable offsets
    // (It's just assertion code, so it doesn't have to be pretty! -JTP)

    iOffset = INT32(parg16->f2);
    WOW32ASSERT(iOffset >= 0 ||
        iOffset == GCL_HBRBACKGROUND ||
        iOffset == GCL_HCURSOR ||
        iOffset == GCL_HICON ||
        iOffset == GCL_CBWNDEXTRA ||
        iOffset == GCL_CBCLSEXTRA ||
        iOffset == GCL_STYLE)

    hwnd = HWND32(parg16->f1);
    ul = WORD32(parg16->f3);

    switch(iOffset) {
        case GCL_HBRBACKGROUND:
            if (ul > COLOR_ENDCOLORS)
                ul = (LONG) HBRUSH32(ul);

            ul = SetClassLong(hwnd, iOffset, (LONG) ul);

            if (ul > COLOR_ENDCOLORS)
                ul = GETHBRUSH16(ul);
            break;

        case GCL_HCURSOR:
            ul = GETHCURSOR16(SetClassLong(hwnd, iOffset, (LONG)HCURSOR32(ul)));
            break;

        case GCL_HICON:
            ul = GETHICON16(SetClassLong(hwnd, iOffset, (LONG)HICON32(ul)));
            break;

        case GCL_HMODULE:
            ul = 0;         // not allowed to set this
            break;

        case GCL_CBWNDEXTRA:
        case GCL_STYLE:
            ul = SetClassLong(hwnd, iOffset, (LONG)ul);
            break;

        case GCL_CBCLSEXTRA:
            // apps shouldn't do this but of course some do!
            // (see GCW_CBCLSEXTRA notes in thunk for RegisterClass())
            WOW32WARNMSG(0, ("WOW:SetClassWord(): app changing cbClsExtra!"));

            // only allow this to be set by classes registered via WOW
            if(GetClassLong(hwnd, GCL_WNDPROC) & WNDPROC_WOW) { 

                // set the bozo flag 
                iOffset = GetClassLong(hwnd, GCL_CBCLSEXTRA);
                iOffset -= sizeof(DWORD);
                flag = SetClassLong(hwnd, iOffset, 1);

                // save the value the app passed 
                iOffset -= sizeof(DWORD);
                ul      = (WORD)SetClassWord(hwnd, iOffset, LOWORD(ul));

                // SetClassWord() returns the "previous" value.
                // if this is the first time the app pulls this stunt it will
                // get back the original cbClsExtra, otherwise it will get back
                // the value it stored here on the previous call.
                if(!flag) {
                    ul = iOffset; // the WOW DWORDs have already been subtracted
                }
            }
            else {
                ul = 0;  // no can do for non-WOW classes
            }
            break;

        default:
            ul = SetClassWord(hwnd, iOffset, (WORD)ul);
            break;
    }

    FREEARGPTR(parg16);
    RETURN(ul);
}


/*++
    BOOL  UnregisterClass(<lpClassName>, <hInstance>)

    The %UnregisterClass% function removes the window class specified by
    <lpClassName> from the window-class table, freeing the storage required for
    the class.

    <lpClassName>
        Points to a null-terminated string containing the class name. This class
        name must have been previously registered by calling the RegisterClass
        function with a valid hInstance field in the %WNDCLASS% structure
        parameter. Predefined classes, such as dialog-box controls, may not be
        unregistered.

    <hInstance>
        Identifies the instance of the module that created the class.

    The return value is TRUE if the function successfully removed the window
    class from the window-class table. It is FALSE if the class could not be
    found or if a window exists that was created with the class.

    Before using this function, destroy all windows created with the specified
    class.
--*/

ULONG FASTCALL WU32UnregisterClass(PVDMFRAME pFrame)
{
    ULONG ul;
    PSZ psz1, pszClass;
    register PUNREGISTERCLASS16 parg16;
    CHAR    szAtomName[WOWCLASS_ATOM_NAME];

    GETARGPTR(pFrame, sizeof(UNREGISTERCLASS16), parg16);
    GETPSZIDPTR(parg16->vpszClass, psz1);

    if ( HIWORD(psz1) == 0 ) {
        pszClass = szAtomName;
        GetAtomName( (ATOM)psz1, pszClass, WOWCLASS_ATOM_NAME );
    } else {
        pszClass = psz1;
    }

#ifdef WOWEDIT
    if (!_stricmp(pszClass, "Edit"))
    pszClass = "WOWEdit";
#endif

    ul = GETBOOL16(UnregisterClass(
                    pszClass,
                    HMODINST32(parg16->hInstance)
                  ));

    FREEPSZPTR(psz1);
    FREEARGPTR(parg16);
    RETURN(ul);
}