//*****************************************************************************
//
// HOOKS -
//
//     32bit stubs and thunks for 16bit hooks
//
//
// 01-07-92  NanduriR   Created.
//
//*****************************************************************************

#include "precomp.h"
#pragma hdrstop

MODNAME(wowhooks.c);

//*****************************************************************************
//
// Global data.  Data is valid only in WOW process context. DLL Data is not
// Shared amongst various processes. If a WOW hook is set WIN32 USER will call
// the hookproc in the context of the thread that has the hook set. This implies
// that all the global data in this DLL is acessible by every WOW thread.
//
// Just reaffirming: since USER will call the hookprocs in our process/thread
//                   context there is no need for this data to be available
//                   in shared memory.
//
//*****************************************************************************



HOOKPERPROCESSDATA vaHookPPData = { NULL, 0};

//
// In SetWindowsHook we return an index into this array if it was
// successful. Since NULL implies an error, we cannot use the Zeroth element
// in this array. So we set its 'InUse' flag to TRUE.
//


HOOKSTATEDATA vaHookStateData[] = {
           0,  TRUE, 0, NULL, (HKPROC)NULL,                0, 0, 0, NULL,
           0, FALSE, 0, NULL, (HKPROC)WU32SubStdHookProc01,0, 0, 0, NULL,
           0, FALSE, 0, NULL, (HKPROC)WU32SubStdHookProc02,0, 0, 0, NULL,
           0, FALSE, 0, NULL, (HKPROC)WU32SubStdHookProc03,0, 0, 0, NULL,
           0, FALSE, 0, NULL, (HKPROC)WU32SubStdHookProc04,0, 0, 0, NULL,
           0, FALSE, 0, NULL, (HKPROC)WU32SubStdHookProc05,0, 0, 0, NULL,
           0, FALSE, 0, NULL, (HKPROC)WU32SubStdHookProc06,0, 0, 0, NULL,
           0, FALSE, 0, NULL, (HKPROC)WU32SubStdHookProc07,0, 0, 0, NULL,
           0, FALSE, 0, NULL, (HKPROC)WU32SubStdHookProc08,0, 0, 0, NULL,
           0, FALSE, 0, NULL, (HKPROC)WU32SubStdHookProc09,0, 0, 0, NULL,
           0, FALSE, 0, NULL, (HKPROC)WU32SubStdHookProc10,0, 0, 0, NULL,
           0, FALSE, 0, NULL, (HKPROC)WU32SubStdHookProc11,0, 0, 0, NULL,
           0, FALSE, 0, NULL, (HKPROC)WU32SubStdHookProc12,0, 0, 0, NULL,
           0, FALSE, 0, NULL, (HKPROC)WU32SubStdHookProc13,0, 0, 0, NULL,
           0, FALSE, 0, NULL, (HKPROC)WU32SubStdHookProc14,0, 0, 0, NULL,
           0, FALSE, 0, NULL, (HKPROC)WU32SubStdHookProc15,0, 0, 0, NULL
          };


HOOKPARAMS vHookParams = {0, 0, 0};

INT viCurrentHookStateDataIndex = 0;
                                 // this is used when 'DefHookProc' is called.



//*****************************************************************************
// Sub-Standard Hook Procs -
//
// Hook Stubs. The 'index' (the fourth parameter) is an index into the
// the HookStateData array. All needed info is available in this array.
// The hook stubs need to be exported.
//
//*****************************************************************************


LONG APIENTRY WU32SubStdHookProc01(INT nCode, LONG wParam, LONG lParam)
{
    return WU32StdHookProc(nCode, wParam, lParam, 0x01);
}

LONG APIENTRY WU32SubStdHookProc02(INT nCode, LONG wParam, LONG lParam)
{
    return WU32StdHookProc(nCode, wParam, lParam, 0x02);
}

LONG APIENTRY WU32SubStdHookProc03(INT nCode, LONG wParam, LONG lParam)
{
    return WU32StdHookProc(nCode, wParam, lParam, 0x03);
}

LONG APIENTRY WU32SubStdHookProc04(INT nCode, LONG wParam, LONG lParam)
{
    return WU32StdHookProc(nCode, wParam, lParam, 0x04);
}

LONG APIENTRY WU32SubStdHookProc05(INT nCode, LONG wParam, LONG lParam)
{
    return WU32StdHookProc(nCode, wParam, lParam, 0x05);
}

LONG APIENTRY WU32SubStdHookProc06(INT nCode, LONG wParam, LONG lParam)
{
    return WU32StdHookProc(nCode, wParam, lParam, 0x06);
}

LONG APIENTRY WU32SubStdHookProc07(INT nCode, LONG wParam, LONG lParam)
{
    return WU32StdHookProc(nCode, wParam, lParam, 0x07);
}

LONG APIENTRY WU32SubStdHookProc08(INT nCode, LONG wParam, LONG lParam)
{
    return WU32StdHookProc(nCode, wParam, lParam, 0x08);
}

LONG APIENTRY WU32SubStdHookProc09(INT nCode, LONG wParam, LONG lParam)
{
    return WU32StdHookProc(nCode, wParam, lParam, 0x09);
}

LONG APIENTRY WU32SubStdHookProc10(INT nCode, LONG wParam, LONG lParam)
{
    return WU32StdHookProc(nCode, wParam, lParam, 0x0a);
}

LONG APIENTRY WU32SubStdHookProc11(INT nCode, LONG wParam, LONG lParam)
{
    return WU32StdHookProc(nCode, wParam, lParam, 0x0b);
}

LONG APIENTRY WU32SubStdHookProc12(INT nCode, LONG wParam, LONG lParam)
{
    return WU32StdHookProc(nCode, wParam, lParam, 0x0c);
}

LONG APIENTRY WU32SubStdHookProc13(INT nCode, LONG wParam, LONG lParam)
{
    return WU32StdHookProc(nCode, wParam, lParam, 0x0d);
}

LONG APIENTRY WU32SubStdHookProc14(INT nCode, LONG wParam, LONG lParam)
{
    return WU32StdHookProc(nCode, wParam, lParam, 0x0e);
}

LONG APIENTRY WU32SubStdHookProc15(INT nCode, LONG wParam, LONG lParam)
{
    return WU32StdHookProc(nCode, wParam, lParam, 0x0f);
}

//*****************************************************************************
// W32InitHookState:
//
// Initializes the global data. Note that this data is initialized for every
// Process that loads this DLL. However data that is visible only to the 'WOW'
// process is what we care about.
//
//*****************************************************************************


BOOL W32InitHookState(HANDLE hMod)
{
    INT      i;

    vaHookPPData.hMod = hMod;
    vaHookPPData.cHookProcs = sizeof(vaHookStateData) /
                                                 sizeof(vaHookStateData[0]);

    for (i = 0; i < vaHookPPData.cHookProcs; i++) {
         vaHookStateData[i].iIndex = i;
         vaHookStateData[i].hMod = hMod;
    }

    return TRUE;
}



//*****************************************************************************
// W32GetNotInUseHookStateData:
//
// Steps through the Global HookStateData and returns a pointer to an 'unused'
// element. This is called only when the Hook is being Set.
//
//*****************************************************************************


BOOL W32GetNotInUseHookStateData(LPHOOKSTATEDATA lpData)
{
    INT i;
    for (i = 0; i < vaHookPPData.cHookProcs; i++) {
         if (!vaHookStateData[i].InUse) {
             vaHookStateData[i].InUse = TRUE;
             *lpData = vaHookStateData[i];
             return TRUE;
         }
    }
    LOGDEBUG(LOG_ALWAYS, ("W32GetNotInUseHookStateData: All thunk hook procs in use.\n"));
    return FALSE;
}



//*****************************************************************************
// W32SetHookStateData:
//
// Writes into the global data structure at the specified index.
//
//*****************************************************************************

BOOL W32SetHookStateData(LPHOOKSTATEDATA lpData)
{
    vaHookStateData[lpData->iIndex] = *lpData;
    return TRUE;
}

//*****************************************************************************
// W32GetHookStateData:
//
// Retrieves data from the global data structure at the specified index.
//
//*****************************************************************************

BOOL W32GetHookStateData(LPHOOKSTATEDATA lpData)
{
    if ( lpData->iIndex >= 0 && lpData->iIndex < vaHookPPData.cHookProcs ) {
        *lpData = vaHookStateData[lpData->iIndex];
        return TRUE;
    } else {
        return FALSE;
    }
}

//*****************************************************************************
// W32GetThunkHookProc:
//
//     Its callled to find a 32stub for the hook that is being set.
//     Returns TRUE if successful else FALSE.
//     The data is partially updated to reflect the characteristics of the hook
//     that is being set.
//
//*****************************************************************************

BOOL W32GetThunkHookProc(INT iHook, DWORD Proc16, LPHOOKSTATEDATA lpData)
{
    register PTD ptd = CURRENTPTD();

    if (W32GetNotInUseHookStateData(lpData)) {
        lpData->iHook  = iHook;
        lpData->Proc16 = Proc16;
        lpData->TaskId = ptd->htask16 ;
        W32SetHookStateData(lpData);
        return TRUE;
    }
    else
        return FALSE;

}



//*****************************************************************************
// W32IsDuplicateHook:
//
//     Verifies if the given hook has already been set. This is to catch
//     certain apps which go on Setting the same hook without Unhooking the
//     previous hook.
//
//     Returns the 'stubindex' if hook already exists else 0;
//
//*****************************************************************************

INT W32IsDuplicateHook(INT iHook, DWORD Proc16, INT TaskId)
{
    INT i;
    for (i = 0; i < vaHookPPData.cHookProcs; i++) {
         if (vaHookStateData[i].InUse &&
                 vaHookStateData[i].iHook == iHook &&
                 vaHookStateData[i].TaskId == TaskId &&
                 vaHookStateData[i].Proc16 == Proc16      ) {
             return i;
         }
    }

    return 0;
}

//*****************************************************************************
// W32FreeHHook:
//
//     The state of the specified hook is set to 'Not in use'.
//     Returns hHook of the hook being freed.
//
//*****************************************************************************


HHOOK W32FreeHHook(INT iHook, DWORD Proc16)
{

    register PTD ptd = CURRENTPTD();
    INT i;
    for (i = 0; i < vaHookPPData.cHookProcs; i++) {
         if (vaHookStateData[i].InUse &&
             vaHookStateData[i].iHook == iHook &&
             vaHookStateData[i].TaskId == (INT)ptd->htask16 &&
             vaHookStateData[i].Proc16 == Proc16) {
             vaHookStateData[i].InUse = FALSE;
             return vaHookStateData[i].hHook;
         }
    }
    LOGDEBUG(LOG_ALWAYS, ("W32FreeHHook: Couldn't locate the specified hook."));
    return (HHOOK)NULL;
}




//*****************************************************************************
// W32FreeHHookOfIndex:
//
//     The state of the specified hook is set to 'Not in use'.
//     Returns hHook of the hook being freed.
//
//*****************************************************************************


HHOOK W32FreeHHookOfIndex(INT iFunc)
{
    register PTD ptd = CURRENTPTD();

    if (iFunc && iFunc < vaHookPPData.cHookProcs)
        if (vaHookStateData[iFunc].InUse &&
               vaHookStateData[iFunc].TaskId == (INT)ptd->htask16) {
        vaHookStateData[iFunc].InUse = FALSE;
        return vaHookStateData[iFunc].hHook;
    }
    LOGDEBUG(LOG_ALWAYS, ("W32FreeHHookOfIndex: Couldn't locate the specified hook."));
    return (HHOOK)NULL;
}




//*****************************************************************************
// W32GetHookParams:
//
//     Returns the 32bit hookparams of the current hook.
//
//*****************************************************************************


BOOL W32GetHookParams(LPHOOKPARAMS lpHookParams)
{
    if (lpHookParams) {
        *lpHookParams = vHookParams;
    }

    return (BOOL)lpHookParams;
}



//*****************************************************************************
// W32StdHookProc: (Standard Hook Proc)
//
//     All the stubs call this proc.
//     Return value is dependent on the hook type.
//
//*****************************************************************************

LONG APIENTRY WU32StdHookProc(INT nCode, LONG wParam, LONG lParam, INT iFunc)
{
    //
    // USER will call us in WOW context. Just Verify.
    //

    if (!vaHookStateData[iFunc].InUse) {
        // DbgPrint("WU32StdHookProc:Stub %04x called in WRONG process\n", iFunc);
        return FALSE;
    }

    //
    // USER can call us even if we have GP Faulted - so just ignore the input
    //

    if (CURRENTPTD()->gfIgnoreInput) {
        LOGDEBUG(LOG_ALWAYS,("WU32StdHookProc Ignoring Input\n"));
        return FALSE;
    }

    //
    // store the stubindex. If the hookproc calls the DefHookProc() we will
    // be able to findout the Hook Type.
    //

    viCurrentHookStateDataIndex = iFunc;

    //
    // store the hook params. These will be used if DefHookProc gets called
    // by the 16bit stub. We are interested only in lParam.
    //

    vHookParams.nCode = nCode;
    vHookParams.wParam = wParam;
    vHookParams.lParam = lParam;

    switch (vaHookStateData[iFunc].iHook) {
        case WH_CALLWNDPROC:
            return ThunkCallWndProcHook(nCode, wParam, (LPCWPSTRUCT)lParam,
                                                   &vaHookStateData[iFunc]);

        case WH_CBT:

            return ThunkCbtHook(nCode, wParam, lParam,
                                                   &vaHookStateData[iFunc]);


        case WH_KEYBOARD:
            return ThunkKeyBoardHook(nCode, wParam, lParam,
                                                   &vaHookStateData[iFunc]);

        case WH_MSGFILTER:

            // This code is TEMP only and must be fixed. ChandanC 5/2/92.
            if ((WORD)vaHookStateData[iFunc].TaskId !=
                       (WORD)((PTD)CURRENTPTD())->htask16)
                    break;

            WOW32ASSERT((WORD)vaHookStateData[iFunc].TaskId ==
                       (WORD)((PTD)CURRENTPTD())->htask16);
        case WH_SYSMSGFILTER:
        case WH_GETMESSAGE:

            return ThunkMsgFilterHook(nCode, wParam, (LPMSG)lParam,
                               &vaHookStateData[iFunc]);
            break;

        case WH_JOURNALPLAYBACK:
        case WH_JOURNALRECORD:
            return ThunkJournalHook(nCode, wParam, (LPEVENTMSG)lParam,
                                                   &vaHookStateData[iFunc]);

        case WH_DEBUG:
            return ThunkDebugHook(nCode, wParam, lParam,
                                                   &vaHookStateData[iFunc]);


        case WH_MOUSE:
            return ThunkMouseHook(nCode, wParam, (LPMOUSEHOOKSTRUCT)lParam,
                                                   &vaHookStateData[iFunc]);

        case WH_SHELL:
            return ThunkShellHook(nCode, wParam, lParam,
                                                   &vaHookStateData[iFunc]);

        default:
            LOGDEBUG(LOG_ALWAYS,("W32StdHookProc: Unknown Hook type."));
    }

    return (LONG)FALSE;

}



//*****************************************************************************
//
// ThunkHookProc for Hooks of type WH_CALLWNDPROC -
//
//     Return type is VOID.
//
//*****************************************************************************


LONG ThunkCallWndProcHook(INT nCode, LONG wParam, LPCWPSTRUCT lpCwpStruct,
                                                     LPHOOKSTATEDATA lpHSData)
{
    VPVOID      vp;
    PCWPSTRUCT16 pCwpStruct16;
    PARM16 Parm16;
    WM32MSGPARAMEX wm32mpex;
    BOOL   fMessageNeedsThunking;

    wm32mpex.Parm16.WndProc.wMsg   = (WORD) lpCwpStruct->message;
    wm32mpex.Parm16.WndProc.wParam = (WORD) lpCwpStruct->wParam;
    wm32mpex.Parm16.WndProc.lParam = (LONG) lpCwpStruct->lParam;

    fMessageNeedsThunking =  (lpCwpStruct->message < 0x400) &&
                                  (aw32Msg[lpCwpStruct->message].lpfnM32 != WM32NoThunking);
    if (fMessageNeedsThunking) {

        LOGDEBUG(3,("%04X (%s)\n", CURRENTPTD()->htask16, (aw32Msg[lpCwpStruct->message].lpszW32)));

        wm32mpex.fThunk = THUNKMSG;
        wm32mpex.hwnd   = lpCwpStruct->hwnd;
        wm32mpex.uMsg   = lpCwpStruct->message;
        wm32mpex.uParam = lpCwpStruct->wParam;
        wm32mpex.lParam = lpCwpStruct->lParam;
        wm32mpex.lpfnM32 = aw32Msg[wm32mpex.uMsg].lpfnM32;
        wm32mpex.pww = (PWW)NULL;
        if (!(wm32mpex.lpfnM32)(&wm32mpex)) {
            LOGDEBUG(LOG_ALWAYS,("ThunkCallWndProcHook: cannot thunk 32-bit message %04x\n",
                    lpCwpStruct->message));
        }
    }

    vp = stackalloc16(sizeof(CWPSTRUCT16));

    GETMISCPTR(vp, pCwpStruct16);

    STOREWORD(pCwpStruct16->hwnd, GETHWND16(lpCwpStruct->hwnd));
    STOREWORD(pCwpStruct16->message, wm32mpex.Parm16.WndProc.wMsg  );
    STOREWORD(pCwpStruct16->wParam,  wm32mpex.Parm16.WndProc.wParam);
    STORELONG(pCwpStruct16->lParam,  wm32mpex.Parm16.WndProc.lParam);

    FLUSHVDMPTR(vp, sizeof(CWPSTRUCT16), pCwpStruct16);
    FREEVDMPTR(pCwpStruct16);

    Parm16.HookProc.nCode = (SHORT)nCode;
    Parm16.HookProc.wParam = (SHORT)wParam;
    Parm16.HookProc.lParam = vp;

    CallBack16(RET_HOOKPROC, &Parm16, lpHSData->Proc16, (PVPVOID)&wm32mpex.lReturn);

#ifdef DEBUG
    GETMISCPTR(vp, pCwpStruct16);

    if (pCwpStruct16->message != wm32mpex.Parm16.WndProc.wMsg)
        LOGDEBUG(LOG_ALWAYS,("ThunkCallWndProcHook: IN message != OUT message"));

    FREEVDMPTR(pCwpStruct16);
#endif

    if (fMessageNeedsThunking) {

        wm32mpex.fThunk = UNTHUNKMSG;
        (wm32mpex.lpfnM32)(&wm32mpex);
    }

    stackfree16(vp);

    return (LONG)FALSE;   // return value doesn't matter
}



//*****************************************************************************
//
// ThunkHookProc for Hooks of type WH_CBT -
//
//     Return type is BOOL.
//
//*****************************************************************************

LONG ThunkCbtHook(INT nCode, LONG wParam, LONG lParam,
                                                     LPHOOKSTATEDATA lpHSData)
{
    LONG lReturn = FALSE;
    PARM16 Parm16;

    VPVOID vp;
    PMOUSEHOOKSTRUCT16 pMHStruct16;
    PCBTACTIVATESTRUCT16 pCbtAStruct16;
    VPCREATESTRUCT16     vpcs16;
    PCBT_CREATEWND16     pCbtCWnd16;
    WM32MSGPARAMEX       wm32mpex;

    Parm16.HookProc.nCode = (SHORT)nCode;
    Parm16.HookProc.wParam = (SHORT)wParam;
    Parm16.HookProc.lParam = lParam;

    switch(nCode) {
        case HCBT_MOVESIZE:

            // wParam = HWND, lParam = LPRECT

            Parm16.HookProc.wParam = GETHWND16(wParam);
            vp = stackalloc16(sizeof(RECT16));

            PUTRECT16(vp, (LPRECT)lParam);

            Parm16.HookProc.lParam = vp;

            break;

        case HCBT_MINMAX:

            // wParam = HWND, lParam = SW_*  --- a command

            Parm16.HookProc.wParam = GETHWND16(wParam);
            break;

        case HCBT_QS:

            // wParam = 0, lParam = 0

            break;

        case HCBT_CREATEWND:

            // wParam = HWND, lParam = LPCBT_CREATEWND

            wm32mpex.fThunk = THUNKMSG;
            wm32mpex.hwnd = 0;
            wm32mpex.uMsg = 0;
            wm32mpex.uParam = 0;
            wm32mpex.lParam = (LONG)((LPCBT_CREATEWND)lParam)->lpcs;
            wm32mpex.pww = (PWW)NULL;
            if (!WM32Create(&wm32mpex))
                return FALSE;
            vpcs16 = wm32mpex.Parm16.WndProc.lParam;
            lReturn = wm32mpex.lReturn;

            vp = stackalloc16(sizeof(CBT_CREATEWND16));

            GETMISCPTR(vp, pCbtCWnd16);
            STOREDWORD(pCbtCWnd16->vpcs, vpcs16);
            STOREWORD(pCbtCWnd16->hwndInsertAfter,
            GETHWNDIA16(((LPCBT_CREATEWND)lParam)->hwndInsertAfter));

            Parm16.HookProc.wParam = GETHWND16(wParam);
            Parm16.HookProc.lParam = vp;

            FLUSHVDMPTR(vp, sizeof(CBT_CREATEWND16), pCbtCWnd16);
            FREEVDMPTR(pCbtCWnd16);
            break;

        case HCBT_DESTROYWND:

            // wParam = HWND, lParam = 0

            Parm16.HookProc.wParam = GETHWND16(wParam);
            break;

        case HCBT_ACTIVATE:

            // wParam = HWND, lParam = LPCBTACTIVATESTRUCT

            vp = stackalloc16(sizeof(CBTACTIVATESTRUCT16));

            GETMISCPTR(vp, pCbtAStruct16);
            PUTCBTACTIVATESTRUCT16(pCbtAStruct16, ((LPCBTACTIVATESTRUCT)lParam));

            Parm16.HookProc.wParam = GETHWND16(wParam);
            Parm16.HookProc.lParam = vp;

            FLUSHVDMPTR(vp, sizeof(CBTACTIVATESTRUCT16), pCbtAStruct16);
            FREEVDMPTR(pCbtAStruct16);
            break;

        case HCBT_CLICKSKIPPED:

            // wParam = mouse message, lParam = LPMOUSEHOOKSTRUCT

            vp = stackalloc16(sizeof(MOUSEHOOKSTRUCT16));

            GETMISCPTR(vp, pMHStruct16);
            PUTMOUSEHOOKSTRUCT16(pMHStruct16, (LPMOUSEHOOKSTRUCT)lParam);

            Parm16.HookProc.lParam = vp;

            FLUSHVDMPTR(vp, sizeof(MOUSEHOOKSTRUCT16), pMHStruct16);
            FREEVDMPTR(pMHStruct16);
            break;

        case HCBT_KEYSKIPPED:

            // wParam, lParam   -- keyup/down message params

            break;

        case HCBT_SYSCOMMAND:

            // wParam = SC_ syscomand, lParam = DWORD(x,y)

            break;

        case HCBT_SETFOCUS:

            // wParam = HWND, lParam = HWND

            Parm16.HookProc.wParam = GETHWND16(wParam);
            Parm16.HookProc.lParam = GETHWND16(lParam);
            break;

        default:
            LOGDEBUG(LOG_ALWAYS, ("ThunkCbtHook: Unknown HCBT_ code\n"));
            break;
    }

    CallBack16(RET_HOOKPROC, &Parm16, lpHSData->Proc16, (PVPVOID)&lReturn);

    switch(nCode) {
        case HCBT_MOVESIZE:

            GETRECT16(vp, (LPRECT)lParam);
            stackfree16(vp);
            break;

        case HCBT_CREATEWND:
            GETMISCPTR(vp, pCbtCWnd16);
            ((LPCBT_CREATEWND)lParam)->hwndInsertAfter =
                               HWNDIA32(FETCHWORD(pCbtCWnd16->hwndInsertAfter));
            FREEVDMPTR(pCbtCWnd16);
            wm32mpex.fThunk = UNTHUNKMSG;
            wm32mpex.lReturn = lReturn;
            WM32Create(&wm32mpex);
            lReturn = wm32mpex.lReturn;
            stackfree16(vp);
            break;


        case HCBT_ACTIVATE:

            GETMISCPTR(vp, pCbtAStruct16);
            GETCBTACTIVATESTRUCT16(pCbtAStruct16, (LPCBTACTIVATESTRUCT)lParam);
            FREEVDMPTR(pCbtAStruct16);
            stackfree16(vp);
            break;

        case HCBT_CLICKSKIPPED:

            GETMISCPTR(vp, pMHStruct16);
            GETMOUSEHOOKSTRUCT16(pMHStruct16, (LPMOUSEHOOKSTRUCT)lParam);
            FREEVDMPTR(pMHStruct16);
            stackfree16(vp);
            break;

        // case HCBT_MINMAX:
        // case HCBT_QS:
        // case HCBT_DESTROYWND:
        // case HCBT_KEYSKIPPED:
        // case HCBT_SYSCOMMAND:
        // case HCBT_SETFOCUS:

        default:
            break;
    }

    // the value in LOWORD is the valid return value

    return (LONG)(BOOL)LOWORD(lReturn);
}



//*****************************************************************************
//
// ThunkHookProc for Hooks of type WH_KEYBOARD -
//
//     Return type is BOOL.
//
//*****************************************************************************

LONG ThunkKeyBoardHook(INT nCode, LONG wParam, LONG lParam,
                                                     LPHOOKSTATEDATA lpHSData)
{
    LONG lReturn;
    PARM16 Parm16;

    Parm16.HookProc.nCode = (SHORT)nCode;
    Parm16.HookProc.wParam = (SHORT)wParam;
    Parm16.HookProc.lParam = lParam;

    CallBack16(RET_HOOKPROC, &Parm16, lpHSData->Proc16, (PVPVOID)&lReturn);

    // the value in LOWORD is the valid return value

    return (LONG)(BOOL)LOWORD(lReturn);
}



//*****************************************************************************
//
// ThunkHookProc for Hooks of type WH_GETMESSAGE -
//                                 WH_MSGFILTER -
//                                 WH_SYSMSGFILTER -
//
//     Return type is BOOL.
//
//*****************************************************************************

LONG ThunkMsgFilterHook(INT nCode, LONG wParam, LPMSG lpMsg,
                                                     LPHOOKSTATEDATA lpHSData)
{
    VPVOID vp;
    PMSG16 pMsg16;
    PARM16 Parm16;
    WM32MSGPARAMEX wm32mpex;

    BOOL fHookModifiedLpMsg;
    HWND hwnd32;
    MSGPARAMEX mpex;
    BOOL fUseOld;
    static MSG msgSave;
    static int cRecurse = 0;
    static PARM16 Parm16Save;
    BOOL   fMessageNeedsThunking;

    // This should be removed when all thunk functions appropiately fill these
    // in, till then we must have these 3 lines before calling thunk functions !
    // ChandanC, 2/28/92
    //

    wm32mpex.Parm16.WndProc.wMsg   = (WORD) lpMsg->message;
    wm32mpex.Parm16.WndProc.wParam = (WORD) lpMsg->wParam;
    wm32mpex.Parm16.WndProc.lParam = (LONG) lpMsg->lParam;

    fMessageNeedsThunking =  (lpMsg->message < 0x400) &&
                                  (aw32Msg[lpMsg->message].lpfnM32 != WM32NoThunking);
    fFreeDDElParam = FALSE;

    if (fMessageNeedsThunking) {
        LOGDEBUG(3,("%04X (%s)\n", CURRENTPTD()->htask16,
                                           (aw32Msg[lpMsg->message].lpszW32)));

        wm32mpex.fThunk = THUNKMSG;
        wm32mpex.hwnd   = lpMsg->hwnd;
        wm32mpex.uMsg   = lpMsg->message;
        wm32mpex.uParam = lpMsg->wParam;
        wm32mpex.lParam = lpMsg->lParam;
        wm32mpex.pww = (PWW)NULL;
        wm32mpex.fFree = FALSE;
        wm32mpex.lpfnM32 = aw32Msg[wm32mpex.uMsg].lpfnM32;
        if (!((wm32mpex.lpfnM32)(&wm32mpex))) {
            LOGDEBUG(LOG_ALWAYS,("ThunkMsgFilterHook: cannot thunk 32-bit message %04x\n",
                                                             lpMsg->message));
        }
    }

    fFreeDDElParam = TRUE;

    //
    // Check to see if we've recursed into this routine. If so and the message
    // contents are the same, use the last message pointer we gave the 16
    // bit app rather than allocating a new one. Need this to solve the
    // SoundBits Browser problem. It checks to see if the lpmsg passed
    // is the same as it got last time - if so, it doesn't call this other
    // function that causes recursion.
    //

    fUseOld = FALSE;

    if (cRecurse != 0 && lpMsg->hwnd == msgSave.hwnd &&
                lpMsg->message == msgSave.message &&
                lpMsg->wParam == msgSave.wParam &&
                lpMsg->lParam == msgSave.lParam &&
                lpMsg->time == msgSave.time &&
                lpMsg->pt.x == msgSave.pt.x &&
                lpMsg->pt.y == msgSave.pt.y) {

        fUseOld = TRUE;

    } else {
        //
        // Not the same... reset this thing in case the count is screwed
        // up. Also remember this message, in case we do recurse.
        //

        cRecurse = 0;
        msgSave = *lpMsg;
    }

    if (!fUseOld) {
        vp = malloc16(sizeof(MSG16));
        if (vp == (VPVOID)NULL)
            return FALSE;

        GETMISCPTR(vp, pMsg16);
        STOREWORD(pMsg16->hwnd, GETHWND16((lpMsg)->hwnd));
        STOREWORD(pMsg16->message, wm32mpex.Parm16.WndProc.wMsg  );
        STOREWORD(pMsg16->wParam,  wm32mpex.Parm16.WndProc.wParam);
        STORELONG(pMsg16->lParam,  wm32mpex.Parm16.WndProc.lParam);
        STORELONG(pMsg16->time, (lpMsg)->time);
        STOREWORD(pMsg16->pt.x, (lpMsg)->pt.x);
        STOREWORD(pMsg16->pt.y, (lpMsg)->pt.y);

        Parm16.HookProc.nCode = (SHORT)nCode;
        Parm16.HookProc.wParam = (SHORT)wParam;
        Parm16.HookProc.lParam = vp;

        //
        // Remember Parm16 in case we need to use it again due to the recursion
        // case.
        //

        Parm16Save = Parm16;

    } else {

        //
        // Use old message contents.
        //

        Parm16 = Parm16Save;
        vp = (VPVOID)Parm16Save.HookProc.lParam;

        GETMISCPTR(vp, pMsg16);
    }

    FLUSHVDMPTR(vp, sizeof(MSG16), pMsg16);

    //
    // Count how many times we recurse through this hook proc. Need this count
    // to solve the SoundBits Browser problem. It checks to see if the lpMsg
    // passed is the same as it got last time - if so, it doesn't call this
    // other function that causes recursion.
    //

    cRecurse++;
    CallBack16(RET_HOOKPROC, &Parm16, lpHSData->Proc16, (PVPVOID)&wm32mpex.lReturn);
    cRecurse--;

    // set the correct return value BEFORE unthunking
    wm32mpex.lReturn = (LONG)(BOOL)LOWORD(wm32mpex.lReturn);

    //
    // Free the 32 pointer to 16 bit args and get it again in case underlying
    // memory movement occured.
    //

    FREEVDMPTR(pMsg16);
    GETMISCPTR(vp, pMsg16);

    fFreeDDElParam = FALSE;

    // Theoretically an app can change the lpMsg and return a totall different
    // data altogether. In practice most apps don't do anything so it is pretty
    // expensive to go through the 'unthunk' procedure all the time. So here is
    // a compromise. We copy the message params only if the output message is
    // different from the input message.
    //
    // (davehart) Polaris PackRat modifies WM_KEYDOWN messages sent to its
    // multiline "Note" edit control in its Phone Book Entry dialog box.
    // It changes wParam (the VK_ code) from 0xD (VK_RETURN) to 0xA, which
    // is understood by the edit control to be identical to VK_RETURN but
    // won't cause the dialog box to close.  So we special-case here
    // WM_KEYDOWN where the hook proc changed the virtual key (in wParam)
    // and unthunk it properly.

    fHookModifiedLpMsg = (pMsg16->message != wm32mpex.Parm16.WndProc.wMsg) ||
                         (wm32mpex.Parm16.WndProc.wMsg == WM_KEYDOWN &&
                            pMsg16->wParam != wm32mpex.Parm16.WndProc.wParam) ||
                         (pMsg16->hwnd != GETHWND16(lpMsg->hwnd));


    if (fHookModifiedLpMsg) {
        mpex.Parm16.WndProc.hwnd = pMsg16->hwnd;
        mpex.Parm16.WndProc.wMsg = pMsg16->message;
        mpex.Parm16.WndProc.wParam = pMsg16->wParam;
        mpex.Parm16.WndProc.lParam = pMsg16->lParam,
        mpex.iMsgThunkClass = WOWCLASS_UNKNOWN;
        hwnd32 = ThunkMsg16(&mpex);
        // reset flag if message thunking failed.

        if (!hwnd32)
            fHookModifiedLpMsg = FALSE;
    }

    if (fMessageNeedsThunking) {

        wm32mpex.fThunk = UNTHUNKMSG;
        (wm32mpex.lpfnM32)(&wm32mpex);
    }

    fFreeDDElParam = TRUE;

    if (fHookModifiedLpMsg) {
        lpMsg->hwnd = hwnd32;
        lpMsg->message = mpex.uMsg;
        lpMsg->wParam =  mpex.uParam;
        lpMsg->lParam =  mpex.lParam;
        lpMsg->time      = FETCHLONG(pMsg16->time);
        lpMsg->pt.x      = FETCHSHORT(pMsg16->pt.x);
        lpMsg->pt.y      = FETCHSHORT(pMsg16->pt.y);
    }


    FREEVDMPTR(pMsg16);

    if (!fUseOld) {
        free16(vp);
    }

    // the value in LOWORD is the valid return value

    return (LONG)(BOOL)LOWORD(wm32mpex.lReturn);
}



//*****************************************************************************
//
// ThunkHookProc for Hooks of type WH_JOURNALPLAYBACK -
//                                 WH_JOUNRALRECORD -
//
//     Return type is DWORD.
//
//*****************************************************************************

LONG ThunkJournalHook(INT nCode, LONG wParam, LPEVENTMSG lpEventMsg,
                                                     LPHOOKSTATEDATA lpHSData)
{
    LONG lReturn;
    VPVOID vp;
    PEVENTMSG16 pEventMsg16;
    PARM16 Parm16;

    if ( lpEventMsg ) {
        vp = stackalloc16(sizeof(EVENTMSG16));


        // The WIN32 EVENTMSG structure has an additional field 'hwnd', which
        // is not there in WIN31 EVENTMSG structure. This field can be ignored
        // without any repercussions.

        if (lpHSData->iHook == WH_JOURNALRECORD) {
            GETMISCPTR(vp, pEventMsg16);
            PUTEVENTMSG16(pEventMsg16, lpEventMsg);
            FLUSHVDMPTR(vp, sizeof(EVENTMSG16), pEventMsg16);
            FREEVDMPTR(pEventMsg16);
        }


    } else {
        // lpEventMsg can be NULL indicating that no message data is requested.
        // If this is the case, there is no need to copy data for either
        // journal record or playback.
        vp = (VPVOID)0;
    }

    Parm16.HookProc.nCode = (SHORT)nCode;
    Parm16.HookProc.wParam = (SHORT)wParam;
    Parm16.HookProc.lParam = vp;

    CallBack16(RET_HOOKPROC, &Parm16, lpHSData->Proc16, (PVPVOID)&lReturn);

    if ( lpEventMsg ) {
        GETMISCPTR(vp, pEventMsg16);

        if (lpHSData->iHook == WH_JOURNALPLAYBACK) {
            GETEVENTMSG16(pEventMsg16, lpEventMsg);

#ifdef  DEBUG
            if (MessageNeedsThunking(lpEventMsg->message)) {
                LOGDEBUG(LOG_ALWAYS, ("ThunkJournalHook: Playing back unexpected message 0x%x", lpEventMsg->message));
            }
#endif

        } else {
            WOW32ASSERT(lpHSData->iHook == WH_JOURNALRECORD);
            WOW32ASSERT(aw32Msg[FETCHWORD(pEventMsg16->message)].lpfnM32 == WM32NoThunking);

            // If the app modified the message then copy the new message info
            // (rather than copying it every time we only copy it if the
            // app change the message)
            if (FETCHWORD(pEventMsg16->message) != lpEventMsg->message) {
                GETEVENTMSG16(pEventMsg16, lpEventMsg);
            }
        }

        FREEVDMPTR(pEventMsg16);
        stackfree16(vp);
    }

    return lReturn;
}



//*****************************************************************************
//
// ThunkHookProc for Hooks of type WH_DEBUG -
//
//     Return type is BOOL.
//
//*****************************************************************************

LONG ThunkDebugHook(INT nCode, LONG wParam, LONG lParam,
                                                     LPHOOKSTATEDATA lpHSData)
{
    LONG lReturn;

    lReturn = TRUE;

    UNREFERENCED_PARAMETER(nCode);
    UNREFERENCED_PARAMETER(wParam);
    UNREFERENCED_PARAMETER(lParam);
    UNREFERENCED_PARAMETER(lpHSData);

    LOGDEBUG(LOG_ALWAYS, ("ThunkDebugHook:Not implemented.\n"));

    // the value in LOWORD is the valid return value

    return (LONG)(BOOL)LOWORD(lReturn);
}



//*****************************************************************************
//
// ThunkHookProc for Hooks of type WH_MOUSE -
//
//     Return type is BOOL.
//
//*****************************************************************************

LONG ThunkMouseHook(INT nCode, LONG wParam, LPMOUSEHOOKSTRUCT lpMHStruct,
                                                     LPHOOKSTATEDATA lpHSData)
{
    LONG lReturn;
    VPVOID vp;
    PMOUSEHOOKSTRUCT16 pMHStruct16;
    PARM16 Parm16;

    vp = stackalloc16(sizeof(MOUSEHOOKSTRUCT16));

    GETMISCPTR(vp, pMHStruct16);
    PUTMOUSEHOOKSTRUCT16(pMHStruct16, lpMHStruct);

    Parm16.HookProc.nCode = (SHORT)nCode;
    Parm16.HookProc.wParam = (SHORT)wParam;
    Parm16.HookProc.lParam = vp;

    FLUSHVDMPTR(vp, sizeof(MOUSEHOOKSTRUCT16), pMHStruct16);
    FREEVDMPTR(pMHStruct16);

    CallBack16(RET_HOOKPROC, &Parm16, lpHSData->Proc16, (PVPVOID)&lReturn);

    GETMISCPTR(vp, pMHStruct16);
    GETMOUSEHOOKSTRUCT16(pMHStruct16, lpMHStruct);
    FREEVDMPTR(pMHStruct16);
    stackfree16(vp);
    return (LONG)(BOOL)LOWORD(lReturn);
}



//*****************************************************************************
//
// ThunkHookProc for Hooks of type WH_SHELL -
//
//     Return value apparently is zero.
//
//*****************************************************************************

LONG ThunkShellHook(INT nCode, LONG wParam, LONG lParam,
                                                     LPHOOKSTATEDATA lpHSData)
{
    LONG lReturn;
    PARM16 Parm16;

    Parm16.HookProc.nCode = (SHORT)nCode;
    Parm16.HookProc.lParam = lParam;

    switch (nCode) {
        case HSHELL_WINDOWCREATED:
        case HSHELL_WINDOWDESTROYED:
            Parm16.HookProc.wParam = (SHORT)GETHWND16(wParam);
            break;

        case HSHELL_ACTIVATESHELLWINDOW:
            // fall thru

        default:
            Parm16.HookProc.wParam = (SHORT)wParam;
            break;
    }

    CallBack16(RET_HOOKPROC, &Parm16, lpHSData->Proc16, (PVPVOID)&lReturn);

    // lReturn = 0?

    return (LONG)lReturn;
}



//*****************************************************************************
// W32UnhookHooks:
//
//     Scans the list of active hooks for those for the passed in handle.
//     Those that match are unhooked.
//
//*****************************************************************************

VOID W32UnhookHooks( HAND16 hMod16, BOOL fQueue )
{
    INT i;

    for (i = 0; i < vaHookPPData.cHookProcs; i++) {
        if (vaHookStateData[i].InUse ) {

            if ( !fQueue && ((HAND16)(vaHookStateData[i].hMod16) == hMod16) ) {
                //
                // Unhook this guy!
                //

                if (UnhookWindowsHookEx(vaHookStateData[i].hHook)) {
                    LOGDEBUG(7, ("W32FreeModuleHooks: Freed iHook (WH_*) %04x\n",
                                                vaHookStateData[i].iHook));
                }
                else {
                    LOGDEBUG(LOG_ALWAYS, ("W32FreeModuleHooks: ERROR Freeing iHook (WH_*) %04x\n",
                                                vaHookStateData[i].iHook));
                }

                // reset the state, even if Unhooking failed.

                vaHookStateData[i].TaskId = 0;
                vaHookStateData[i].InUse = FALSE;
            }
        }
    }
}

//*****************************************************************************
// W32FreeOwnedHooks -
//
//     Called during thread exit time. Frees all hooks set by specified thread.
//
//*****************************************************************************

BOOL W32FreeOwnedHooks(INT iTaskId)
{
    INT i;
    for (i = 0; i < vaHookPPData.cHookProcs; i++) {
         if (vaHookStateData[i].InUse &&
                                       vaHookStateData[i].TaskId == iTaskId) {
             if (UnhookWindowsHookEx(vaHookStateData[i].hHook)) {
                 LOGDEBUG(7, ("W32FreeOwnedHooks: Freed iHook (WH_*) %04x\n",
                                             vaHookStateData[i].iHook));
             }
             else {
                 LOGDEBUG(LOG_ALWAYS, ("W32FreeOwnedHooks: ERROR Freeing iHook (WH_*) %04x\n",
                                             vaHookStateData[i].iHook));
             }

             // reset the state, even if Unhooking failed.

             vaHookStateData[i].TaskId = 0;
             vaHookStateData[i].InUse = FALSE;
         }
    }

    return TRUE;
}


//*****************************************************************************
// W32StdDefHookProc: (Standard Def Hook Proc)
//
//     WU32DefHookProc is called here.
//     Return value is the new lParam.
//
//*****************************************************************************

LONG APIENTRY WU32StdDefHookProc(INT nCode, LONG wParam, LONG lParam, INT iFunc)
{
    switch (vaHookStateData[iFunc].iHook) {
        case WH_CALLWNDPROC:
            return ThunkCallWndProcHook16(nCode, wParam, (VPVOID)lParam,
                                                   &vaHookStateData[iFunc]);

        case WH_CBT:

            return ThunkCbtHook16(nCode, wParam, (VPVOID)lParam,
                                                   &vaHookStateData[iFunc]);


        case WH_KEYBOARD:
            return ThunkKeyBoardHook16(nCode, wParam, lParam,
                                                   &vaHookStateData[iFunc]);

        case WH_MSGFILTER:
        case WH_SYSMSGFILTER:
        case WH_GETMESSAGE:

            return ThunkMsgFilterHook16(nCode, wParam, (VPVOID)lParam,
                               &vaHookStateData[iFunc]);

        case WH_JOURNALPLAYBACK:
        case WH_JOURNALRECORD:
            return ThunkJournalHook16(nCode, wParam, (VPVOID)lParam,
                                                   &vaHookStateData[iFunc]);

        case WH_DEBUG:
            return ThunkDebugHook16(nCode, wParam, lParam,
                                                   &vaHookStateData[iFunc]);


        case WH_MOUSE:
            return ThunkMouseHook16(nCode, wParam, (VPVOID)lParam,
                                                   &vaHookStateData[iFunc]);

        case WH_SHELL:
            return ThunkShellHook16(nCode, wParam, lParam,
                                                   &vaHookStateData[iFunc]);

        default:
            LOGDEBUG(LOG_ALWAYS,("WU32StdDefHookProc: Unknown Hook type.\n"));
    }

    return (LONG)FALSE;

}

//*****************************************************************************
//
// 16->32 ThunkHookProc for Hooks of type WH_CALLWNDPROC -
//
//     Return type is VOID.
//
//*****************************************************************************



LONG ThunkCallWndProcHook16(INT nCode, LONG wParam, VPVOID  vpCwpStruct,
                                                     LPHOOKSTATEDATA lpHSData)
{
    CWPSTRUCT CwpStruct;
    PCWPSTRUCT16 pCwpStruct16;
    MSGPARAMEX mpex;

    GETMISCPTR(vpCwpStruct, pCwpStruct16);

    mpex.Parm16.WndProc.hwnd = pCwpStruct16->hwnd;
    mpex.Parm16.WndProc.wMsg = pCwpStruct16->message;
    mpex.Parm16.WndProc.wParam = pCwpStruct16->wParam;
    mpex.Parm16.WndProc.lParam = pCwpStruct16->lParam,
    mpex.iMsgThunkClass = WOWCLASS_UNKNOWN;
    mpex.hwnd = ThunkMsg16(&mpex);

    CwpStruct.message = mpex.uMsg;
    CwpStruct.wParam  = mpex.uParam;
    CwpStruct.lParam  = mpex.lParam;
    CwpStruct.hwnd    = mpex.hwnd;

    mpex.lReturn = CallNextHookEx(lpHSData->hHook, nCode, wParam,
                                                           (LPARAM)&CwpStruct);
    if (MSG16NEEDSTHUNKING(&mpex)) {
        mpex.uMsg   = CwpStruct.message;
        mpex.uParam = CwpStruct.wParam;
        mpex.lParam = CwpStruct.lParam;
        (mpex.lpfnUnThunk16)(&mpex);
    }

    FREEVDMPTR(pCwpStruct16);

    return mpex.lReturn;
}


//*****************************************************************************
//
// 16->32 ThunkHookProc for Hooks of type WH_CBT -
//
//     Return type is BOOL.
//
//*****************************************************************************

LONG ThunkCbtHook16(INT nCode, LONG wParam, VPVOID lParam,
                                                     LPHOOKSTATEDATA lpHSData)
{
    LONG lReturn = FALSE;
    WPARAM wParamNew;
    LPARAM lParamNew;
    MSGPARAMEX mpex;

    PMOUSEHOOKSTRUCT16 pMHStruct16;
    PCBTACTIVATESTRUCT16 pCbtAStruct16;
    PCBT_CREATEWND16     pCbtCWnd16;

    MOUSEHOOKSTRUCT      MHStruct;
    RECT                 Rect;
    CBTACTIVATESTRUCT    CbtAStruct;
    CBT_CREATEWND        CbtCWnd;

    wParamNew = wParam;
    lParamNew = lParam;

    switch(nCode) {
        case HCBT_MOVESIZE:
            // wParam = HWND, lParam = LPRECT

            wParamNew = (WPARAM)HWND32(wParam);
            lParamNew = (LPARAM)&Rect;

            GETRECT16(lParam, &Rect);

            break;


        case HCBT_MINMAX:
            // wParam = HWND, lParam = SW_*  --- a command

            wParamNew = (WPARAM)HWND32(wParam);
            break;

        case HCBT_QS:

            break;

        case HCBT_CREATEWND:
            // wParam = HWND, lParam = LPCBT_CREATEWND


            lParamNew = (LPARAM)&CbtCWnd;

            GETMISCPTR(lParam, pCbtCWnd16);

            mpex.Parm16.WndProc.hwnd = 0;
            mpex.Parm16.WndProc.wMsg = WM_CREATE;
            mpex.Parm16.WndProc.wParam = 0;
            mpex.Parm16.WndProc.lParam = FETCHDWORD(pCbtCWnd16->vpcs);
            mpex.iMsgThunkClass = 0;
            ThunkMsg16(&mpex);
            (LONG)CbtCWnd.lpcs = mpex.lParam;

            wParamNew = (WPARAM)HWND32(wParam);
            CbtCWnd.hwndInsertAfter =
                               HWNDIA32(FETCHWORD(pCbtCWnd16->hwndInsertAfter));

            FREEVDMPTR(pCbtCWnd16);

            break;

        case HCBT_DESTROYWND:

            // wParam = HWND, lParam = 0

            wParamNew = (WPARAM)HWND32(wParam);
            break;

        case HCBT_ACTIVATE:

            // wParam = HWND, lParam = LPCBTACTIVATESTRUCT

            wParamNew = (WPARAM)HWND32(wParam);
            lParamNew = (LPARAM)&CbtAStruct;

            GETMISCPTR(lParam, pCbtAStruct16);
            GETCBTACTIVATESTRUCT16(pCbtAStruct16, &CbtAStruct);
            FREEVDMPTR(pCbtAStruct16);

            break;

        case HCBT_CLICKSKIPPED:

            // wParam = mouse message, lParam = LPMOUSEHOOKSTRUCT

            lParamNew = (LPARAM)&MHStruct;

            GETMISCPTR(lParam, pMHStruct16);
            GETMOUSEHOOKSTRUCT16(pMHStruct16, &MHStruct);
            FREEVDMPTR(pMHStruct16);
            break;

        case HCBT_KEYSKIPPED:

            // wParam, lParam   -- keyup/down message params

            break;

        case HCBT_SYSCOMMAND:

            // wParam = SC_ syscomand, lParam = DWORD(x,y)

            break;

        case HCBT_SETFOCUS:

            // wParam = HWND, lParam = HWND

            wParamNew = (WPARAM)HWND32(wParam);
            lParamNew = (WPARAM)HWND32(lParam);
            break;

        default:
            LOGDEBUG(LOG_ALWAYS, ("ThunkCbtHook: Unknown HCBT_ code\n"));
            break;
    }


    lReturn = CallNextHookEx(lpHSData->hHook, nCode,
                                                 wParamNew, (LPARAM)lParamNew);

    switch(nCode) {
        case HCBT_MOVESIZE:

            PUTRECT16(lParam, (LPRECT)lParamNew);
            break;

        case HCBT_CREATEWND:
            GETMISCPTR(lParam, pCbtCWnd16);
            mpex.lParam = (LONG)CbtCWnd.lpcs;
            mpex.lReturn = lReturn;
            WOW32ASSERT(MSG16NEEDSTHUNKING(&mpex));
            (mpex.lpfnUnThunk16)(&mpex);
            lReturn = mpex.lReturn;

            STOREWORD(pCbtCWnd16->hwndInsertAfter,
                      GETHWNDIA16(((LPCBT_CREATEWND)lParamNew)->
                                                             hwndInsertAfter));
            FLUSHVDMPTR((VPVOID)lParam, sizeof(CBT_CREATEWND16), pCbtCWnd16);
            FREEVDMPTR(pCbtCWnd16);
            break;


        case HCBT_ACTIVATE:

            GETMISCPTR(lParam, pCbtAStruct16);
            PUTCBTACTIVATESTRUCT16(pCbtAStruct16, (LPCBTACTIVATESTRUCT)lParamNew);
            FLUSHVDMPTR((VPVOID)lParam, sizeof(CBTACTIVATESTRUCT16),
                                                                 pCbtAStruct16);
            FREEVDMPTR(pCbtAStruct16);
            break;

        case HCBT_CLICKSKIPPED:

            GETMISCPTR(lParam, pMHStruct16);
            PUTMOUSEHOOKSTRUCT16(pMHStruct16, (LPMOUSEHOOKSTRUCT)lParamNew);
            FLUSHVDMPTR((VPVOID)lParam, sizeof(MOUSEHOOKSTRUCT16),
                                                                 pMHStruct16);
            FREEVDMPTR(pMHStruct16);
            break;

        // case HCBT_MINMAX:
        // case HCBT_QS:
        // case HCBT_DESTROYWND:
        // case HCBT_KEYSKIPPED:
        // case HCBT_SYSCOMMAND:
        // case HCBT_SETFOCUS:

        default:
            break;
    }

    // the value in LOWORD is the valid return value

    return (LONG)(BOOL)LOWORD(lReturn);
}



//*****************************************************************************
//
// 16->32 ThunkHookProc for Hooks of type WH_KEYBOARD -
//
//     Return type is BOOL.
//
//*****************************************************************************

LONG ThunkKeyBoardHook16(INT nCode, LONG wParam, LONG lParam,
                                                     LPHOOKSTATEDATA lpHSData)
{
    LONG lReturn;

    lReturn = CallNextHookEx(lpHSData->hHook, nCode, wParam,
                                                           (LPARAM)lParam);

    // the value in LOWORD is the valid return value

    return (LONG)(BOOL)LOWORD(lReturn);
}



//*****************************************************************************
//
// 16->32 ThunkHookProc for Hooks of type WH_GETMESSAGE -
//                                        WH_MSGFILTER -
//                                        WH_SYSMSGFILTER -
//
//     Return type is BOOL.
//
//*****************************************************************************

LONG ThunkMsgFilterHook16(INT nCode, LONG wParam, VPVOID vpMsg,
                                                     LPHOOKSTATEDATA lpHSData)
{
    PMSG16 pMsg16;
    MSG  Msg;
    MSGPARAMEX mpex;



    GETMISCPTR(vpMsg, pMsg16);

    fFreeDDElParam = FALSE;

    mpex.Parm16.WndProc.hwnd = pMsg16->hwnd;
    mpex.Parm16.WndProc.wMsg = pMsg16->message;
    mpex.Parm16.WndProc.wParam = pMsg16->wParam;
    mpex.Parm16.WndProc.lParam = pMsg16->lParam;
    mpex.iMsgThunkClass = 0;
    ThunkMsg16(&mpex);

    fFreeDDElParam = TRUE;

    Msg.message   = mpex.uMsg;
    Msg.wParam    = mpex.uParam;
    Msg.lParam    = mpex.lParam;
    Msg.hwnd      = HWND32(FETCHWORD(pMsg16->hwnd));
    Msg.time      = FETCHLONG(pMsg16->time);
    Msg.pt.x      = FETCHSHORT(pMsg16->pt.x);
    Msg.pt.y      = FETCHSHORT(pMsg16->pt.y);

    FREEVDMPTR(pMsg16);

    mpex.lReturn = CallNextHookEx(lpHSData->hHook, nCode, wParam,
                                                           (LPARAM)&Msg);
    GETMISCPTR(vpMsg, pMsg16);

    if (MSG16NEEDSTHUNKING(&mpex)) {
        mpex.uMsg   = Msg.message;
        mpex.uParam = Msg.wParam;
        mpex.lParam = Msg.lParam;
        (mpex.lpfnUnThunk16)(&mpex);
    }

    STORELONG(pMsg16->time, Msg.time);
    STOREWORD(pMsg16->pt.x, Msg.pt.x);
    STOREWORD(pMsg16->pt.y, Msg.pt.y);

    FLUSHVDMPTR(vpMsg, sizeof(MSG16), pMsg16);
    FREEVDMPTR(pMsg16);

    // the value in LOWORD is the valid return value

    return (LONG)(BOOL)LOWORD(mpex.lReturn);
}



//*****************************************************************************
//
// 16->32 ThunkHookProc for Hooks of type WH_JOURNALPLAYBACK -
//                                        WH_JOUNRALRECORD -
//
//     Return type is DWORD.
//
//*****************************************************************************

LONG ThunkJournalHook16(INT nCode, LONG wParam, VPVOID vpEventMsg,
                                                     LPHOOKSTATEDATA lpHSData)
{
    LONG lReturn;
    PEVENTMSG16 pEventMsg16;
    EVENTMSG    EventMsg;
    LPEVENTMSG  lpEventMsg;

    if ( vpEventMsg ) {


        // The WIN32 EVENTMSG structure has an additional field 'hwnd', which
        // is not there in WIN31 EVENTMSG structure. This field can be ignored
        // without any repercussions.

        if (lpHSData->iHook == WH_JOURNALRECORD) {
            GETMISCPTR(vpEventMsg, pEventMsg16);
            GETEVENTMSG16(pEventMsg16, &EventMsg);
            EventMsg.hwnd = (HWND)0;
            FREEVDMPTR(pEventMsg16);
        }
        lpEventMsg = &EventMsg;


    } else {
        lpEventMsg = NULL;
    }


    lReturn = CallNextHookEx(lpHSData->hHook, nCode, wParam, (LPARAM)lpEventMsg );

    if ( vpEventMsg ) {

        if (lpHSData->iHook == WH_JOURNALPLAYBACK) {
            GETMISCPTR(vpEventMsg, pEventMsg16);
            PUTEVENTMSG16(pEventMsg16, &EventMsg);
            FLUSHVDMPTR(vpEventMsg, sizeof(EVENTMSG16), pEventMsg16);
            FREEVDMPTR(pEventMsg16);
        }

    }

    return lReturn;
}



//*****************************************************************************
//
// 16->32 ThunkHookProc for Hooks of type WH_DEBUG -
//
//     Return type is BOOL.
//
//*****************************************************************************

LONG ThunkDebugHook16(INT nCode, LONG wParam, LONG lParam,
                                                     LPHOOKSTATEDATA lpHSData)
{
    LONG lReturn;

    lReturn = TRUE;

    UNREFERENCED_PARAMETER(nCode);
    UNREFERENCED_PARAMETER(wParam);
    UNREFERENCED_PARAMETER(lParam);
    UNREFERENCED_PARAMETER(lpHSData);

    LOGDEBUG(LOG_ALWAYS, ("ThunkDebugHook16:Not implemented.\n"));

    // the value in LOWORD is the valid return value

    return (LONG)(BOOL)LOWORD(lReturn);
}



//*****************************************************************************
//
// 16->32 ThunkHookProc for Hooks of type WH_MOUSE -
//
//     Return type is BOOL.
//
//*****************************************************************************

LONG ThunkMouseHook16(INT nCode, LONG wParam, VPVOID vpMHStruct,
                                                     LPHOOKSTATEDATA lpHSData)
{
    LONG lReturn;
    PMOUSEHOOKSTRUCT16 pMHStruct16;
    MOUSEHOOKSTRUCT    MHStruct;

    GETMISCPTR(vpMHStruct, pMHStruct16);
    GETMOUSEHOOKSTRUCT16(pMHStruct16, &MHStruct);
    FREEVDMPTR(pMHStruct16);

    lReturn = CallNextHookEx(lpHSData->hHook, nCode, wParam,
                                                           (LPARAM)&MHStruct);

    GETMISCPTR(vpMHStruct, pMHStruct16);
    PUTMOUSEHOOKSTRUCT16(pMHStruct16, &MHStruct);
    FLUSHVDMPTR((VPVOID)vpMHStruct, sizeof(MOUSEHOOKSTRUCT16), pMHStruct16);
    FREEVDMPTR(pMHStruct16);

    return (LONG)(BOOL)LOWORD(lReturn);
}



//*****************************************************************************
//
// 16->32 ThunkHookProc for Hooks of type WH_SHELL -
//
//     Return value is apparently zero.
//
//*****************************************************************************

LONG ThunkShellHook16(INT nCode, LONG wParam, LONG lParam,
                                                     LPHOOKSTATEDATA lpHSData)
{
    LONG lReturn;

    switch (nCode) {
        case HSHELL_WINDOWCREATED:
        case HSHELL_WINDOWDESTROYED:
            wParam = (LONG)HWND32(wParam);
            break;

        case HSHELL_ACTIVATESHELLWINDOW:
            // fall thru

        default:
            break;
    }

    lReturn = CallNextHookEx(lpHSData->hHook, nCode, wParam,
                                                           (LPARAM)lParam);

    // lReturn = 0?

    return (LONG)lReturn;
}


//*****************************************************************************
// W32GetHookDDEMsglParam:
//
//     Returns the lParam of the actual hook message. called for dde messages
//     only. returns valid lParam else 0.
//
//*****************************************************************************


DWORD  W32GetHookDDEMsglParam()
{
    INT iFunc;
    LONG lParam;

    iFunc = viCurrentHookStateDataIndex;
    lParam  = vHookParams.lParam;

    if (lParam) {
        switch (vaHookStateData[iFunc].iHook) {
            case WH_CALLWNDPROC:
                lParam =  ((LPCWPSTRUCT)lParam)->lParam;
                break;

            case WH_MSGFILTER:
            case WH_SYSMSGFILTER:
            case WH_GETMESSAGE:
                lParam =  ((LPMSG)lParam)->lParam;
                break;

            default:
                lParam = 0;
        }
    }

    return lParam;

}
