/*++
 *
 *  WOW v1.0
 *
 *  Copyright (c) 1991, Microsoft Corporation
 *
 *  WALIAS.C
 *  WOW32 16-bit handle alias support
 *
 *  History:
 *  Created 27-Jan-1991 by Jeff Parsons (jeffpar)
 *  Modified 12-May-1992 by Mike Tricker (miketri) to add MultiMedia support
--*/

#include "precomp.h"
#pragma hdrstop

MODNAME(walias.c);

extern CRITICAL_SECTION gcsWOW;
extern PTD gptdTaskHead;

//BUGBUG - this must be removed once MM_MCISYSTEM_STRING is defined in MMSYSTEM.H.
#ifndef MM_MCISYSTEM_STRING
    #define MM_MCISYSTEM_STRING 0x3CA
#endif

#ifdef  DEBUG
extern  BOOL fSkipLog;          // TRUE to temporarily skip certain logging
#endif

typedef struct _stdclass {
    LPSTR   lpszClassName;
    ATOM    aClassAtom;
    WNDPROC lpfnWndProc;
    INT     iOrdinal;
    DWORD   vpfnWndProc;
} STDCLASS;

// Some cool defines stolen from USERSRV.H
#define MENUCLASS       MAKEINTATOM(0x8000)
#define DESKTOPCLASS    MAKEINTATOM(0x8001)
#define DIALOGCLASS     MAKEINTATOM(0x8002)
#define SWITCHWNDCLASS  MAKEINTATOM(0x8003)
#define ICONTITLECLASS  MAKEINTATOM(0x8004)

// See WARNING below!
STDCLASS stdClasses[] = {
    NULL,           0,                      NULL,   0,                      0,  // WOWCLASS_UNKNOWN
    NULL,           0,                      NULL,   0,                      0,  // WOWCLASS_WIN16
    "BUTTON",       0,                      NULL,   FUN_BUTTONWNDPROC,      0,  // WOWCLASS_BUTTON,
    "COMBOBOX",     0,                      NULL,   FUN_COMBOBOXCTLWNDPROC, 0,  // WOWCLASS_COMBOBOX,
    "EDIT",         0,                      NULL,   FUN_EDITWNDPROC,        0,  // WOWCLASS_EDIT,
    "LISTBOX",      0,                      NULL,   FUN_LBOXCTLWNDPROC,     0,  // WOWCLASS_LISTBOX,
    "MDICLIENT",    0,                      NULL,   FUN_MDICLIENTWNDPROC,   0,  // WOWCLASS_MDICLIENT,
    "SCROLLBAR",    0,                      NULL,   FUN_SBWNDPROC,          0,  // WOWCLASS_SCROLLBAR,
    "STATIC",       0,                      NULL,   FUN_STATICWNDPROC,      0,  // WOWCLASS_STATIC,
    "#32769",       (WORD)DESKTOPCLASS,     NULL,   FUN_DESKTOPWNDPROC,     0,  // WOWCLASS_DESKTOP,
    "#32770",       (WORD)DIALOGCLASS,      NULL,   FUN_DEFDLGPROCTHUNK,    0,  // WOWCLASS_DIALOG,
    "#32772",       (WORD)ICONTITLECLASS,   NULL,   FUN_TITLEWNDPROC,       0,  // WOWCLASS_ICONTITLE,
    "#32768",       (WORD)MENUCLASS,        NULL,   FUN_MENUWNDPROC,        0,  // WOWCLASS_MENU,
    "#32771",       (WORD)SWITCHWNDCLASS,   NULL,   0,                      0,  // WOWCLASS_SWITCHWND,
    "COMBOLBOX",    0,                      NULL,   FUN_LBOXCTLWNDPROC,     0,  // WOWCLASS_COMBOLBOX
};
//
// WARNING! The above sequence and values must be maintained otherwise the
// table in WMSG16.C for message thunking must be changed.  Same goes for
// the #define's in WALIAS.H
//
// The above COMBOLBOX case is special because it is class that is
// almost identical to a listbox.  Therefore we lie about it.

INT GetStdClassNumber(
    PSZ pszClass
) {
    INT     i;

    if ( HIWORD(pszClass) ) {

        // They passed us a string

        for ( i = WOWCLASS_BUTTON; i < NUMEL(stdClasses); i++ ) {
            if ( WOW32_stricmp(pszClass, stdClasses[i].lpszClassName) == 0 ) {
                return( i );
            }
        }
    } else {

        // They passed us an atom

        for ( i = WOWCLASS_BUTTON; i < NUMEL(stdClasses); i++ ) {
            if ( stdClasses[i].aClassAtom == 0 ) {
                // RegisterWindowMessage is an undocumented way of determining
                // an atom value in the context of the server-side heap.
                stdClasses[i].aClassAtom = (ATOM)RegisterWindowMessage(stdClasses[i].lpszClassName);
            }
            if ( (ATOM)LOWORD(pszClass) == stdClasses[i].aClassAtom ) {
                return( i );
            }
        }
    }
    return( WOWCLASS_WIN16 );  // private 16-bit class created by the app
}

// Returns a 32 window proc given a class index

WNDPROC GetStdClassWndProc(
    DWORD   iClass
) {
    WNDPROC lpfn32;

    if ( iClass < WOWCLASS_WIN16 || iClass > WOWCLASS_MAX ) {
        WOW32ASSERT(FALSE);
        return( NULL );
    }

    lpfn32 = stdClasses[iClass].lpfnWndProc;

    if ( lpfn32 == NULL ) {
        WNDCLASS    wc;
        BOOL        f;

        f = GetClassInfo( NULL, stdClasses[iClass].lpszClassName, &wc );

        if ( f ) {
            VPVOID  vp;
       DWORD UNALIGNED * lpdw;

            lpfn32 = wc.lpfnWndProc;
            stdClasses[iClass].lpfnWndProc = lpfn32;

            vp = GetStdClassThunkProc(iClass);
            vp = (VPVOID)((DWORD)vp - sizeof(DWORD)*3);

            GETVDMPTR( vp, sizeof(DWORD)*3, lpdw );

            WOW32ASSERT(*lpdw == SUBCLASS_MAGIC);   // Are we editing the right stuff?

            if (!lpdw)
                *(lpdw+2) = (DWORD)lpfn32;

            FLUSHVDMCODEPTR( vp, sizeof(DWORD)*3, lpdw );
            FREEVDMPTR( lpdw );

        }
    }
    return( lpfn32 );
}

// Returns a 16 window proc thunk given a class index

DWORD GetStdClassThunkProc(
    INT     iClass
) {
    DWORD   dwResult;
    SHORT   iOrdinal;
    PARM16  Parm16;

    if ( iClass < WOWCLASS_WIN16 || iClass > WOWCLASS_MAX ) {
        WOW32ASSERT(FALSE);
        return( 0 );
    }

    iOrdinal = (SHORT)stdClasses[iClass].iOrdinal;

    if ( iOrdinal == 0 ) {
        return( (DWORD)NULL );
    }

    // If we've already gotten this proc, then don't bother calling into 16-bit
    dwResult = stdClasses[iClass].vpfnWndProc;

    if ( dwResult == (DWORD)NULL ) {

        // Callback into the 16-bit world asking for the 16:16 address

        Parm16.SubClassProc.iOrdinal = iOrdinal;

        if (!CallBack16(RET_SUBCLASSPROC, &Parm16, (VPPROC)NULL,
                          (PVPVOID)&dwResult)) {
            WOW32ASSERT(FALSE);
            return( 0 );
        }
        // Save it since it is a constant.
        stdClasses[iClass].vpfnWndProc = dwResult;
    }
    return( dwResult );
}

/*
 * PWC GetClassWOWWords(hInst, pszClass)
 *   is a ***private*** API for WOW only. It returns a pointer to the
 *   WOW Class structure in the server's window class structure.
 *   This is similar to GetClassLong(hwnd32, GCL_WOWWORDS) (see FindPWC),
 *   but in this case we don't have a hwnd32, we have the class name
 *   and instance handle.
 */

PWC FindClass16(LPCSTR pszClass, HAND16 hInst)
{
    register PWC pwc;

    pwc = (PWC)(pfnOut.pfnGetClassWOWWords)(HMODINST32(hInst), pszClass);
    WOW32WARNMSGF(
        pwc,
        ("WOW32 warning: GetClassWOWWords('%s', %04x) returned NULL\n", pszClass, hInst)
        );

    return (pwc);
}



#ifdef DEBUG

INT nAliases;
INT iLargestListSlot;

PSZ apszHandleClasses[] = {
    "Unknown",      // WOWCLASS_UNKNOWN
    "Window",       // WOWCLASS_WIN16
    "Button",       // WOWCLASS_BUTTON
    "ComboBox",     // WOWCLASS_COMBOBOX
    "Edit",         // WOWCLASS_EDIT
    "ListBox",      // WOWCLASS_LISTBOX
    "MDIClient",    // WOWCLASS_MDICLIENT
    "Scrollbar",    // WOWCLASS_SCROLLBAR
    "Static",       // WOWCLASS_STATIC
    "Desktop",      // WOWCLASS_DESKTOP
    "Dialog",       // WOWCLASS_DIALOG
    "Menu",         // WOWCLASS_MENU
    "IconTitle",    // WOWCLASS_ICONTITLE
    "Accel",        // WOWCLASS_ACCEL
    "Cursor",       // WOWCLASS_CURSOR
    "Icon",         // WOWCLASS_ICON
    "DC",           // WOWCLASS_DC
    "Font",         // WOWCLASS_FONT
    "MetaFile",     // WOWCLASS_METAFILE
    "Region",       // WOWCLASS_RGN
    "Bitmap",       // WOWCLASS_BITMAP
    "Brush",        // WOWCLASS_BRUSH
    "Palette",      // WOWCLASS_PALETTE
    "Pen",          // WOWCLASS_PEN
    "Object"        // WOWCLASS_OBJECT
};


BOOL MessageNeedsThunking(UINT uMsg)
{
    switch (uMsg) {
        case WM_CREATE:
        case WM_ACTIVATE:
        case WM_SETFOCUS:
        case WM_KILLFOCUS:
        case WM_SETTEXT:
        case WM_GETTEXT:
        case WM_ERASEBKGND:
        case WM_WININICHANGE:
        case WM_DEVMODECHANGE:
        case WM_ACTIVATEAPP:
        case WM_SETCURSOR:
        case WM_MOUSEACTIVATE:
        case WM_GETMINMAXINFO:
        case WM_ICONERASEBKGND:
        case WM_NEXTDLGCTL:
        case WM_DRAWITEM:
        case WM_MEASUREITEM:
        case WM_DELETEITEM:
        case WM_VKEYTOITEM:
        case WM_CHARTOITEM:
        case WM_SETFONT:
        case WM_GETFONT:
        case WM_QUERYDRAGICON:
        case WM_COMPAREITEM:
        case WM_OTHERWINDOWCREATED:
        case WM_OTHERWINDOWDESTROYED:
        case WM_COMMNOTIFY:
        case WM_WINDOWPOSCHANGING:
        case WM_WINDOWPOSCHANGED:
        case WM_NCCREATE:
        case WM_NCCALCSIZE:
        case WM_COMMAND:
        case WM_HSCROLL:
        case WM_VSCROLL:
        case WM_INITMENU:
        case WM_INITMENUPOPUP:
        case WM_MENUSELECT:
        case WM_MENUCHAR:
        case WM_ENTERIDLE:
        case WM_CTLCOLORMSGBOX:
        case WM_CTLCOLOREDIT:
        case WM_CTLCOLORLISTBOX:
        case WM_CTLCOLORBTN:
        case WM_CTLCOLORDLG:
        case WM_CTLCOLORSCROLLBAR:
        case WM_CTLCOLORSTATIC:
        case WM_PARENTNOTIFY:
        case WM_MDICREATE:
        case WM_MDIDESTROY:
        case WM_MDIACTIVATE:
        case WM_MDIGETACTIVE:
        case WM_MDISETMENU:
        case WM_RENDERFORMAT:
        case WM_PAINTCLIPBOARD:
        case WM_VSCROLLCLIPBOARD:
        case WM_SIZECLIPBOARD:
        case WM_ASKCBFORMATNAME:
        case WM_CHANGECBCHAIN:
        case WM_HSCROLLCLIPBOARD:
        case WM_PALETTEISCHANGING:
        case WM_PALETTECHANGED:
        case MM_JOY1MOVE:
        case MM_JOY2MOVE:
        case MM_JOY1ZMOVE:
        case MM_JOY2ZMOVE:
        case MM_JOY1BUTTONDOWN:
        case MM_JOY2BUTTONDOWN:
        case MM_JOY1BUTTONUP:
        case MM_JOY2BUTTONUP:
        case MM_MCINOTIFY:
        case MM_MCISYSTEM_STRING:
        case MM_WOM_OPEN:
        case MM_WOM_CLOSE:
        case MM_WOM_DONE:
        case MM_WIM_OPEN:
        case MM_WIM_CLOSE:
        case MM_WIM_DATA:
        case MM_MIM_OPEN:
        case MM_MIM_CLOSE:
        case MM_MIM_DATA:
        case MM_MIM_LONGDATA:
        case MM_MIM_ERROR:
        case MM_MIM_LONGERROR:
        case MM_MOM_OPEN:
        case MM_MOM_CLOSE:
        case MM_MOM_DONE:
            LOGDEBUG(LOG_IMPORTANT,
                ("MessageNeedsThunking: WM_msg %04x is not thunked\n", uMsg));
            return TRUE;

        default:
            return FALSE;

    }
}

#endif


PTD ThreadProcID32toPTD(DWORD dwThreadID, DWORD dwProcessID)
{
    PTD ptd, ptdThis;
    PWOAINST pWOA;

    //
    // If we have active child instances of WinOldAp,
    // try to map the process ID of a child Win32 app
    // to the corresponding WinOldAp PTD.
    //

    ptdThis = CURRENTPTD();

    EnterCriticalSection(&ptdThis->csTD);

    pWOA = ptdThis->pWOAList;

    while (pWOA && pWOA->dwChildProcessID != dwProcessID) {
        pWOA = pWOA->pNext;
    }

    if (pWOA) {

        ptd = pWOA->ptdWOA;

        LeaveCriticalSection(&ptdThis->csTD);

    } else {

        LeaveCriticalSection(&ptdThis->csTD);

        //
        // We didn't find a WinOldAp PTD to return, see
        // if the thread ID matches one of our app threads.
        //

        EnterCriticalSection(&gcsWOW);

        ptd = gptdTaskHead;

        while (ptd && ptd->dwThreadID != dwThreadID) {
            ptd = ptd->ptdNext;
        }

        LeaveCriticalSection(&gcsWOW);
    }

    return ptd;

}

PTD Htask16toPTD(
    HTASK16 htask16
) {
    PTD  ptd;

    EnterCriticalSection(&gcsWOW);

    ptd = gptdTaskHead;

    while(ptd) {

        if ( ptd->htask16 == htask16 ) {
            break;
        }
        ptd = ptd->ptdNext;
    }

    LeaveCriticalSection(&gcsWOW);

    return ptd;
}


HTASK16 ThreadID32toHtask16(
    DWORD   ThreadID32
) {
    PTD ptd;
    HTASK16 htask16;


    if ( ThreadID32 == 0 ) {
        WOW32ASSERTMSG(ThreadID32, "WOW::ThreadID32tohTask16: Thread ID is 0\n");
        htask16 = 0;
    } else {

        ptd = ThreadProcID32toPTD( ThreadID32, (DWORD)-1 );
        if ( ptd ) {
            // Good, its one of our wow threads.
            htask16 = ptd->htask16;
        } else {
            // Nope, its is some other 32-bit thread
            htask16 = FindHtaskAlias( ThreadID32 );
            if ( htask16 == 0 ) {
                //
                // See the comment in WOLE2.C for a nice description
                //
                htask16 = AddHtaskAlias( ThreadID32 );
            }
        }
    }

    return htask16;
}

DWORD Htask16toThreadID32(
    HTASK16 htask16
) {
    if ( htask16 == 0 ) {
        return( 0 );
    }

    if ( ISTASKALIAS(htask16) ) {
        return( GetHtaskAlias(htask16,NULL) );
    } else {
        return( THREADID32(htask16) );
    }
}

//***************************************************************************
// GetGCL_HMODULE - returns the valid hmodule if the window corresponds to
//                  a 16bit class else returns the hmodule of 16bit user.exe
//                  if the window is of a standard class.
//
// These cases are required for compatibility sake.
//         apps like VirtualMonitor, hDC etc depend on such behaviour.
//                                                              - Nanduri
//***************************************************************************
WORD gUser16hInstance = 0;

ULONG GetGCL_HMODULE(HWND hwnd)
{
    ULONG    ul;
    PTD      ptd;
    PWOAINST pWOA;
    DWORD    dwProcessID;

    ul = (ULONG)GetClassLong(hwnd, GCL_HMODULE);

    //
    // hMod32 = 0xZZZZ0000
    //

    if (ul != 0 && LOWORD(ul) == 0) {

        //
        // If we have active WinOldAp children, see if this window
        // belongs to a Win32 process spawned by one of the
        // active winoldap's.  If it is, return the hmodule
        // of the corresponding winoldap.  Otherwise we
        // return user.exe's hinstance (why not hmodule?)
        //

        dwProcessID = (DWORD)-1;
        GetWindowThreadProcessId(hwnd, &dwProcessID);

        ptd = CURRENTPTD();

        EnterCriticalSection(&ptd->csTD);

        pWOA = ptd->pWOAList;
        while (pWOA && pWOA->dwChildProcessID != dwProcessID) {
            pWOA = pWOA->pNext;
        }

        if (pWOA) {
            ul = pWOA->ptdWOA->hMod16;
            LOGDEBUG(LOG_ALWAYS, ("WOW32 GetClassLong(0x%x, GWW_HMODULE) returning 0x%04x\n",
                                  hwnd, ul));
        } else {
            ul = (ULONG) gUser16hInstance;
            WOW32ASSERT(ul);
        }

        LeaveCriticalSection(&ptd->csTD);
    }
    else {
        ul = (ULONG)GETHMOD16(ul);      // 32-bit hmod is HMODINST32
    }

    return ul;
}

//
// EXPORTED handle mapping functions.  WOW32 code should use the
// macros defined in walias.h -- these functions are for use by
// third-party 32-bit code running in WOW, for example called
// using generic thunks from WOW-specific 16-bit code.
//

HANDLE WOWHandle32 (WORD h16, WOW_HANDLE_TYPE htype)
{
    switch (htype) {
        case WOW_TYPE_HWND:
            return HWND32(h16);
        case WOW_TYPE_HMENU:
            return HMENU32(h16);
        case WOW_TYPE_HDWP:
            return HDWP32(h16);
        case WOW_TYPE_HDROP:
            return HDROP32(h16);
        case WOW_TYPE_HDC:
            return HDC32(h16);
        case WOW_TYPE_HFONT:
            return HFONT32(h16);
        case WOW_TYPE_HMETAFILE:
            return HMETA32(h16);
        case WOW_TYPE_HRGN:
            return HRGN32(h16);
        case WOW_TYPE_HBITMAP:
            return HBITMAP32(h16);
        case WOW_TYPE_HBRUSH:
            return HBRUSH32(h16);
        case WOW_TYPE_HPALETTE:
            return HPALETTE32(h16);
        case WOW_TYPE_HPEN:
            return HPEN32(h16);
        case WOW_TYPE_HACCEL:
            return HACCEL32(h16);
        case WOW_TYPE_HTASK:
            return (HANDLE)HTASK32(h16);
        case WOW_TYPE_FULLHWND:
            return (HANDLE)FULLHWND32(h16);
        default:
            return(INVALID_HANDLE_VALUE);
    }
}

WORD WOWHandle16 (HANDLE h32, WOW_HANDLE_TYPE htype)
{
    switch (htype) {
        case WOW_TYPE_HWND:
            return GETHWND16(h32);
        case WOW_TYPE_HMENU:
            return GETHMENU16(h32);
        case WOW_TYPE_HDWP:
            return GETHDWP16(h32);
        case WOW_TYPE_HDROP:
            return GETHDROP16(h32);
        case WOW_TYPE_HDC:
            return GETHDC16(h32);
        case WOW_TYPE_HFONT:
            return GETHFONT16(h32);
        case WOW_TYPE_HMETAFILE:
            return GETHMETA16(h32);
        case WOW_TYPE_HRGN:
            return GETHRGN16(h32);
        case WOW_TYPE_HBITMAP:
            return GETHBITMAP16(h32);
        case WOW_TYPE_HBRUSH:
            return GETHBRUSH16(h32);
        case WOW_TYPE_HPALETTE:
            return GETHPALETTE16(h32);
        case WOW_TYPE_HPEN:
            return GETHPEN16(h32);
        case WOW_TYPE_HACCEL:
            return GETHACCEL16(h32);
        case WOW_TYPE_HTASK:
            return GETHTASK16(h32);
        default:
            return(0xffff);
    }
}

PVOID gpGdiHandleInfo = (PVOID)-1;

//WARNING: This structure must match ENTRY in ntgdi\inc\hmgshare.h

typedef struct _ENTRYWOW
{
    LONG   l1;
    LONG   l2;
    USHORT FullUnique;
    USHORT us1;
    LONG   l3;
} ENTRYWOW, *PENTRYWOW;

//
// this routine converts a 16bit GDI handle to a 32bit handle.  There
// is no need to do any validation on the handle since the 14bit space
// for handles ignoring the low two bits is completely contained in the
// valid 32bit handle space.
//

HANDLE hConvert16to32(int h16)
{
    ULONG h32;
    int i = h16 >> 2;

    h32 = i | (ULONG)(((PENTRYWOW)gpGdiHandleInfo)[i].FullUnique) << 16;

    return((HANDLE)h32);
}


// Implemented as temporary fix for Whistler Bug #435401
// Should be fixed for Blackcomb or sooner
extern HANDLE hmodWOW32;

HAND16 hConvert32to16(DWORD h32)
{
    PARM16 Parm16;
    VPVOID vp = 0;    

    // see if handle is over 14-bits
    if(LOWORD(h32) > 0x00003FFF) {
       char szErrorMessage[512];
       char szTitle[512];
       char szModName[9];
       PTDB pTDB;

       pTDB = (PVOID)SEGPTR(CURRENTPTD()->htask16,0);

       RtlCopyMemory(szModName, pTDB->TDB_ModName, sizeof(szModName)-1);
       szModName[sizeof(szModName)-1] = 0;
       if (!LoadString(hmodWOW32, iszApplication,
               szTitle, sizeof(szTitle)/sizeof(CHAR)))
       {
           szTitle[0] = 0;
       }
       strcat(szTitle, szModName);

       LoadString(hmodWOW32,
                  iszExceedGDIHandleLimit,
                  szErrorMessage,
                  sizeof szErrorMessage);

       // The GDI32 handle allocation has gone over the 16K threshhold
       // This thread is toast.
       WOWSysErrorBox(szTitle, szErrorMessage, SEB_OK | SEB_DEFBUTTON, 0, 0);

       LOGDEBUG(LOG_ALWAYS,
                ("GDI32 handle limit exceeded for Task %04X \n",
                CURRENTPTD()->htask16));

       CallBack16(RET_FORCETASKEXIT, &Parm16, 0, &vp);

       // this should never return
       return(0);
    }

    // everything is hunky dory
    return((HAND16)(((DWORD) (h32)) << 2));
}

// We probably don't need to worry about this buffer being too small since we're
// really only interested in the predefined standard classes which tend to
// be rather short-named.
#define MAX_CLASSNAME_LEN  64

// There is a time frame (from when an app calls CreateWindow until USER32 gets
// a message at one of its WndProc's for the window - see FritzS) during which
// the fnid (class type) can't be set officially for the window.  If the
// GETICLASS macro is invoked during this period, it will be unable to find the
// iClass for windows created on any of the standard control classes using the
// fast fnid index method (see walias.h).  Once the time frame is passed, the
// fast fnid method will work fine for these windows.
//
// This is manifested in apps that set CBT hooks and try to subclass the
// standard classes while in their hook proc.  See bug #143811.

INT GetIClass(PWW pww, HWND hwnd)
{
    INT   iClass;
    DWORD dwClassAtom;

    // if it is a standard class
    if(((pww->fnid & 0xfff) >= FNID_START) &&
                 ((pww->fnid & 0xfff) <= FNID_END)) {

        // return the class id for this initialized window
        iClass = pfnOut.aiWowClass[( pww->fnid & 0xfff) - FNID_START];

    }

    else {

       iClass = WOWCLASS_WIN16;       // default return: app private class           
   
       dwClassAtom = GetClassLong(hwnd, GCW_ATOM);
   
       if(dwClassAtom) {
           iClass = GetStdClassNumber((PSZ)dwClassAtom);
       }
    }

    return(iClass);
}