
/*++

 *
 *  WOW v1.0
 *
 *  Copyright (c) 1991, Microsoft Corporation
 *
 *  WMDISP32.C
 *  WOW32 32-bit message thunks
 *
 *  History:
 *  Created 19-Feb-1992 by Chandan S. Chauhan (ChandanC)
 *  Changed 12-May-1992 by Mike Tricker (MikeTri) Added MultiMedia thunks
 *  Changed 09-Jul-1992 by v-cjones Added msg profiling debugger extension
--*/


#include "precomp.h"
#pragma hdrstop
#ifdef FE_IME
#include "wownls.h"
#include "ime.h"
#include "prshtp.h"
#endif // FE_IME

MODNAME(wmdisp32.c);

BOOL fThunkDDEmsg = TRUE;

extern WORD msgFINDREPLACE;  // see WCOMMDLG.C

#ifdef WOWPROFILE  // for MSG profiling only (debugger extension)
INT fWMsgProfRT = 0;
#endif


BOOL W32Win16DlgProcEx(HWND hdlg, UINT uMsg, UINT uParam, LONG lParam,
    VPWNDPROC vpDlgProc16,  // Next WndProc to call or NULL if default
    PWW pww)    // hwnd's PWW if already known or NULL
{
    BOOL fSuccess;
    register PTD ptd;
    WM32MSGPARAMEX wm32mpex;
    BOOL   fMessageNeedsThunking;

#ifdef WOWPROFILE  // for MSG profiling only (debugger extension)
    DWORD dwTics;
#endif

    ptd = CURRENTPTD();

    WOW32ASSERT(vpDlgProc16);

    // take out the marker bits and fix the RPL bits
    UnMarkWOWProc (vpDlgProc16,vpDlgProc16);

    // If the app has GP Faulted we don't want to pass it any more input
    // This should be removed when USER32 does clean up on task death so
    // it doesn't call us - mattfe june 24 92 HACK32

    if (ptd->dwFlags & TDF_IGNOREINPUT) {
        LOGDEBUG(6,("    W32Dlg16WndProc Ignoring Input Messsage %04X\n",uMsg));
        WOW32ASSERTMSG(gfIgnoreInputAssertGiven,
                       "W32Dlg16WndProc: TDF_IGNOREINPUT hack was used, shouldn't be, "
                       "please email DaveHart with repro instructions.  Hit 'g' to ignore this "
                       "and suppress this assertion from now on.\n");
        gfIgnoreInputAssertGiven = TRUE;
        return FALSE;
    }

    wm32mpex.Parm16.WndProc.hwnd   = GETHWND16(hdlg);
    wm32mpex.Parm16.WndProc.wMsg   = (WORD)uMsg;
    wm32mpex.Parm16.WndProc.wParam = (WORD)uParam;
    wm32mpex.Parm16.WndProc.lParam = (LONG)lParam;
    wm32mpex.Parm16.WndProc.hInst  = 0;   // Forces AX = SS on WndProc entry,
                                          // for Win 3.1 compatibility.

    fMessageNeedsThunking =  (uMsg < 0x400) &&
                                  (aw32Msg[uMsg].lpfnM32 != WM32NoThunking);

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

#ifdef WOWPROFILE  // for MSG profiling only (debugger extension)
        dwTics = GetWOWTicDiff(0L);
#endif
        wm32mpex.fThunk = THUNKMSG;
        wm32mpex.hwnd = hdlg;
        wm32mpex.uMsg = uMsg;
        wm32mpex.uParam = uParam;
        wm32mpex.lParam = lParam;
        wm32mpex.pww = pww;
        wm32mpex.fFree = TRUE;
        wm32mpex.lpfnM32 = aw32Msg[uMsg].lpfnM32;
        if (!(wm32mpex.lpfnM32)(&wm32mpex)) {
            LOGDEBUG(LOG_ERROR,("    W32Win16DlgProcEx ERROR: cannot thunk 32-bit message %04x\n", uMsg));
            goto Error;
        }

#ifdef WOWPROFILE  // for MSG profiling only (debugger extension)
        if( !fWMsgProfRT ) {  // only if not profiling round trip
            aw32Msg[uMsg].cTics += GetWOWTicDiff(dwTics);
        }
#endif

    }
    else {
        LOGDEBUG(6,("    No Thunking was required for the 32-bit message %s(%04x)\n", (LPSZ)GetWMMsgName(uMsg), uMsg));
    }

    BlockWOWIdle(FALSE);

    fSuccess = CallBack16(RET_WNDPROC, &wm32mpex.Parm16, vpDlgProc16, (PVPVOID)&wm32mpex.lReturn);

    BlockWOWIdle(TRUE);

    // the callback function of a dialog is of type FARPROC whose return value
    // is of type 'int'. Since dx:ax is copied into lReturn in the above
    // CallBack16 call, we need to zero out the hiword, otherwise we will be
    // returning an erroneous value.

    wm32mpex.lReturn = (LONG)((SHORT)(LOWORD(wm32mpex.lReturn)));

    if (fMessageNeedsThunking) {

#ifdef WOWPROFILE  // for MSG profiling only (debugger extension)
        if( !fWMsgProfRT ) {  // only if not round trip profiling
            dwTics = GetWOWTicDiff(0L);
        }
#endif // WOWPROFILE


        //
        // if you send a message to a dialog what gets returned
        // to the caller is the dlg's msgresult window long.
        // app dialog functions will call
        //     SetWindowLong(hdlg, DWL_MSGRESULT, n);
        // during message processing so the right thing gets returned.
        // scottlu says we only need to do this for wm_gettext, it's
        // the only message whose result is an output count.
        //

        if (uMsg == WM_GETTEXT  &&  wm32mpex.lReturn != 0) {
            wm32mpex.lReturn = GetWindowLong(hdlg, DWL_MSGRESULT);
        }

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

#ifdef WOWPROFILE  // for MSG profiling only (debugger extension)
        aw32Msg[uMsg].cTics += GetWOWTicDiff(dwTics);
        aw32Msg[uMsg].cCalls++;   // increment # times message passed
#endif // WOWPROFILE

    }

    if (!fSuccess)
        goto Error;

Done:

    return wm32mpex.lReturn;

Error:
    LOGDEBUG(6,("    W32Win16DlgProcEx WARNING: cannot call back, using default message handling\n"));
    wm32mpex.lReturn = 0;
    goto Done;
}








LONG W32Win16WndProcEx(HWND hwnd, UINT uMsg, UINT uParam, LONG lParam,
    VPWNDPROC vpWndProc16,  // Next WndProc to call or NULL if default
    PWW pww)    // hwnd's PWW if already known or NULL
{
    BOOL fSuccess;
    LONG ulReturn;
    register PTD ptd;
    WM32MSGPARAMEX wm32mpex;
    BOOL   fMessageNeedsThunking;
//#ifdef DEBUG
//    CHAR szClassName[80];
//#endif

#ifdef WOWPROFILE  // for MSG profiling only (debugger extension)
    DWORD dwTics;
#endif

    ptd = CURRENTPTD();

    WOW32ASSERT(vpWndProc16);

    // take out the marker bits and fix the RPL bits
    UnMarkWOWProc (vpWndProc16,vpWndProc16);


    //
    // If the app has GP Faulted we don't want to pass it any more input
    // This should be removed when USER32 does clean up on task death so
    // it doesn't call us - mattfe june 24 92 HACK32
    //
    // It's not a problem if the callback is going to a 16:16 proc in user.exe
    // like DefWindowProc.
    //

    if (ptd->dwFlags & TDF_IGNOREINPUT &&
        HIWORD(vpWndProc16) != HIWORD(gpfn16GetProcModule)) {

        LOGDEBUG(6,("    W32Win16WndProcEx Ignoring Input Messsage %04X\n",uMsg));
        WOW32ASSERTMSG(gfIgnoreInputAssertGiven,
                       "W32Win16WndProcEx: TDF_IGNOREINPUT hack was used, shouldn't be, "
                       "please email DaveHart with repro instructions.  Hit 'g' to ignore this "
                       "and suppress this assertion from now on.\n");
        gfIgnoreInputAssertGiven = TRUE;
        goto SilentError;
    }

    //
    // Don't send WM_DEVMODECHANGE if TDF_EATDEVMODEMSG is set
    // access2.0 faults if it is in EnumMetaFile and receives WM_DEVMODECHANGE
    // while in it whistler bug 189703
    //

    if (( ptd->dwFlags & TDF_EATDEVMODEMSG ) && ( uMsg == WM_DEVMODECHANGE )) {
        return 0;
    }

    //
    // Figure out the class for this hwnd if we haven't seen it before
    //

    if (!pww) {
        if (!(pww = (PWW) GetWindowLong(hwnd, GWL_WOWWORDS))) {
            LOGDEBUG(LOG_ALWAYS,("WOW :: W32Win16WndProcEx ERROR: GetWindowLong(0x%x, GWL_WOWWORDS) fails\n", hwnd));
            goto Error;
        }
    }
#ifdef FE_IME
    if (( uMsg == WM_IME_REPORT ) && ( uParam == IR_STRINGEX )) {
        HANDLE hInstance;

        hInstance = (HANDLE)(ULONG)GetWindowLong(hwnd, GWL_HINSTANCE);
        hInstance = (HANDLE)(ULONG)VALIDHMOD(hInstance);
        if (W32GetExpWinVer(hInstance) < 0x030a)
            return 0;
    }

    //
    // Don't dispatch Version 4.0 IMM Messages to 16bit apps.
    //
    // WM_IME_STARTCOMPOSITION         0x010D
    // WM_IME_ENDCOMPOSITION           0x010E
    // WM_IME_COMPOSITION              0x010F
    // WM_IME_SETCONTEXT               0x0281
    // WM_IME_NOTIFY                   0x0282
    // WM_IME_CONTROL                  0x0283
    // WM_IME_COMPOSITIONFULL          0x0284
    // WM_IME_SELECT                   0x0285
    // WM_IME_CHAR                     0x0286
    // WM_IME_SYSTEM                   0x0287
    //
    if ((( uMsg >= WM_IME_STARTCOMPOSITION ) && ( uMsg <= WM_IME_COMPOSITION )) ||
        (( uMsg >= WM_IME_SETCONTEXT ) && (uMsg <= WM_IME_SYSTEM ))) {

        // Korean Edit conrol need to dispatch new IMM messages.
        if ( !(GetStdClassWndProc(WOWCLASS_EDIT) && GetSystemDefaultLangID()==0x412) ) {
             HANDLE hInstance;
             hInstance = (HANDLE)(ULONG)GetWindowLong(hwnd, GWL_HINSTANCE);
             hInstance = (HANDLE)(ULONG)VALIDHMOD(hInstance);
             if (W32GetExpWinVer(hInstance) < 0x0400)
                 goto Error;
        }
    }
#endif // FE_IME

    // This message is WIN32 only.  It is sent by WOW32 during the processing
    // of an EM_SETSEL in WU32Send/PostMessage.  If an MLE is subclassed the
    // message will come through here attempting to travel back to the 16-bit
    // app's wndproc.  Instead of sending back a message that the 16-bit app
    // doesn't understand it will be intercepted here and sent directly to the
    // standard EditWindowProc.  I'm not adding a Thunk because it shouldn't
    // go to the app.

    if (uMsg == EM_SCROLLCARET) {
        WNDPROC EditWndProc;

        // find the 32-bit EditWindowProc
        // We should only be in this state if the app has subclassed so this
        // call should be safe.

        EditWndProc = (WNDPROC)GetStdClassWndProc(WOWCLASS_EDIT);

        if (EditWndProc) {
            CallWindowProc(EditWndProc, hwnd, EM_SCROLLCARET, 0, 0);
        }
        else {
            LOGDEBUG(LOG_ALWAYS,("    W32Win16WndProcEx ERROR: cannot find 32-bit EditWindowProc\n"));
        }
        return 0;   // notification message, no return code
    }
   
    // Thunk this 32 bit message to 16 bit message

    LOGDEBUG(6,("    Thunking window %x message %s\n", hwnd, GetWMMsgName(uMsg)));
#ifdef DEBUG
    if((uMsg & WOWPRIVATEMSG) && ((uMsg & ~WOWPRIVATEMSG) < 0x400)) {
        LOGDEBUG(6,("     -- private WOW bit set for %s\n", GetWMMsgName(uMsg & ~WOWPRIVATEMSG)));
    }
#endif

    wm32mpex.Parm16.WndProc.hwnd   = GETHWND16(hwnd);
    wm32mpex.Parm16.WndProc.wMsg   = (WORD)uMsg;
    wm32mpex.Parm16.WndProc.wParam = (WORD)uParam;
    wm32mpex.Parm16.WndProc.lParam = (LONG)lParam;
    wm32mpex.Parm16.WndProc.hInst  = LOWORD(pww->hModule);

    // An app can send one of its private class windows a message say 401.
    // This message will not be thunked in WMSG16.C because the
    // messages >= 0x400 and we did not want to thunk it in WMSG16.C
    //

    fMessageNeedsThunking =  (uMsg < 0x400) &&
                                  (aw32Msg[uMsg].lpfnM32 != WM32NoThunking);

    if (fMessageNeedsThunking) {
        LOGDEBUG(6,("%04X (%s)\n", ptd->htask16, (aw32Msg[uMsg].lpszW32)));

#ifdef WOWPROFILE  // for MSG profiling only (debugger extension)
        dwTics = GetWOWTicDiff(0L);
#endif
        wm32mpex.fThunk = THUNKMSG;
        wm32mpex.hwnd = hwnd;
        wm32mpex.uMsg = uMsg;
        wm32mpex.uParam = uParam;
        wm32mpex.lParam = lParam;
        wm32mpex.pww = pww;
        wm32mpex.fFree = TRUE;
        wm32mpex.lpfnM32 = aw32Msg[uMsg].lpfnM32;
        ulReturn = (wm32mpex.lpfnM32)(&wm32mpex);

#ifdef WOWPROFILE  // for MSG profiling only (debugger extension)
        if( !fWMsgProfRT ) {  // only if not profiling round trip
            aw32Msg[uMsg].cTics += GetWOWTicDiff(dwTics);
        }
#endif

        if (!ulReturn) {
            LOGDEBUG(LOG_ALWAYS,("    W32Win16WndProcEx ERROR: cannot thunk 32-bit message %s (%x)\n", GetWMMsgName(uMsg), uMsg));
            goto Error;
        }
    }

    if (vpWndProc16 == (VPVOID)NULL) {
        WOW32ASSERT(vpWndProc16);
        goto SilentError;
    }

    LOGDEBUG(6,("16-bit Window Proc = %08lX\n", vpWndProc16));

    BlockWOWIdle(FALSE);

    fSuccess = CallBack16(RET_WNDPROC, &wm32mpex.Parm16, vpWndProc16, (PVPVOID)&wm32mpex.lReturn);

    BlockWOWIdle(TRUE);

    // During CreateWindow some apps draw their own non-client area and don't
    // pass WM_NCCALCSIZE to DefWindowProc which causes Win 95 and NT's user to
    // not set some needed window flags. Mavis Beacon is an example. We'll pass
    // the message for them.

    if (uMsg == WM_NCCALCSIZE) {
        if (CURRENTPTD()->dwWOWCompatFlagsEx & WOWCFEX_DEFWNDPROCNCCALCSIZE) {
            DefWindowProc(hwnd, uMsg, uParam, lParam);
        }
    }

    // UnThunk this 32 bit message

    LOGDEBUG(6,("    UnThunking window %x message %s\n", hwnd, (LPSZ)GetWMMsgName(uMsg)));
#ifdef DEBUG
    if((uMsg & WOWPRIVATEMSG) && ((uMsg - WOWPRIVATEMSG) < 0x400)) {
        LOGDEBUG(6,("     -- private WOW bit set for %s\n", (LPSZ)GetWMMsgName(uMsg)));
    }
#endif

    if (fMessageNeedsThunking) {

#ifdef WOWPROFILE  // for MSG profiling only (debugger extension)
        if( !fWMsgProfRT ) {  // only if not profiling round trip
            dwTics = GetWOWTicDiff(0L);
        }
#endif

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

#ifdef WOWPROFILE  // for MSG profiling only (debugger extension)
        aw32Msg[uMsg].cTics += GetWOWTicDiff(dwTics);
        aw32Msg[uMsg].cCalls++;   // increment # times message passed
#endif

    }

    if (!fSuccess) {
        goto Error;
    }

    return (wm32mpex.lReturn);

Error:
    LOGDEBUG(LOG_ALWAYS,("    W32Win16WndProcEx ERROR: cannot call back, using default message handling\n"));
SilentError:
    return DefWindowProc(hwnd, uMsg, uParam, lParam);
}



// The following functions are used to "thunk" a 32 bit message to 16 bit
// message.
//
// To add a thunk function for a 32 bit message,
//    - Modify the entry for the message in "aw32Msg" function array
//  (in wmtbl32.c) to point to the new thunk function.
//    - Define the new thunk function in this file.
//


// These messages do not require any thunking so just copy the 32 bit wParam
// and lParam to 16 bit wParam and lParam.
//
//
//  WM_CANCELMODE
//  WM_CHAR
//  WM_CHILDACTIVATE
//  WM_CLEAR
//  WM_CLOSE
//  WM_COMMNOTIFY
//  WM_COMPACTING
//  WM_COPY
//  WM_CUT
//  WM_DEADCHAR
//  WM_DESTROY
//  WM_DRAWCLIPBOARD
//  WM_ENABLE
//  WM_ENDSESSION
//  WM_FONTCHANGE
//  WM_GETFONT
//  WM_GETTEXTLENGTH
//  WM_HOTKEY
//  WM_INPUTFOCUS
//  WM_ISACTIVEICON (undocumented)
//  WM_KEYDOWN
//  WM_KEYUP
//  WM_LBTRACKPOINT (undocumented)
//  WM_LBUTTONDBLCLK
//  WM_LBUTTONDOWN
//  WM_LBUTTONUP
//  WM_MBUTTONDBLCLK
//  WM_MBUTTONDOWN
//  WM_MBUTTONUP
//  WM_MDICASCADE
//  WM_MDIICONARRANGE
//  WM_MDINEXT
//  WM_MDITILE
//  WM_MOUSEENTER
//  WM_MOUSELEAVE
//  WM_MOUSEMOVE
//  WM_MOVE
//  WM_NCCALCRGN
//  WM_NCDESTROY
//  WM_NCHITTEST
//  WM_NCLBUTTONDBLCLK
//  WM_NCLBUTTONDOWN
//  WM_NCLBUTTONUP
//  WM_NCMBUTTONDBLCLK
//  WM_NCMBUTTONDOWN
//  WM_NCMBUTTONUP
//  WM_NCMOUSEMOVE
//  WM_NCRBUTTONDBLCLK
//  WM_NCRBUTTONDOWN
//  WM_NCRBUTTONUP
//  WM_PAINTICON
//  WM_PASTE
//  WM_POWER
//  WM_QUERYENDSESSION
//  WM_QUERYNEWPALETTE
//  WM_QUERYOPEN
//  WM_QUERYPARKICON (undocumented)
//  WM_QUEUESYNC
//  WM_QUIT
//  WM_RBUTTONDBLCLK
//  WM_RBUTTONDOWN
//  WM_RBUTTONUP
//  WM_RENDERALLFORMATS
//  WM_RENDERFORMAT
//  WM_SETREDRAW
//  WM_SHOWWINDOW
//  WM_SIZE
//  WM_SPOOLERSTATUS (double-check lParam conversion on this one -JTP)
//  WM_SYSCHAR
//  WM_SYSCOLORCHANGE
//  WM_SYSCOMMAND
//  WM_SYSDEADCHAR
//  WM_SYSKEYDOWN
//  WM_SYSKEYUP
//  WM_SYSTEMERROR
//  WM_TIMECHANGE
//  WM_UNDO
//  MM_JOY1BUTTONDOWN     - MultiMedia messages
//  MM_JOY1BUTTONUP
//  MM_JOY1MOVE
//  MM_JOY1ZMOVE
//  MM_JOY2BUTTONDOWN
//  MM_JOY2BUTTONUP
//  MM_JOY2MOVE
//  MM_JOY2ZMOVE
//  MM_MCINOTIFY          - MultiMedia messages


BOOL FASTCALL WM32NoThunking(LPWM32MSGPARAMEX lpwm32mpex)
{

#if 0
    //
    // this routine is never called!  It's used as a placeholder.
    // if you want to make a change here, you have to make the change
    // to the places where we compare the thunk routine to WM32NoThunking
    // and only call the thunk routine if it's not this.  also make sure
    // that this 'default' thunking happens for NoThunking messages.
    //

    if (lpwm32mpex->fThunk) {
        LOGDEBUG(6,("    No Thunking was required for the 32-bit message %s(%04x)\n", (LPSZ)GetWMMsgName(lpwm32mpex->uMsg), lpwm32mpex->uMsg));

        lpwm32mpex->Parm16.WndProc.wMsg = (WORD)lpwm32mpex->uMsg;
        lpwm32mpex->Parm16.WndProc.wParam = (WORD)lpwm32mpex->uParam;
        lpwm32mpex->Parm16.WndProc.lParam = (LONG)lpwm32mpex->lParam;
    }

    //
    // this routine is never called!  It's used as a placeholder.
    // if you want to make a change here, you have to make the change
    // to the places where we compare the thunk routine to WM32NoThunking
    // and only call the thunk routine if it's not this.
    //
#endif

    //
    // Return FALSE, so if for some reason this routine gets used
    // the failure to thunk will be apparent.
    //

    return FALSE;
}

#ifdef DEBUG         // see the macro WM32UNDOCUMENTED

// These are undocumented messages for Win 3.0 so take a look at the app
// who is using them.

BOOL FASTCALL WM32Undocumented(LPWM32MSGPARAMEX lpwm32mpex)
{

    if (lpwm32mpex->fThunk) {
        LOGDEBUG(3,(" Window %08lX is receiving Undocumented Message %s\n", lpwm32mpex->hwnd, (LPSZ)GetWMMsgName(lpwm32mpex->uMsg), lpwm32mpex->uMsg));

        lpwm32mpex->Parm16.WndProc.wMsg = (WORD)lpwm32mpex->uMsg;
        lpwm32mpex->Parm16.WndProc.wParam = (WORD)lpwm32mpex->uParam;
        lpwm32mpex->Parm16.WndProc.lParam = (LONG)lpwm32mpex->lParam;
    }

    return (TRUE);
}

#endif



// This function thunks the messages,
//
//  WM_CREATE
//  WM_NCCREATE
//

BOOL FASTCALL WM32Create(LPWM32MSGPARAMEX lpwm32mpex)
{


    INT cb;
    VPVOID vpClass = 0;
    VPVOID vpName = 0;
    VPVOID vpCreateParams = 0;
    register PCREATESTRUCT16 pcws16;
    LPCREATESTRUCT lParam = (LPCREATESTRUCT) lpwm32mpex->lParam;

    if (lpwm32mpex->fThunk) {

        if (HIWORD(lParam)) {

            // BUGBUG -- The assumption here is that GlobalAlloc will never
            // return a memory object that isn't word-aligned, so that we can
            // assign word-aligned words directly;  we have no idea whether the
            // memory is dword-aligned or not however, so dwords must always
            // be paranoidly stored with the STOREDWORD/STORELONG macros -JTP


            if (lParam->lpszClass) {
                if ( HIWORD(lParam->lpszClass) == 0 ) {
                    vpClass = (VPVOID)lParam->lpszClass;
                }
                else {
                    cb = strlen(lParam->lpszClass)+1;
                    if (!(vpClass = malloc16(cb)))
                        goto Error;
                    putstr16(vpClass, lParam->lpszClass, cb);
                }
            }

            if (lParam->lpszName) {
                cb = strlen(lParam->lpszName)+1;
                if (!(vpName = malloc16(cb)))
                    goto Error;
                putstr16(vpName, lParam->lpszName, cb);
            }

            if (lpwm32mpex->pww == NULL) {
                lpwm32mpex->pww = (PWW)GetWindowLong(lpwm32mpex->hwnd, GWL_WOWWORDS);
                if (lpwm32mpex->pww == NULL)
                    return FALSE;   // Window is dead
            }

            if (lParam->lpCreateParams && (lpwm32mpex->pww->ExStyle & WS_EX_MDICHILD) ) {
                // This works because wm32mdicreate thunk doesn't use any
                // parameters except lParam

                WM32MSGPARAMEX wm32mpexT;
                wm32mpexT.fThunk = lpwm32mpex->fThunk;
                wm32mpexT.hwnd = lpwm32mpex->hwnd;
                wm32mpexT.uMsg = WM_MDICREATE;
                wm32mpexT.uParam = lpwm32mpex->uParam;
                wm32mpexT.lParam = (LONG)lParam->lpCreateParams;
                wm32mpexT.pww = lpwm32mpex->pww;
                wm32mpexT.fFree = lpwm32mpex->fFree;
                wm32mpexT.Parm16.WndProc.lParam = 0;
                WM32MDICreate(&wm32mpexT);
                lpwm32mpex->dwParam = wm32mpexT.dwParam;
                vpCreateParams = wm32mpexT.Parm16.WndProc.lParam;
            }
            else {
                vpCreateParams = (VPVOID)lParam->lpCreateParams;
            }

            // be sure allocation size matches stackfree16() size below
            if (!(lpwm32mpex->Parm16.WndProc.lParam = stackalloc16(sizeof(CREATESTRUCT16))))
                return FALSE;

            GETVDMPTR(lpwm32mpex->Parm16.WndProc.lParam, sizeof(CREATESTRUCT16), pcws16);

            STOREDWORD(pcws16->vpszClass, vpClass);
            STOREDWORD(pcws16->vpszWindow, vpName);
            STOREDWORD(pcws16->vpCreateParams, vpCreateParams);

            lpwm32mpex->dwTmp[0] = vpClass; // store for later freeing
            lpwm32mpex->dwTmp[1] = vpName;


            // BUGBUG 08-Apr-91 JeffPar -- What if hModule is for a 32-bit task?
            pcws16->hInstance    = GETHINST16(lParam->hInstance);
            pcws16->hMenu    = GETHMENU16(lParam->hMenu);
            pcws16->hwndParent   = GETHWND16(lParam->hwndParent);
            pcws16->cy       = (SHORT)lParam->cy;
            pcws16->cx       = (SHORT)lParam->cx;
            pcws16->y        = (SHORT)lParam->y;
            pcws16->x        = (SHORT)lParam->x;
            STOREDWORD(pcws16->dwStyle, lParam->style);
            STOREDWORD(pcws16->dwExStyle, lParam->dwExStyle);

            FLUSHVDMPTR(lpwm32mpex->Parm16.WndProc.lParam, sizeof(CREATESTRUCT16), pcws16);
            FREEVDMPTR(pcws16);

            return TRUE;

          Error:
            LOGDEBUG(LOG_ALWAYS,(" !!!! WM32Create, WM_CREATE thunking failed !!!! Window %08lX ", lpwm32mpex->hwnd));
            if (HIW(vpClass)) free16(vpClass);
            if (vpName)       free16(vpName);
            return (FALSE);

            // do some clean up
            // UnThunkWMCreate32(lParam, lpwm32mpex->Parm16.WndProc.lParam);

        } else {
            return TRUE;
        }




    }
    else {

        if (lpwm32mpex->Parm16.WndProc.lParam) {

            if (lpwm32mpex->pww == NULL) {
                lpwm32mpex->pww = (PWW)GetWindowLong(lpwm32mpex->hwnd, GWL_WOWWORDS);
                if (lpwm32mpex->pww == NULL)
                    return FALSE;   // Window is dead
            }

            if (lParam->lpCreateParams && (lpwm32mpex->pww->ExStyle & WS_EX_MDICHILD) ) {
                WM32MSGPARAMEX wm32mpexT;
                GETVDMPTR(lpwm32mpex->Parm16.WndProc.lParam, sizeof(CREATESTRUCT16), pcws16);
                wm32mpexT.fThunk = lpwm32mpex->fThunk;
                wm32mpexT.hwnd = lpwm32mpex->hwnd;
                wm32mpexT.uMsg = WM_MDICREATE;
                wm32mpexT.uParam = lpwm32mpex->uParam;
                wm32mpexT.lParam = (LONG)lParam->lpCreateParams;
                wm32mpexT.pww = lpwm32mpex->pww;
                wm32mpexT.fFree = lpwm32mpex->fFree;
                wm32mpexT.Parm16.WndProc.lParam = (VPVOID)FETCHDWORD(pcws16->vpCreateParams);
                wm32mpexT.lReturn = 0;
                wm32mpexT.dwParam = lpwm32mpex->dwParam;
                WM32MDICreate(&wm32mpexT);
                FREEVDMPTR(pcws16);
            }

            vpClass = lpwm32mpex->dwTmp[0];
            vpName  = lpwm32mpex->dwTmp[1];

            //  if HIWORD(class) is zero, class is an atom, else a pointer.

            if (HIW16(vpClass)) {
                free16(vpClass);
            }

            if (vpName) {
                free16(vpName);
            }

            stackfree16((VPVOID) lpwm32mpex->Parm16.WndProc.lParam,
                        sizeof(CREATESTRUCT16));
        }

        return TRUE;
    }

}


// This function thunks the messages,
//
//  WM_NCACTIVATE
//  WM_ACTIVATE
//

BOOL FASTCALL WM32Activate(LPWM32MSGPARAMEX lpwm32mpex)
{

    if (lpwm32mpex->fThunk) {
        lpwm32mpex->Parm16.WndProc.wParam = LOWORD(lpwm32mpex->uParam);
        LOW(lpwm32mpex->Parm16.WndProc.lParam) = GETHWND16(lpwm32mpex->lParam);
        HIW(lpwm32mpex->Parm16.WndProc.lParam) = HIWORD(lpwm32mpex->uParam);
    }

    return (TRUE);
}



// This function thunks the messages,
//
//  WM_VKEYTOITEM
//  WM_CHARTOITEM
//  WM_BEGINDRAG
//

BOOL FASTCALL WM32VKeyToItem(LPWM32MSGPARAMEX lpwm32mpex)
{

    if (lpwm32mpex->fThunk) {
        LOW(lpwm32mpex->Parm16.WndProc.lParam) = GETHWND16(lpwm32mpex->lParam);
        HIW(lpwm32mpex->Parm16.WndProc.lParam) = HIWORD(lpwm32mpex->uParam);
    }
    else {
        lpwm32mpex->lReturn = (INT)(SHORT)(lpwm32mpex->lReturn); // sign extend.
    }

    return (TRUE);
}


// This function thunks the messages,
//
//  WM_SETFOCUS
//  WM_KILLFOCUS
//  WM_SETCURSOR
//  WM_MOUSEACTIVATE
//  WM_MDIDESTROY
//  WM_MDIRESTORE
//  WM_MDIMAXIMIZE
//  WM_VSCROLLCLIPBOARD
//  WM_HSCROLLCLIPBOARD
//  WM_PALETTECHANGED
//  WM_PALETTEISCHANGING
//  WM_INITDIALOG
//

BOOL FASTCALL WM32SetFocus(LPWM32MSGPARAMEX lpwm32mpex)
{

    if (lpwm32mpex->fThunk) {
        lpwm32mpex->Parm16.WndProc.wParam = GETHWND16(lpwm32mpex->uParam);
    }

    return (TRUE);
}

// This function thunks the messages,
//
//  WM_SETTEXT
//  WM_WININICHANGE
//  WM_DEVMODECHANGE
//

BOOL FASTCALL WM32SetText(LPWM32MSGPARAMEX lpwm32mpex)
{
    INT cb;


    if (lpwm32mpex->fThunk) {
        if (lpwm32mpex->lParam) {

            LONG lParam = (LONG)GetParam16(lpwm32mpex->lParam);
            if (lParam) {
                lpwm32mpex->Parm16.WndProc.lParam = lParam;
                return (TRUE);
            }

            cb = strlen((LPSZ)lpwm32mpex->lParam)+1;
            lpwm32mpex->dwTmp[0] = (DWORD)cb;

            // winworks2.0a requires DS based string pointers for this message
            if (CURRENTPTD()->dwWOWCompatFlags & WOWCF_DSBASEDSTRINGPOINTERS) {

                // be sure allocation size matches stackfree16() size below
                if (!(lpwm32mpex->Parm16.WndProc.lParam = stackalloc16(cb)))
                    return FALSE;

            } else {
                if (!(lpwm32mpex->Parm16.WndProc.lParam = malloc16(cb)))
                    return FALSE;
            }
            putstr16((VPSZ)lpwm32mpex->Parm16.WndProc.lParam, (LPSZ)lpwm32mpex->lParam, cb);
        }
    }
    else {
// BUGBUG 09-Apr-91 -- Should I copy back?
        if (DeleteParamMap(lpwm32mpex->Parm16.WndProc.lParam, PARAM_16, NULL)) {
            return TRUE;
        }

        if (lpwm32mpex->Parm16.WndProc.lParam) {
            if (CURRENTPTD()->dwWOWCompatFlags & WOWCF_DSBASEDSTRINGPOINTERS) {
                stackfree16((VPVOID) lpwm32mpex->Parm16.WndProc.lParam,
                            ((UINT)lpwm32mpex->dwTmp[0]));
            } else {
                free16((VPVOID) lpwm32mpex->Parm16.WndProc.lParam);
            }
        }
    }

    return (TRUE);
}


// This function thunks the message,
//
//  WM_GETTEXT
//

BOOL FASTCALL WM32GetText(LPWM32MSGPARAMEX lpwm32mpex)
{
    INT cb;
    LPSTR   psz;
    INT cbWrote;



    if (lpwm32mpex->fThunk) {

        if (CURRENTPTD()->dwWOWCompatFlags & WOWCF_DSBASEDSTRINGPOINTERS) {
            //
            // msworks 2.0a has a wndproc called EdWnProc() which when it gets
            // a WM_GETTEXT, assumes lParam is a based pointer whose segment
            // value is equal to winwork's ds. That is true under win3.1, but
            // if wow calls malloc16, it'll have a different segment value.
            // so instead alloc the space on the caller's stack. Since most
            // apps have SS == DS, this will fix apps that do this, including
            // msworks 2.0a.
            //

            // be sure allocation size matches stackfree16() size below
            lpwm32mpex->dwTmp[0] = (DWORD)lpwm32mpex->Parm16.WndProc.wParam;
            lpwm32mpex->Parm16.WndProc.lParam = stackalloc16(lpwm32mpex->dwTmp[0]);

        } else {
            lpwm32mpex->Parm16.WndProc.lParam = malloc16(lpwm32mpex->Parm16.WndProc.wParam);
        }

        //
        // non-zero fill to detect people who write more than they
        // say that they do!
        //
        GETVDMPTR(lpwm32mpex->Parm16.WndProc.lParam, lpwm32mpex->Parm16.WndProc.wParam, psz);
        RtlFillMemory(psz, lpwm32mpex->Parm16.WndProc.wParam, 0xff);
        FLUSHVDMPTR(lpwm32mpex->Parm16.WndProc.lParam, lpwm32mpex->Parm16.WndProc.wParam, psz);
        FREEVDMPTR(psz);
        return (BOOL)lpwm32mpex->Parm16.WndProc.lParam;
    }
    else {
        // some apps return garbage in the high word.  safely assume
        // that cbWindowText < 64K
        HIW(lpwm32mpex->lReturn) = 0;

        // it is necessary to check the length of the buffer, specified in
        // lpwm32mpex->uParam. if number of bytes (lpwm32mpex->lReturn) that are to be copied is
        // EQUAL to the length of the buffer, then copy ONLY the bytes EQUAL
        // to the length of the buffer.
        //

        // Paradox is one of the apps where this condition is hit.
        // bug # 4272.


        //

        if (lpwm32mpex->Parm16.WndProc.lParam) {

            cb = lpwm32mpex->lReturn + 1;

            if (lpwm32mpex->uParam == 0) {
                // cb = 0 if lReturn == 0 && uParam == 0

                if (cb == 1)
                    cb--;
            }
            else if (cb == 2 || cb == 1) {
                // Here only if uParam != 0
                //
                // Determine how much of the buffer they touched!
                //
                // MyAdvancedLabelMaker returns 1 when they really return
                // more than 1.  Since the return 1, cb will be 2.  Then
                // We check to see how much of the buffer they really modified.
                // Then we lie and say that they really filled in that much
                // of the buffer.
                //
                // Sql administator also does this, except it returns 0
                // bug 7731

                GETVDMPTR(lpwm32mpex->Parm16.WndProc.lParam, lpwm32mpex->Parm16.WndProc.wParam, psz);

                cbWrote = lpwm32mpex->uParam;
                while (cbWrote && (psz[cbWrote-1] == '\xff')) {
                    cbWrote--;
                }
                // copy out as many bytes as they wrote
                // distinguish between 'zerobytes written vs. one byte written'

                lpwm32mpex->lReturn = (cbWrote) ? (cbWrote - 1) : 0;
                cb = cbWrote;

                FREEVDMPTR(psz);
            }


            // cb = min(cb, wparam) only if wparam != 0
            // MSPROFIT: does
            //    ret = sendmessage(hwnd, wm_gettest, wparam = 0, lparam);
            //    where ret != 0. so we have to copy the necessary bytes into
            //    lparam eventhough wparam is zero. It does this for reading
            //    those ominprseent "$0.00" strings in the app (ledgers etc).
            //
            //                                   - nanduri

            if (lpwm32mpex->uParam && (UINT)cb > lpwm32mpex->uParam) {
                cb = lpwm32mpex->uParam;
            }

            getstr16((VPSZ)lpwm32mpex->Parm16.WndProc.lParam, (LPSZ)lpwm32mpex->lParam, cb);

            if (CURRENTPTD()->dwWOWCompatFlags & WOWCF_DSBASEDSTRINGPOINTERS) {
                stackfree16((VPVOID) lpwm32mpex->Parm16.WndProc.lParam,
                            ((UINT)lpwm32mpex->dwTmp[0]));
            } else {
                free16((VPVOID) lpwm32mpex->Parm16.WndProc.lParam);
            }
        }
    }

    return (TRUE);
}


// This function thunks the messages,
//
//  WM_ERASEBKGND
//  WM_ICONERASEBKGND
//

BOOL FASTCALL WM32EraseBkGnd(LPWM32MSGPARAMEX lpwm32mpex)
{

    if (lpwm32mpex->fThunk) {
        lpwm32mpex->Parm16.WndProc.wParam = GETHDC16(lpwm32mpex->uParam);
    }

    return (TRUE);
}




// This function thunks the message
//
//  WM_CHANGEUISTATE
//  WM_UPDATEUISTATE
//  WM_QUERYUISTATE
//

BOOL FASTCALL WM32xxxUIState(LPWM32MSGPARAMEX lpwm32mpex)
{

    // just copy the wParam into the lParam
    if (lpwm32mpex->fThunk) {
        lpwm32mpex->Parm16.WndProc.lParam = (LONG)lpwm32mpex->uParam;
        lpwm32mpex->Parm16.WndProc.wParam = 0;

        // this is here because there is talk that they might extend this
        // message to use lParam -- heck, they didn't even tell us that they
        // implemented this message in the first place!! (shame on shell!!)
        WOW32WARNMSG((lpwm32mpex->lParam == 0),
                     ("WOW::WM32xxxUIState:lParam != 0. Better investigate!\n"));
    }

    // now just reverse the process
    else {
        lpwm32mpex->uParam = (UINT)lpwm32mpex->Parm16.WndProc.lParam;
        lpwm32mpex->lParam = 0;
    }

    return (TRUE);
}





// This function thunks the messages,
//
//  WM_ACTIVATEAPP
//

BOOL FASTCALL WM32ActivateApp(LPWM32MSGPARAMEX lpwm32mpex)
{
    extern void UpdateInt16State(void);

    if (lpwm32mpex->fThunk) {

        LOW(lpwm32mpex->Parm16.WndProc.lParam) =
            lpwm32mpex->lParam
              ? ThreadID32toHtask16((DWORD)lpwm32mpex->lParam)
              : 0;

        // We need to update wow int 16 bios when I wow app gets the focus.
        UpdateInt16State();
    }

    return (TRUE);
}


// This function thunks the messages,
//
//  WM_GETMINMAXINFO
//

BOOL FASTCALL WM32GetMinMaxInfo(LPWM32MSGPARAMEX lpwm32mpex)
{
    LPPOINT lParam = (LPPOINT) lpwm32mpex->lParam;


    if (lpwm32mpex->fThunk) {
        if (lParam) {

            // be sure allocation size matches stackfree16() size below
            lpwm32mpex->Parm16.WndProc.lParam = stackalloc16(sizeof(POINT16)*5);

            UnThunkWMGetMinMaxInfo16(lpwm32mpex->Parm16.WndProc.lParam, lParam);
        }
    }
    else {
        ThunkWMGetMinMaxInfo16(lpwm32mpex->Parm16.WndProc.lParam, &lParam);
        if(lpwm32mpex->Parm16.WndProc.lParam) {
            stackfree16((VPVOID) lpwm32mpex->Parm16.WndProc.lParam,
                        sizeof(POINT16)*5);
        }
    }

    return(TRUE);
}



// This function thunks the messages,
//
//  WM_NCPAINT
//

BOOL FASTCALL WM32NCPaint(LPWM32MSGPARAMEX lpwm32mpex)
{

    if (lpwm32mpex->fThunk) {
        lpwm32mpex->Parm16.WndProc.wParam = (lpwm32mpex->uParam == 1) ? 1 :
                                              GETHDC16(lpwm32mpex->uParam);
    }
    return (TRUE);
}



// This function thunks the messages,
//
//  WM_NCDESTROY
//

BOOL FASTCALL WM32NCDestroy(LPWM32MSGPARAMEX lpwm32mpex)
{

    // destroy any timers associated with this window
    if (!lpwm32mpex->fThunk) {
        FreeWindowTimers16(lpwm32mpex->hwnd);
    }
    return (TRUE);
}


// This function thunks the messages,
//
//  WM_GETDLGCODE
//
BOOL FASTCALL WM32GetDlgCode(LPWM32MSGPARAMEX lpwm32mpex)
{

    if (lpwm32mpex->fThunk) {
        if (lpwm32mpex->lParam) {

            // BUGBUG -- The assumption here is that GlobalAlloc will never
            // return a memory object that isn't word-aligned, so that we can
            // assign word-aligned words directly;  we have no idea whether the
            // memory is dword-aligned or not however, so dwords must always
            // be paranoidly stored with the STOREDWORD/STORELONG macros -JTP

            if (!(lpwm32mpex->Parm16.WndProc.lParam = malloc16(sizeof(MSG16))))
                return FALSE;

            putmsg16(lpwm32mpex->Parm16.WndProc.lParam, (LPMSG)lpwm32mpex->lParam);

            return TRUE;
        }
    }
    else {
        // Message structure doesn't need to be copied back does it? -Bob

        if (lpwm32mpex->Parm16.WndProc.lParam) {
            free16((VPVOID) lpwm32mpex->Parm16.WndProc.lParam);
        }

    }
    return (TRUE);
}

// This function thunks the messages,
//
//  WM_NEXTDLGCTL
//

BOOL FASTCALL WM32NextDlgCtl(LPWM32MSGPARAMEX lpwm32mpex)
{

    if (lpwm32mpex->fThunk) {
        if (lpwm32mpex->lParam) {
            lpwm32mpex->Parm16.WndProc.wParam = GETHWND16(lpwm32mpex->uParam);
        }
    }
    return (TRUE);
}


// This function thunks the messages,
//
//  WM_DRAWITEM
//

BOOL FASTCALL WM32DrawItem(LPWM32MSGPARAMEX lpwm32mpex)
{
    LPDRAWITEMSTRUCT lParam = (LPDRAWITEMSTRUCT) lpwm32mpex->lParam;


    if (lpwm32mpex->fThunk) {
        if (lParam) {

            // be sure allocation size matches stackfree16() size below
            lpwm32mpex->Parm16.WndProc.lParam = stackalloc16(sizeof(DRAWITEMSTRUCT16));
            putdrawitem16(lpwm32mpex->Parm16.WndProc.lParam, lParam);
        }
    }
    else {
        // BUGBUG 08-Apr-91 JeffPar -- Reflect changes back to 32-bit structure?
        if (lpwm32mpex->Parm16.WndProc.lParam)
            stackfree16((VPVOID) lpwm32mpex->Parm16.WndProc.lParam,
                        sizeof(DRAWITEMSTRUCT16));
    }

    return(TRUE);
}


// This function thunks the messages,
//
//  WM_MEASUREITEM
//

BOOL FASTCALL WM32MeasureItem(LPWM32MSGPARAMEX lpwm32mpex)
{
    PMEASUREITEMSTRUCT16 pmis16;
    LPMEASUREITEMSTRUCT lParam = (LPMEASUREITEMSTRUCT) lpwm32mpex->lParam;
    BOOL    fHasStrings;
    DWORD   cSize;


    //
    // Compatibility hack
    //
    // CrossTalk 2.0 has a bug where it fails to distinguish between
    // WM_MEASUREITEM and WM_INITDIALOG when doing file.open
    // on WM_MEASUREITEM it calls CallWindowProc() to send what it
    // thinks is lpOpenFileName->lpCust but is really random stack.
    // currently the high word of this random pointer is an hInstance
    // and gets through the validation layer, whereas on Win31 it doesn't.
    // if this WM_MEASUREITEM gets to the app's proc then the app will
    // initialize incorrectly and take a GP.  i have increased the stack
    // allocation by XTALKHACK to ensure that the random data does is not
    // a valid pointer.
    //

#define XTALKHACK (sizeof(OPENFILENAME16)-sizeof(MEASUREITEMSTRUCT16))


    if (lpwm32mpex->fThunk) {
        if (lParam) {

            fHasStrings = FALSE;
            if ( lParam->CtlType == ODT_COMBOBOX || lParam->CtlType == ODT_LISTBOX ) {
                if (lParam->itemWidth == MIFLAG_FLAT) {
                    fHasStrings = TRUE;
                }
            }

            cSize = sizeof(MEASUREITEMSTRUCT16);
            if ( fHasStrings ) {
                cSize += strlen((LPSTR)lParam->itemData) + 1;
            }

            if ( cSize < XTALKHACK+sizeof(MEASUREITEMSTRUCT16) ) {
                cSize = XTALKHACK+sizeof(MEASUREITEMSTRUCT16);
            }

            // be sure allocation size matches stackfree16() size below
            lpwm32mpex->dwTmp[0] = cSize;            
            if ( !(lpwm32mpex->Parm16.WndProc.lParam = stackalloc16(cSize)) )
                return FALSE;

            GETVDMPTR(lpwm32mpex->Parm16.WndProc.lParam, cSize, pmis16);

            pmis16->CtlType = (WORD)lParam->CtlType;
            pmis16->CtlID   = (WORD)lParam->CtlID;
            pmis16->itemID  = (WORD)lParam->itemID;
            pmis16->itemWidth   = (WORD)lParam->itemWidth;
            pmis16->itemHeight  = (WORD)lParam->itemHeight;

#ifdef XTALKHACK
            ((POPENFILENAME16)pmis16)->lCustData = 7;   // invalid far pointer
#endif
            if ( fHasStrings ) {
                pmis16->itemData = lpwm32mpex->Parm16.WndProc.lParam+sizeof(MEASUREITEMSTRUCT16);
                strcpy( (LPSTR)(pmis16+1), (LPSTR)lParam->itemData );
            } else {
                STOREDWORD(pmis16->itemData, lParam->itemData);
            }

            WOW32ASSERT(HIWORD(cSize) == 0);
            FLUSHVDMPTR(lpwm32mpex->Parm16.WndProc.lParam, (USHORT) cSize, pmis16);
            FREEVDMPTR(pmis16);
        }
    }
    else {
        if (lpwm32mpex->Parm16.WndProc.lParam) {
            GETVDMPTR(lpwm32mpex->Parm16.WndProc.lParam, sizeof(MEASUREITEMSTRUCT16), pmis16);

            lParam->CtlType = WORD32(pmis16->CtlType);
            lParam->CtlID   = WORD32(pmis16->CtlID);
            lParam->itemID  = WORD32(pmis16->itemID);

            // itemWidth must sign extend (PPT3 bug & Win3.1 treats it as signed!)
            lParam->itemWidth   = INT32(pmis16->itemWidth);

            lParam->itemHeight  = WORD32(pmis16->itemHeight);
            lParam->itemData    = pmis16->itemData;

            FREEVDMPTR(pmis16);

            stackfree16((VPVOID) lpwm32mpex->Parm16.WndProc.lParam,
                        ((UINT)lpwm32mpex->dwTmp[0]));
        }
    }

    return(TRUE);
}


// This function thunks the messages,
//
//  WM_DELETEITEM
//


BOOL FASTCALL WM32DeleteItem(LPWM32MSGPARAMEX lpwm32mpex)
{
    register PDELETEITEMSTRUCT16 pdes16;
    LPDELETEITEMSTRUCT lParam = (LPDELETEITEMSTRUCT) lpwm32mpex->lParam;



    if (lpwm32mpex->fThunk) {
        if (lParam) {

            // BUGBUG -- The assumption here is that GlobalAlloc will never
            // return a memory object that isn't word-aligned, so that we can
            // assign word-aligned words directly;  we have no idea whether the
            // memory is dword-aligned or not however, so dwords must always
            // be paranoidly stored with the STOREDWORD/STORELONG macros -JTP

            // be sure allocation size matches stackfree16() size below
            lpwm32mpex->Parm16.WndProc.lParam = stackalloc16(sizeof(DELETEITEMSTRUCT16));
            GETVDMPTR(lpwm32mpex->Parm16.WndProc.lParam, sizeof(DELETEITEMSTRUCT16), pdes16);

            pdes16->CtlType = (WORD)lParam->CtlType;
            pdes16->CtlID   = (WORD)lParam->CtlID;
            pdes16->itemID  = (WORD)lParam->itemID;
            pdes16->hwndItem    = GETHWND16(lParam->hwndItem);
            STOREDWORD(pdes16->itemData, lParam->itemData);

            FLUSHVDMPTR(lpwm32mpex->Parm16.WndProc.lParam, sizeof(DELETEITEMSTRUCT16), pdes16);
            FREEVDMPTR(pdes16);
        }
    }
    else {
        if (lpwm32mpex->Parm16.WndProc.lParam)
            stackfree16((VPVOID) lpwm32mpex->Parm16.WndProc.lParam,
                        sizeof(DELETEITEMSTRUCT16));
    }

    return(TRUE);
}


// This function thunks the messages,
//
//  WM_SETFONT
//

BOOL FASTCALL WM32SetFont(LPWM32MSGPARAMEX lpwm32mpex)
{

    if (lpwm32mpex->fThunk) {
        lpwm32mpex->Parm16.WndProc.wParam = GETHFONT16(lpwm32mpex->uParam);
    }

    return (TRUE);
}


// This function thunks the messages,
//
//  WM_QUERYDRAGICON

BOOL FASTCALL WM32QueryDragIcon(LPWM32MSGPARAMEX lpwm32mpex)
{

    if (!lpwm32mpex->fThunk) {
        lpwm32mpex->lReturn = (LONG)HICON32(lpwm32mpex->lReturn);
    }

    return (TRUE);
}


// This function thunks the messages,
//
//  WM_COMPAREITEM
//

BOOL FASTCALL WM32CompareItem(LPWM32MSGPARAMEX lpwm32mpex)
{
    LPCOMPAREITEMSTRUCT lParam = (LPCOMPAREITEMSTRUCT) lpwm32mpex->lParam;


    if (lpwm32mpex->fThunk) {
        if (lParam) {

            // BUGBUG -- The assumption here is that GlobalAlloc will never
            // return a memory object that isn't word-aligned, so that we can
            // assign word-aligned words directly;  we have no idea whether the
            // memory is dword-aligned or not however, so dwords must always
            // be paranoidly stored with the STOREDWORD/STORELONG macros -JTP

            // be sure allocation size matches stackfree16() size below
            lpwm32mpex->Parm16.WndProc.lParam = stackalloc16(sizeof(COMPAREITEMSTRUCT16));
            putcompareitem16(lpwm32mpex->Parm16.WndProc.lParam, lParam);
        }
    }
    else {
        // BUGBUG 08-Apr-91 JeffPar -- Reflect changes back to 32-bit structure?
        if (lpwm32mpex->Parm16.WndProc.lParam)
            stackfree16((VPVOID) lpwm32mpex->Parm16.WndProc.lParam,
                        sizeof(COMPAREITEMSTRUCT16));
    }

    return (TRUE);
}




// This function thunks the messages,
//
//  WM_SIZING
//

BOOL FASTCALL WM32Sizing(LPWM32MSGPARAMEX lpwm32mpex)
{
    VPRECT16   vpRect16;


    if (lpwm32mpex->fThunk) {

        if (lpwm32mpex->lParam) {

            // be sure allocation size matches stackfree16() size below
            vpRect16 = (VPVOID)stackalloc16(sizeof(RECT16));

            if(vpRect16) {
                putrect16(vpRect16, (LPRECT)lpwm32mpex->lParam);

                lpwm32mpex->Parm16.WndProc.lParam = (LONG)vpRect16;
            }
        }
    }
    else {

        if (lpwm32mpex->lParam) {

            vpRect16 = (VPVOID)lpwm32mpex->Parm16.WndProc.lParam;

            if(vpRect16) {
                getrect16(vpRect16, (LPRECT)lpwm32mpex->lParam);

                stackfree16(vpRect16, sizeof(RECT16));
            }
        }
    }

    return (TRUE);
}






// This function thunks the messages,
//
//  WM_NCCALCSIZE
//

BOOL FASTCALL WM32NCCalcSize(LPWM32MSGPARAMEX lpwm32mpex)
{
    PNCCALCSIZE_PARAMS16 pnc16;
    PNCCALCSIZE_PARAMS16 lpnc16;
    VPWINDOWPOS16        vpwp16;
    LPNCCALCSIZE_PARAMS  lParam = (LPNCCALCSIZE_PARAMS)lpwm32mpex->lParam;
    UINT                 cb;
    VPVOID               vp;


    // lpwm32mpex->uParam == TRUE ?  (lParam is LPNCCALCSIZE_PARAMS) : (lParam is LPRECT);
    //

    if (lpwm32mpex->fThunk) {
        if (lParam) {
            if (lpwm32mpex->uParam)
                cb = sizeof(NCCALCSIZE_PARAMS16) + sizeof(WINDOWPOS16);
            else
                cb = sizeof(RECT16);

            // be sure allocation size matches stackfree16() size below
            lpwm32mpex->dwTmp[0] = cb;
            vp = (VPVOID)stackalloc16(cb);

            lpwm32mpex->Parm16.WndProc.lParam = (LONG)vp;

            putrect16((VPRECT16)vp, (LPRECT)lParam);
            if (lpwm32mpex->uParam) {
                pnc16 = (PNCCALCSIZE_PARAMS16)vp;
                putrect16((VPRECT16)(&pnc16->rgrc[1]), &lParam->rgrc[1]);
                putrect16((VPRECT16)(&pnc16->rgrc[2]), &lParam->rgrc[2]);

                GETVDMPTR( pnc16, sizeof(NCCALCSIZE_PARAMS16), lpnc16 );

                vpwp16 = (VPWINDOWPOS16)(pnc16+1);
                lpnc16->lppos = (PWINDOWPOS16)vpwp16;

                FREEVDMPTR( lpnc16 );

                putwindowpos16( vpwp16, lParam->lppos );

            }
        }
    }
    else {
        vp = (VPVOID)lpwm32mpex->Parm16.WndProc.lParam;
        getrect16((VPRECT16)vp, (LPRECT)lParam);
        if (lpwm32mpex->uParam) {
            pnc16 = (PNCCALCSIZE_PARAMS16)vp;

            getrect16((VPRECT16)(&pnc16->rgrc[1]), &lParam->rgrc[1]);
            getrect16((VPRECT16)(&pnc16->rgrc[2]), &lParam->rgrc[2]);

            GETVDMPTR( pnc16, sizeof(NCCALCSIZE_PARAMS16), lpnc16 );

            vpwp16 = (VPWINDOWPOS16)lpnc16->lppos;

            FREEVDMPTR( lpnc16 );

            getwindowpos16( vpwp16, lParam->lppos );


        }
        if(vp) {
            stackfree16(vp, ((UINT)lpwm32mpex->dwTmp[0]));
        }
    }

    return (TRUE);
}


// This function thunks the messages,
//
//  WM_COMMAND
//

BOOL FASTCALL WM32Command(LPWM32MSGPARAMEX lpwm32mpex)
{

    if (lpwm32mpex->fThunk) {
            // it's from a control
        HIW(lpwm32mpex->Parm16.WndProc.lParam) = HIWORD(lpwm32mpex->uParam);
        LOW(lpwm32mpex->Parm16.WndProc.lParam) = GETHWND16(lpwm32mpex->lParam);
    }

    return (TRUE);
}


// This function thunks the messages,
//
//  WM_TIMER
//

BOOL FASTCALL WM32Timer(LPWM32MSGPARAMEX lpwm32mpex)
{

    if (lpwm32mpex->fThunk) {

        /*
        ** map the timer number and the timer proc address (cause its easy)
        */
        PTMR ptmr;

        ptmr = FindTimer32((HAND16)GETHWND16(lpwm32mpex->hwnd), lpwm32mpex->uParam);

        if ( !ptmr ) {
            /*
            ** Edit controls create their own timer, which can safely be
            ** thunked to itself.
            */
            if ( lpwm32mpex->lParam || HIWORD(lpwm32mpex->uParam) ) {
                LOGDEBUG(LOG_WARNING,("  WM32Timer ERROR: cannot find timer %08x\n", lpwm32mpex->uParam));
            }
            return TRUE;
        }

        lpwm32mpex->Parm16.WndProc.lParam = ptmr->vpfnTimerProc;
    }

    return (TRUE);
}




// This function thunks the messages,
//
//  WM_HSCROLL
//  WM_VSCROLL
//

BOOL FASTCALL WM32HScroll(LPWM32MSGPARAMEX lpwm32mpex)
{

    if (lpwm32mpex->fThunk) {
        LOW(lpwm32mpex->Parm16.WndProc.lParam) = HIWORD(lpwm32mpex->uParam);
        HIW(lpwm32mpex->Parm16.WndProc.lParam) = GETHWND16(lpwm32mpex->lParam);
    }

    return (TRUE);
}


// This function thunks the messages,
//
//  WM_INITMENU
//  WM_INITMENUPOPUP
//

BOOL FASTCALL WM32InitMenu(LPWM32MSGPARAMEX lpwm32mpex)
{

    if (lpwm32mpex->fThunk) {
        lpwm32mpex->Parm16.WndProc.wParam = GETHMENU16(lpwm32mpex->uParam);
    }

    return (TRUE);
}


// This function thunks the messages,
//
//  WM_MENUSELECT
//

BOOL FASTCALL WM32MenuSelect(LPWM32MSGPARAMEX lpwm32mpex)
{

    if (lpwm32mpex->fThunk) {

        // Copy the menu flags
        LOW(lpwm32mpex->Parm16.WndProc.lParam) = HIWORD(lpwm32mpex->uParam);

        // Copy the "main" menu
        HIW(lpwm32mpex->Parm16.WndProc.lParam) = GETHMENU16(lpwm32mpex->lParam);

        if (HIWORD(lpwm32mpex->uParam) == 0xFFFF || !(HIWORD(lpwm32mpex->uParam) & MF_POPUP)) {
            lpwm32mpex->Parm16.WndProc.wParam = LOWORD(lpwm32mpex->uParam);       // Its an ID
        }
        else {
            // convert menu index into menu handle
            lpwm32mpex->Parm16.WndProc.wParam = GETHMENU16(GetSubMenu((HMENU)lpwm32mpex->lParam, LOWORD(lpwm32mpex->uParam)));
        }
    }

    return (TRUE);
}





// This function thunks the messages,
//
//  WM_MENUCHAR
//

BOOL FASTCALL WM32MenuChar(LPWM32MSGPARAMEX lpwm32mpex)
{

    if (lpwm32mpex->fThunk) {
        LOW(lpwm32mpex->Parm16.WndProc.lParam) = HIWORD(lpwm32mpex->uParam);
        HIW(lpwm32mpex->Parm16.WndProc.lParam) = GETHMENU16(lpwm32mpex->lParam);
    }

    return (TRUE);
}


// This function thunks the messages,
//
//  WM_ENTERIDLE
//

BOOL FASTCALL WM32EnterIdle(LPWM32MSGPARAMEX lpwm32mpex)
{

    if (lpwm32mpex->fThunk) {
        if ((lpwm32mpex->uParam == MSGF_DIALOGBOX) || (lpwm32mpex->uParam == MSGF_MENU)) {
            LOW(lpwm32mpex->Parm16.WndProc.lParam) = GETHWND16(lpwm32mpex->lParam);
            HIW(lpwm32mpex->Parm16.WndProc.lParam) = 0;
        }
        else {
            LOGDEBUG(LOG_ALWAYS,(" WOW::WM_ENTERIDLE: wParam has unknown value, wParam=%08x, Contact ChandanC\n", lpwm32mpex->uParam));
        }
    }
    return (TRUE);
}


// This function thunks the messages,
//
//  WM_PARENTNOTIFY
//

BOOL FASTCALL WM32ParentNotify(LPWM32MSGPARAMEX lpwm32mpex)
{

    if (lpwm32mpex->fThunk) {
        if ((LOWORD(lpwm32mpex->uParam) == WM_CREATE) || (LOWORD(lpwm32mpex->uParam) == WM_DESTROY)) {
            HIW(lpwm32mpex->Parm16.WndProc.lParam) = HIWORD(lpwm32mpex->uParam);
            LOW(lpwm32mpex->Parm16.WndProc.lParam) = GETHWND16(lpwm32mpex->lParam);
        }
    }

    return (TRUE);
}


// This function thunks the messages,
//
//  WM_MDICreate
//

BOOL FASTCALL WM32MDICreate(LPWM32MSGPARAMEX lpwm32mpex)
{
    INT cb;
    VPVOID vp;
    register PMDICREATESTRUCT16 pmcs16;
    LPMDICREATESTRUCT lParam = (LPMDICREATESTRUCT) lpwm32mpex->lParam;


    if (lpwm32mpex->fThunk) {
        if (lParam) {

            lpwm32mpex->dwParam = (DWORD)0;
            if (lParam->szClass) {
                if ( HIWORD(lParam->szClass) == 0 ) {
                    vp = (VPVOID)lParam->szClass;
                }
                else {
                    cb = strlen(lParam->szClass)+1;
                    if (!(vp = malloc16(cb)))
                        goto Error;
                    putstr16(vp, lParam->szClass, cb);
                }
            }
            else {
                vp = (VPVOID)NULL;
            }

            //
            // pfs:windowsworks overwrite pszclass, so we need to save the
            // so that we can free the memory we just alloced
            //
            lpwm32mpex->dwParam = (DWORD)vp;

            if (lParam->szTitle) {
                cb = strlen(lParam->szTitle)+1;
                if (!(vp = malloc16(cb)))
                    goto Error;
                putstr16(vp, lParam->szTitle, cb);
            }
            else {
                vp = (VPVOID)NULL;
            }

            // BUGBUG -- The assumption here is that GlobalAlloc will never
            // return a memory object that isn't word-aligned, so that we can
            // assign word-aligned words directly;  we have no idea whether the
            // memory is dword-aligned or not however, so dwords must always
            // be paranoidly stored with the STOREDWORD/STORELONG macros -JTP

            if (!(lpwm32mpex->Parm16.WndProc.lParam = malloc16(sizeof(MDICREATESTRUCT16))))
                goto Error;

            GETVDMPTR(lpwm32mpex->Parm16.WndProc.lParam, sizeof(MDICREATESTRUCT16), pmcs16);
            STOREDWORD(pmcs16->vpszClass, lpwm32mpex->dwParam);
            STOREDWORD(pmcs16->vpszTitle, vp);
            pmcs16->hOwner  = GETHINST16(lParam->hOwner);
            pmcs16->x       = (SHORT)lParam->x;
            pmcs16->y       = (SHORT)lParam->y;
            pmcs16->cx      = (SHORT)lParam->cx;
            pmcs16->cy      = (SHORT)lParam->cy;
            STORELONG(pmcs16->style, lParam->style);
            STORELONG(pmcs16->lParam, lParam->lParam);

            FLUSHVDMPTR(lpwm32mpex->Parm16.WndProc.lParam, sizeof(MDICREATESTRUCT16), pmcs16);
            FREEVDMPTR(pmcs16);

            return (TRUE);

          Error:
            LOGDEBUG(LOG_ALWAYS,(" !!!! WM32MDICreate, WM_MDICREATE thunking failed !!!! Window %08lX ", lpwm32mpex->hwnd));
            if (HIW16(lpwm32mpex->dwParam)) free16(lpwm32mpex->dwParam);
            if (vp)                         free16(vp);
            return FALSE;
        }
    }
    else {
        if (lpwm32mpex->Parm16.WndProc.lParam) {
            GETVDMPTR(lpwm32mpex->Parm16.WndProc.lParam, sizeof(MDICREATESTRUCT16), pmcs16);

            if (FETCHDWORD(pmcs16->vpszTitle)) {
                free16(FETCHDWORD(pmcs16->vpszTitle));
            }

            FREEVDMPTR(pmcs16);

            //  if HIWORD(class) is zero, class is an atom, else a pointer.

            if (HIW16(lpwm32mpex->dwParam)) {
                free16(lpwm32mpex->dwParam);
            }


            lpwm32mpex->lReturn = (LONG)HWND32(LOWORD(lpwm32mpex->lReturn));
            free16((VPVOID) lpwm32mpex->Parm16.WndProc.lParam);
        }
    }

    return (TRUE);
}


// This function thunks the messages,
//
//  WM_MDIActivate
//

BOOL FASTCALL WM32MDIActivate(LPWM32MSGPARAMEX lpwm32mpex)
{
    BOOL fHwndIsMdiChild;


    if (lpwm32mpex->fThunk) {

        // the format of the message is different based on the window that's
        // receiving the message. If 'hwnd' is a MdiClient window it is of one
        // form and if 'hwnd' is MdiChild it is of another form. We need to
        // distinguish between the formats to correctly thunk the message.
        //
        // NOTE: we donot make calls like GetClassName because they are
        //       expensive and also I think we came across a case where a
        //       window of 'wow private class' processes these messages
        //
        //                                                - Nanduri

        if (lpwm32mpex->lParam) {

            // lParam != NULL. The message is definitely going to a MdiChild.
            //

            fHwndIsMdiChild = TRUE;
        }
        else {

            // lParam == NULL, doesnot necessarily mean that the message is
            // going to a MdiClient window. So distinguish...

            if (lpwm32mpex->uParam && (GETHWND16(lpwm32mpex->hwnd) ==
                    GETHWND16(lpwm32mpex->uParam))) {

                // if hwnd is same as uParam then definitely hwnd is a MdiChild
                // window. (because if hwnd is a MdiClient then uParam will be
                // a MdiChild and thus they will not be equal)

                fHwndIsMdiChild = TRUE;
            }
            else {
                fHwndIsMdiChild = FALSE;
            }

        }

        if (fHwndIsMdiChild) {
            lpwm32mpex->Parm16.WndProc.wParam =
                    (WORD)(GETHWND16(lpwm32mpex->hwnd) == GETHWND16(lpwm32mpex->lParam));
            LOW(lpwm32mpex->Parm16.WndProc.lParam) = GETHWND16(lpwm32mpex->lParam);
            HIW(lpwm32mpex->Parm16.WndProc.lParam) = GETHWND16(lpwm32mpex->uParam);
        } else {
            lpwm32mpex->Parm16.WndProc.wParam = GETHWND16(lpwm32mpex->uParam);
            lpwm32mpex->Parm16.WndProc.lParam = 0;
        }
    }

    return (TRUE);
}


// This function thunks the messages,
//
//  WM_MDIGETACTIVE
//

BOOL FASTCALL WM32MDIGetActive(LPWM32MSGPARAMEX lpwm32mpex)
{

    if (lpwm32mpex->fThunk) {
        lpwm32mpex->Parm16.WndProc.lParam = 0;
    }
    else {

        if (lpwm32mpex->lParam != 0)
            *((LPBOOL)lpwm32mpex->lParam) = (BOOL)HIWORD(lpwm32mpex->lReturn);

        lpwm32mpex->lReturn = (LONG)HWND32(LOWORD(lpwm32mpex->lReturn));
    }

    return (TRUE);
}


// This function thunks the messages,
//
//  WM_MDISETMENU
//

BOOL FASTCALL WM32MDISetMenu(LPWM32MSGPARAMEX lpwm32mpex)
{

    if (lpwm32mpex->fThunk) {
        if (lpwm32mpex->uMsg == WM_MDIREFRESHMENU) {
            lpwm32mpex->Parm16.WndProc.wParam = TRUE;
            lpwm32mpex->Parm16.WndProc.wMsg = WM_MDISETMENU;
        }
        else {
            lpwm32mpex->Parm16.WndProc.wParam = 0;
        }
        LOW(lpwm32mpex->Parm16.WndProc.lParam) = GETHMENU16(lpwm32mpex->uParam);
        HIW(lpwm32mpex->Parm16.WndProc.lParam) = GETHMENU16(lpwm32mpex->lParam);
    }
    else {
        lpwm32mpex->lReturn = (LONG)HMENU32(lpwm32mpex->lReturn);
    }

    return (TRUE);
}


// This function thunks the messages,
//
//  WM_SIZECLIPBOARD
//  WM_PAINTCLIPBOARD
//


BOOL FASTCALL WM32SizeClipBoard(LPWM32MSGPARAMEX lpwm32mpex)
{
    HAND16 hMem16 = 0;
    VPVOID  vp;
    LPRECT  lp;



    if (lpwm32mpex->fThunk) {
        lpwm32mpex->Parm16.WndProc.wParam = GETHWND16(lpwm32mpex->uParam);

        vp = GlobalAllocLock16(GMEM_MOVEABLE, (lpwm32mpex->uMsg == WM_SIZECLIPBOARD) ?
                               sizeof(RECT) : sizeof(PAINTSTRUCT),  &hMem16);
        if (vp) {
            if (lp = (LPRECT) GlobalLock((HANDLE) lpwm32mpex->lParam)) {
                if (lpwm32mpex->uMsg == WM_SIZECLIPBOARD) {
                    PUTRECT16(vp, lp);
                }
                else {
                    putpaintstruct16(vp, (LPPAINTSTRUCT) lp);
                }
                GlobalUnlock((HANDLE) lpwm32mpex->lParam);
            }
            else {
                LOGDEBUG(LOG_ALWAYS, ("WOW::WM32SizeClipboard: Couldn't lock 32 bit memory handle!\n"));
                // WOW32ASSERT (FALSE);
            }

            GlobalUnlock16(hMem16);
        }
        else {
            hMem16 = 0;
            LOGDEBUG(LOG_ALWAYS, ("WOW::WM32SizeClipboard: Couldn't allocate memory !\n"));
            WOW32ASSERT (FALSE);
        }

        LOW(lpwm32mpex->Parm16.WndProc.lParam) = (WORD) hMem16;
    }
    else {
        if (LOW(lpwm32mpex->Parm16.WndProc.lParam)) {
            GlobalUnlockFree16(GlobalLock16(LOW(lpwm32mpex->Parm16.WndProc.lParam), NULL));
        }
    }

    return (TRUE);
}



// This function thunks the messages,
//
//  WM_ASKCBFORMATNAME
//


BOOL FASTCALL WM32AskCBFormatName(LPWM32MSGPARAMEX lpwm32mpex)
{

    if (lpwm32mpex->fThunk) {
        lpwm32mpex->Parm16.WndProc.lParam = malloc16(lpwm32mpex->Parm16.WndProc.wParam);
        if (lpwm32mpex->Parm16.WndProc.lParam) {
            putstr16((VPSZ)lpwm32mpex->Parm16.WndProc.lParam, (LPSZ)lpwm32mpex->lParam, lpwm32mpex->uParam);
        }

        return (BOOL)lpwm32mpex->Parm16.WndProc.lParam;
    }
    else {
        if (lpwm32mpex->Parm16.WndProc.lParam) {
            getstr16((VPSZ)lpwm32mpex->Parm16.WndProc.lParam, (LPSZ)lpwm32mpex->lParam, lpwm32mpex->uParam);
            free16((VPVOID) lpwm32mpex->Parm16.WndProc.lParam);
        }
    }

    return (TRUE);
}


// This function thunks the messages,
//
//  WM_CHANGECBCHAIN
//

BOOL FASTCALL WM32ChangeCBChain(LPWM32MSGPARAMEX lpwm32mpex)
{

    if (lpwm32mpex->fThunk) {
        lpwm32mpex->Parm16.WndProc.wParam = GETHWND16(lpwm32mpex->uParam);
        lpwm32mpex->Parm16.WndProc.lParam = GETHWND16(lpwm32mpex->lParam);
    }

    return (TRUE);
}



// This function thunks the messages,
//
//  WM_DDEINITIATE
//

BOOL FASTCALL WM32DDEInitiate(LPWM32MSGPARAMEX lpwm32mpex)
{

    if (lpwm32mpex->fThunk) {
        lpwm32mpex->Parm16.WndProc.wParam = GETHWND16(lpwm32mpex->uParam);
        lpwm32mpex->Parm16.WndProc.lParam = lpwm32mpex->lParam;
        WI32DDEAddInitiator(lpwm32mpex->Parm16.WndProc.wParam);
    }
    else {
        WI32DDEDeleteInitiator((HAND16)GETHWND16(lpwm32mpex->uParam));
    }

    return (TRUE);
}


// This function thunks the messages,
//
//  WM_DDEACK
//

BOOL FASTCALL WM32DDEAck(LPWM32MSGPARAMEX lpwm32mpex)
{

    if (lpwm32mpex->fThunk) {
        lpwm32mpex->Parm16.WndProc.wParam = GETHWND16(lpwm32mpex->uParam);

        if (WI32DDEInitiate((HAND16)GETHWND16(lpwm32mpex->hwnd))) {
            //
            // Initiate ACK
            //
            lpwm32mpex->Parm16.WndProc.lParam = lpwm32mpex->lParam;
        }
        else {
            //
            // NON-Initiate ACK
            //

            UINT    lLo = 0;
            UINT    lHi = 0;
            PHDDE   pDdeNode;

            UnpackDDElParam(lpwm32mpex->uMsg, lpwm32mpex->lParam, &lLo, &lHi);

            if (!HIWORD(lHi)) {
                //
                // NON-Execute ACK
                //
                HIW(lpwm32mpex->Parm16.WndProc.lParam) = (WORD) lHi;
            }
            else {
                //
                // Execute ACK
                //

                //
                // The order of To_hwnd and From_hwnd is reversed in the following
                // DDEFirstPair16(), below. This is done to locate the h32.
                //

                pDdeNode = DDEFindAckNode ((HAND16)lpwm32mpex->Parm16.WndProc.wParam,
                                           (HAND16)GETHWND16(lpwm32mpex->hwnd),
                                           (HANDLE) lHi);

                if (!pDdeNode) {

                    //
                    // When ShellExecute does DDE_EXECUTE to open a document,
                    // we don't see its Win32 PostMessage calls so we have no
                    // record of the conversation.  This is our first opportunity
                    // to rectify that, the context of WU32GetMessage thunking
                    // a WM_DDE_ACK message.  We could also get here for other
                    // kinds of ACKs, fortunately the Win32 message alone gives
                    // enough context to distinguish the various flavors, unlike
                    // the Win16 WM_DDE_ACK.
                    //

                    if (lpwm32mpex->lParam >= 0xc0000000) {

                        //
                        // ack responding to initiate
                        //

                        lpwm32mpex->Parm16.WndProc.lParam = lpwm32mpex->lParam;
                    }

                    if (lHi > 0xffff) {

                        //
                        // ack responding to execute: global handle in hiword
                        //

                        HAND16 h16 = 0;
                        DWORD cb;
                        VPVOID vp;
                        LPBYTE lpMem16, lpMem32;
                        DDEINFO DdeInfo;

                        if (!lpwm32mpex->fFree) {
                            cb = GlobalSize((HANDLE)lHi);
                            vp = GlobalAllocLock16(GMEM_DDESHARE, cb, &h16);
                            if (vp) {
                                GETMISCPTR(vp, lpMem16);
                                lpMem32 = GlobalLock((HANDLE)lHi);
                                RtlCopyMemory(lpMem16, lpMem32, cb);
                                GlobalUnlock((HANDLE)lHi);
                                GlobalUnlock16(h16);
                                FREEMISCPTR(lpMem16);

                                DdeInfo.Msg = WM_DDE_EXECUTE;
                                DdeInfo.Format = 0;
                                DdeInfo.Flags = DDE_PACKET;
                                DdeInfo.h16 = 0;

                                DDEAddhandle(
                                    lpwm32mpex->Parm16.WndProc.wParam,
                                    GETHWND16(lpwm32mpex->hwnd),
                                    h16,
                                    (HANDLE)lHi,
                                    &DdeInfo
                                    );

                                pDdeNode = DDEFindAckNode (
                                           (HAND16)lpwm32mpex->Parm16.WndProc.wParam,
                                           (HAND16)GETHWND16(lpwm32mpex->hwnd),
                                           (HANDLE) lHi);

                                WOW32ASSERTMSG(pDdeNode, "WM32DDEAck: Can't find just-added DDE node.\n");
                            }
                        }

                        lpwm32mpex->Parm16.WndProc.lParam = MAKELONG(LOWORD(lLo), h16);

                    } else {

                        //
                        // All other acks have same form: status in loword and
                        // item atom in hiword.
                        //

                        lpwm32mpex->Parm16.WndProc.lParam = MAKELONG(LOWORD(lLo), lHi);
                    }

                }

                if (pDdeNode && pDdeNode->DdeMsg == WM_DDE_EXECUTE) {

                    HIW(lpwm32mpex->Parm16.WndProc.lParam) = pDdeNode->hMem16;

                    if (lpwm32mpex->fFree) {
                        if (lHi) {
                            if (pDdeNode->DdeFlags & DDE_EXECUTE_FREE_MEM) {
                                LOGDEBUG (12, ("WOW::W32DDEAck : Freeing EXECUTE pair h16 = %04x, h32 = %08x\n",
                                                                    pDdeNode->hMem16, lHi));
                                W32UnMarkDDEHandle (pDdeNode->hMem16);
                                GlobalUnlockFree16(GlobalLock16(pDdeNode->hMem16, NULL));
                                if (DDEDeletehandle(pDdeNode->hMem16, (HANDLE) lHi)) {
                                    WOWGLOBALFREE((HANDLE)lHi);
                                }
                                else {
                                    LOGDEBUG (0, ("WOW::DDE Ack : Ack can't find 16 - 32 aliasing :  %04x, %04x, %04x, %08lx, %08lx\n",
                                                  lpwm32mpex->hwnd,
                                                  lpwm32mpex->uMsg,
                                                  lpwm32mpex->Parm16.WndProc.wParam,
                                                  lpwm32mpex->Parm16.WndProc.lParam,
                                                  lHi
                                                  ));
                                }
                            }
                            else {
                                if (pDdeNode->DdeFlags & DDE_EXECUTE_FREE_H16) {
                                    W32UnMarkDDEHandle (pDdeNode->hMem16);
                                    GlobalUnlockFree16(GlobalLock16(pDdeNode->hMem16, NULL));

                                    HIW(lpwm32mpex->Parm16.WndProc.lParam) = pDdeNode->h16;
                                }

                                if (DDEDeletehandle(pDdeNode->hMem16, (HANDLE) lHi)) {
                                    WOWGLOBALFREE((HANDLE)lHi);
                                }
                                else {
                                    LOGDEBUG (0, ("WOW::DDE Ack : Ack can't find 16 - 32 aliasing :  %04x, %04x, %04x, %08lx, %08lx\n",
                                                  lpwm32mpex->hwnd,
                                                  lpwm32mpex->uMsg,
                                                  lpwm32mpex->Parm16.WndProc.wParam,
                                                  lpwm32mpex->Parm16.WndProc.lParam,
                                                  lHi
                                                  ));
                                }


                            }
                        }
                        else {
                            LOGDEBUG (2, ("WOW::W32DDEAck : h32 is NULL \n"));
                            WOW32ASSERT (FALSE);
                        }
                    }
                }
                else {
                    LOGDEBUG (2, ("WOW::DDE Ack : Ack received unexpectedly :  %x, %04x, %04x, %08lx, %08lx\n", lpwm32mpex->hwnd, lpwm32mpex->uMsg, lpwm32mpex->Parm16.WndProc.wParam, lpwm32mpex->Parm16.WndProc.lParam, lHi));
                }
            }

            LOW(lpwm32mpex->Parm16.WndProc.lParam) = (WORD) lLo;

            if (fThunkDDEmsg) {
                FreeDDElParam(lpwm32mpex->uMsg, lpwm32mpex->lParam);
            }

            LOGDEBUG (12, ("WOW::DDE Ack : %04x, %04x, %04x, %08lx, %08lx\n", lpwm32mpex->hwnd, lpwm32mpex->uMsg, lpwm32mpex->Parm16.WndProc.wParam, lpwm32mpex->Parm16.WndProc.lParam, lHi));
        }
    }
    else {
        //
        // We will execute this scenario only if the app ate the message,
        // because we need to free up the memory.
        //

        if (!fThunkDDEmsg) {
            if (lpwm32mpex->lReturn) {
                FreeDDElParam(lpwm32mpex->uMsg, lpwm32mpex->lParam);
            }
        }
    }

    return (TRUE);
}


// This function thunks the messages,
//
//  WM_DDEREQUEST
//  WM_DDETERMINATE
//  WM_DDEUNADVISE
//

BOOL FASTCALL WM32DDERequest(LPWM32MSGPARAMEX lpwm32mpex)
{

    if (lpwm32mpex->fThunk) {
        lpwm32mpex->Parm16.WndProc.wParam = GETHWND16(lpwm32mpex->uParam);
    }

    return (TRUE);
}


// This function thunks the messages,
//
//  WM_DDEADVISE
//

BOOL FASTCALL WM32DDEAdvise(LPWM32MSGPARAMEX lpwm32mpex)
{
    HAND16      h16;
    VPVOID      vp;
    LPBYTE      lpMem16;
    LPBYTE      lpMem32;
    UINT        lLo = 0;
    UINT        lHi = 0;
    DDEINFO     DdeInfo;


    if (lpwm32mpex->fThunk) {
        UnpackDDElParam(lpwm32mpex->uMsg, lpwm32mpex->lParam, &lLo, &lHi);
        lpwm32mpex->Parm16.WndProc.wParam = GETHWND16(lpwm32mpex->uParam);

        if (h16 = DDEFindPair16((HAND16)GETHWND16(lpwm32mpex->hwnd),
                                (HAND16)lpwm32mpex->Parm16.WndProc.wParam,
                                (HANDLE) lLo)) {
            LOW(lpwm32mpex->Parm16.WndProc.lParam) = h16;
        } else {
            vp = GlobalAllocLock16(GMEM_DDESHARE, sizeof(DDEADVISE), &h16);
            if (vp) {
                GETMISCPTR(vp, lpMem16);
                lpMem32 = GlobalLock((HANDLE) lLo);
                RtlCopyMemory(lpMem16, lpMem32, sizeof(DDEADVISE));
                GlobalUnlock((HANDLE) lLo);
                GlobalUnlock16(h16);
                DdeInfo.Msg = LOW(lpwm32mpex->uMsg);
                DdeInfo.Format = 0;
                DdeInfo.Flags = DDE_PACKET;
                DdeInfo.h16 = 0;
                DDEAddhandle((HAND16)GETHWND16(lpwm32mpex->hwnd),
                             (HAND16)lpwm32mpex->Parm16.WndProc.wParam,
                             h16,
                             (HANDLE) lLo,
                             &DdeInfo);
                LOW(lpwm32mpex->Parm16.WndProc.lParam) = h16;
            }
        }

        HIW(lpwm32mpex->Parm16.WndProc.lParam) = (WORD) lHi;

        if (fThunkDDEmsg) {
            FreeDDElParam(lpwm32mpex->uMsg, lpwm32mpex->lParam);
        }
    }
    else {
        //
        // We will execute this scenario only if the app ate the message,
        // because we need to free up the memory.
        //

        if (!fThunkDDEmsg) {
            if (lpwm32mpex->lReturn) {
                FreeDDElParam(lpwm32mpex->uMsg, lpwm32mpex->lParam);
            }
        }
    }

    return (TRUE);
}


// This function thunks the messages,
//
//  WM_DDEDATA
//

BOOL FASTCALL WM32DDEData(LPWM32MSGPARAMEX lpwm32mpex)
{
    HAND16  h16;
    UINT    lLo = 0;
    UINT    lHi = 0;
    DDEINFO DdeInfo;


    if (lpwm32mpex->fThunk) {
        UnpackDDElParam(lpwm32mpex->uMsg, lpwm32mpex->lParam, &lLo, &lHi);
        lpwm32mpex->Parm16.WndProc.wParam = GETHWND16(lpwm32mpex->uParam);

        if (!lLo) {
            LOW(lpwm32mpex->Parm16.WndProc.lParam) = 0;
        } else if (h16 = DDEFindPair16((HAND16)GETHWND16(lpwm32mpex->hwnd),
                                       (HAND16)lpwm32mpex->Parm16.WndProc.wParam,
                                       (HANDLE) lLo)) {
            LOW(lpwm32mpex->Parm16.WndProc.lParam) = h16;
        } else {
            DdeInfo.Msg = LOW(lpwm32mpex->uMsg);
            h16 = DDECopyhData16((HAND16)GETHWND16(lpwm32mpex->hwnd),
                                 (HAND16)lpwm32mpex->Parm16.WndProc.wParam,
                                 (HANDLE) lLo,
                                 &DdeInfo);

            //
            // If we could not allocate 16 bit memory, then return NULL to the
            // caller.
            //

            if (!h16) {
                if (fThunkDDEmsg) {
                    FreeDDElParam(lpwm32mpex->uMsg, lpwm32mpex->lParam);
                }

                lpwm32mpex->Parm16.WndProc.wParam = (WORD) lHi;
                lpwm32mpex->Parm16.WndProc.lParam = lLo;
                return (0);
            }


            DdeInfo.Flags = DDE_PACKET;
            DdeInfo.h16 = 0;
            DDEAddhandle((HAND16)GETHWND16(lpwm32mpex->hwnd),
                         (HAND16)lpwm32mpex->Parm16.WndProc.wParam,
                         h16,
                         (HANDLE) lLo,
                         &DdeInfo);

            LOW(lpwm32mpex->Parm16.WndProc.lParam) = h16;
        }

        HIW(lpwm32mpex->Parm16.WndProc.lParam) = (WORD) lHi;

        if (fThunkDDEmsg) {
            FreeDDElParam(lpwm32mpex->uMsg, lpwm32mpex->lParam);
        }


    }
    else {

        //
        // We will execute this scenario only if the app ate the message,
        // because we need to free up the memory.
        //

        if (!fThunkDDEmsg) {
            if (lpwm32mpex->lReturn) {
                FreeDDElParam(lpwm32mpex->uMsg, lpwm32mpex->lParam);
            }
        }
    }

    return (TRUE);
}


// This function thunks the messages,
//
//  WM_POKE
//

BOOL FASTCALL WM32DDEPoke(LPWM32MSGPARAMEX lpwm32mpex)
{

    HAND16  h16;
    UINT    lLo = 0;
    UINT    lHi = 0;
    DDEINFO DdeInfo;


    if (lpwm32mpex->fThunk) {
        UnpackDDElParam(lpwm32mpex->uMsg, lpwm32mpex->lParam, &lLo, &lHi);
        lpwm32mpex->Parm16.WndProc.wParam = GETHWND16(lpwm32mpex->uParam);

        // sudeepb 03-Apr-1996
        // House Design Gold Edition sends a DDE_POKE message with lParam
        // being 0. We are suppose to thunk this message with lParam being
        // zero. Without this check, the below code will fail this call
        // and the message will not be thunked to the app.

        if (lLo == 0) {
            LOW(lpwm32mpex->Parm16.WndProc.lParam) = 0;
            HIW(lpwm32mpex->Parm16.WndProc.lParam) = (WORD) lHi;
            return (TRUE);
        }

        if (h16 = DDEFindPair16((HAND16)GETHWND16(lpwm32mpex->hwnd),
                                (HAND16)lpwm32mpex->Parm16.WndProc.wParam,
                                (HANDLE) lLo)) {
            LOW(lpwm32mpex->Parm16.WndProc.lParam) = h16;
        } else {
            DdeInfo.Msg = LOW(lpwm32mpex->uMsg);
            h16 = DDECopyhData16((HAND16)GETHWND16(lpwm32mpex->hwnd),
                                 (HAND16)lpwm32mpex->Parm16.WndProc.wParam,
                                 (HANDLE) lLo,
                                 &DdeInfo);


            //
            // If we could not allocate 16 bit memory, then return NULL to the
            // caller.
            //

            if (!h16) {
                if (fThunkDDEmsg) {
                    FreeDDElParam(lpwm32mpex->uMsg, lpwm32mpex->lParam);
                }

                lpwm32mpex->Parm16.WndProc.lParam = lLo;
                return (0);
            }

            DdeInfo.Flags = DDE_PACKET;
            DdeInfo.h16 = 0;
            DDEAddhandle((HAND16)GETHWND16(lpwm32mpex->hwnd),
                         (HAND16)lpwm32mpex->Parm16.WndProc.wParam,
                         h16,
                         (HANDLE) lLo,
                         &DdeInfo);

            LOW(lpwm32mpex->Parm16.WndProc.lParam) = h16;
        }

        HIW(lpwm32mpex->Parm16.WndProc.lParam) = (WORD) lHi;

        if (fThunkDDEmsg) {
            FreeDDElParam(lpwm32mpex->uMsg, lpwm32mpex->lParam);
        }
    }
    else {
        //
        // We will execute this scenario only if the app ate the message,
        // because we need to free up the memory.
        //

        if (!fThunkDDEmsg) {
            if (lpwm32mpex->lReturn) {
                FreeDDElParam(lpwm32mpex->uMsg, lpwm32mpex->lParam);
            }
        }
    }

    return (TRUE);
}


// This function thunks the messages,
//
//  WM_DDE_EXECUTE
//

BOOL FASTCALL WM32DDEExecute(LPWM32MSGPARAMEX lpwm32mpex)
{

    HAND16  h16;
    VPVOID  vp;
    LPBYTE  lpMem16;
    LPBYTE  lpMem32;
    DDEINFO DdeInfo;


    if (lpwm32mpex->fThunk) {
        lpwm32mpex->Parm16.WndProc.wParam = GETHWND16(lpwm32mpex->uParam);

        if (h16 = DDEFindPair16((HAND16)GETHWND16(lpwm32mpex->hwnd),
                                (HAND16)lpwm32mpex->Parm16.WndProc.wParam,
                                (HANDLE) lpwm32mpex->lParam)) {
            HIW(lpwm32mpex->Parm16.WndProc.lParam) = h16;
        } else {
            vp = GlobalAllocLock16(GMEM_DDESHARE, GlobalSize((HANDLE) lpwm32mpex->lParam), &h16);
            if (vp) {
                GETMISCPTR(vp, lpMem16);
                lpMem32 = GlobalLock((HANDLE) lpwm32mpex->lParam);
                RtlCopyMemory(lpMem16, lpMem32, GlobalSize((HANDLE) lpwm32mpex->lParam));
                GlobalUnlock((HANDLE) lpwm32mpex->lParam);
                GlobalUnlock16(h16);

                DdeInfo.Msg = LOW(lpwm32mpex->uMsg);
                DdeInfo.Format = 0;
                DdeInfo.Flags = DDE_PACKET;
                DdeInfo.h16 = 0;
                DDEAddhandle((HAND16)GETHWND16(lpwm32mpex->hwnd),
                             (HAND16)lpwm32mpex->Parm16.WndProc.wParam,
                             h16,
                             (HANDLE) lpwm32mpex->lParam,
                             &DdeInfo);

                HIW(lpwm32mpex->Parm16.WndProc.lParam) = h16;
            }
        }
        LOW(lpwm32mpex->Parm16.WndProc.lParam) = 0;
    }

    return (TRUE);
}


// This function thunks the messages,
//
//  WM_CTLCOLORMSGBOX
//  WM_CTLCOLOREDIT
//  WM_CTLCOLORLISTBOX
//  WM_CTLCOLORBTN
//  WM_CTLCOLORDLG
//  WM_CTLCOLORSCROLLBAR
//  WM_CTLCOLORSTATIC
//
// into WM_CTLCOLOR and the high word of lParam specifies the
// control type.
//

BOOL FASTCALL WM32CtlColor(LPWM32MSGPARAMEX lpwm32mpex)
{

    if (lpwm32mpex->fThunk) {
        lpwm32mpex->Parm16.WndProc.wMsg = WM_CTLCOLOR;
        if(lpwm32mpex->uMsg != WM_CTLCOLOR) {  // see 16-bit thunk for this special case
            lpwm32mpex->Parm16.WndProc.wParam = GETHDC16(lpwm32mpex->uParam);
            LOW(lpwm32mpex->Parm16.WndProc.lParam) = GETHWND16(lpwm32mpex->lParam);
            HIW(lpwm32mpex->Parm16.WndProc.lParam) = (WORD) (lpwm32mpex->uMsg - WM_CTLCOLORMSGBOX);
        }
    }
    else {
        if ((ULONG)lpwm32mpex->lReturn > COLOR_ENDCOLORS) {
            lpwm32mpex->lReturn = (LONG) HBRUSH32(lpwm32mpex->lReturn);
        }
    }

    return (TRUE);
}


// This function thunks the messages,
//
//  WM_GETFONT
//

BOOL FASTCALL WM32GetFont(LPWM32MSGPARAMEX lpwm32mpex)
{

    if (!lpwm32mpex->fThunk) {
        lpwm32mpex->lReturn = (LONG)HFONT32(lpwm32mpex->lReturn);
    }

    return (TRUE);
}


// This function thunks the messages,
//
// WM_NEXTMENU
//
//           Win16          NT
//  wParam   VK_KEY         VK_KEY
//  lParam.l hmenu          PMDINEXTMENU
//  lParam.h 0
//  return.l menu           BOOL
//  return.h window
//


BOOL FASTCALL WM32NextMenu(LPWM32MSGPARAMEX lpwm32mpex)
{

    if (lpwm32mpex->fThunk) {
        if (lpwm32mpex->lParam) {
            LOW(lpwm32mpex->Parm16.WndProc.lParam) = GETHMENU16(((PMDINEXTMENU)lpwm32mpex->lParam)->hmenuIn);
            HIW(lpwm32mpex->Parm16.WndProc.lParam) = 0;
        }
    } else {
        if (lpwm32mpex->lParam) {
            ((PMDINEXTMENU)lpwm32mpex->lParam)->hmenuNext = HMENU32(LOWORD(lpwm32mpex->lReturn));
            ((PMDINEXTMENU)lpwm32mpex->lParam)->hwndNext = HWND32(HIWORD(lpwm32mpex->lReturn));
            lpwm32mpex->lReturn = TRUE;
        } else {
            lpwm32mpex->lReturn = FALSE;
        }
    }

    return (TRUE);
}


BOOL FASTCALL WM32Destroy (LPWM32MSGPARAMEX lpwm32mpex)
{

    if (!lpwm32mpex->fThunk) {
        if (CACHENOTEMPTY()) {
            // because of our method of window aliasing, 'hwnd' may or may
            // not be a real 32bit handle. ie. it may be (hwnd16 | 0xffff0000).
            // So always use hwnd16.

            ReleaseCachedDCs((CURRENTPTD())->htask16, GETHWND16(lpwm32mpex->hwnd), 0,
                               (HWND)0, SRCHDC_TASK16_HWND16);
        }
    }
    return (TRUE);
}






// This function thunks the messages,
//  WM_DROPFILES

BOOL FASTCALL WM32DropFiles(LPWM32MSGPARAMEX lpwm32mpex)
{
    if (lpwm32mpex->fThunk) {
        return (BOOL)(lpwm32mpex->Parm16.WndProc.wParam = GETHDROP16(lpwm32mpex->uParam));
    }

    return (TRUE);
}






// This function thunks the messages,
//  WM_PRINT
//  WM_PRINTCLIENT

BOOL FASTCALL WM32PrintClient(LPWM32MSGPARAMEX lpwm32mpex)
{
    if (lpwm32mpex->fThunk) {
        return (BOOL)(lpwm32mpex->Parm16.WndProc.wParam = GETHDC16(lpwm32mpex->uParam));
    }

    return (TRUE);
}




// This function thunks the messages,
//
//  WM_DROPOBJECT
//  WM_QUERYDROPOBJECT
//  WM_DRAGLOOP
//  WM_DRAGSELECT
//  WM_DRAGMOVE
//

BOOL FASTCALL WM32DropObject(LPWM32MSGPARAMEX lpwm32mpex)
{
    register PDROPSTRUCT16 pds16;
    register LPDROPSTRUCT  lParam = (LPDROPSTRUCT)lpwm32mpex->lParam;

    if (lpwm32mpex->fThunk) {

        lpwm32mpex->Parm16.WndProc.wParam = (WORD)lpwm32mpex->uParam;

        // BUGBUG -- The assumption here is that GlobalAlloc will never
        // return a memory object that isn't word-aligned, so that we can
        // assign word-aligned words directly;  we have no idea whether the
        // memory is dword-aligned or not however, so dwords must always
        // be paranoidly stored with the STOREDWORD/STORELONG macros -JTP

        if (!(lpwm32mpex->Parm16.WndProc.lParam = malloc16(sizeof(DROPSTRUCT16))))
            return FALSE;

        GETVDMPTR(lpwm32mpex->Parm16.WndProc.lParam, sizeof(DROPSTRUCT16), pds16);

        pds16->hwndSource = GETHWND16(lParam->hwndSource);
        pds16->hwndSink   = GETHWND16(lParam->hwndSink);
        pds16->wFmt       = (WORD) lParam->wFmt;
        STOREDWORD(pds16->dwData, lParam->dwData);

        pds16->ptDrop.x = (SHORT)lParam->ptDrop.x;
        pds16->ptDrop.y = (SHORT)lParam->ptDrop.y;
        STOREDWORD(pds16->dwControlData, lParam->dwControlData);

        FLUSHVDMPTR(lpwm32mpex->Parm16.WndProc.lParam, sizeof(DROPSTRUCT16), pds16);
        FREEVDMPTR(pds16);

    } else {

        free16((VPVOID) lpwm32mpex->Parm16.WndProc.lParam);

        if (lpwm32mpex->uMsg == WM_QUERYDROPOBJECT) {

            //
            // Return value is either TRUE, FALSE,
            // or a cursor!
            //
            if (lpwm32mpex->lReturn && lpwm32mpex->lReturn != (LONG)TRUE) {
                lpwm32mpex->lReturn = (LONG)HCURSOR32(lpwm32mpex->lReturn);
            }
        }
    }

    return (TRUE);
}


// This function thunks the messages,
//
//  WM_WINDOWPOSCHANGING
//  WM_WINDOWPOSCHANGED
//

BOOL FASTCALL WM32WindowPosChanging (LPWM32MSGPARAMEX lpwm32mpex)
{
    LPWINDOWPOS lParam = (LPWINDOWPOS) lpwm32mpex->lParam;


    if (lpwm32mpex->fThunk) {

        // be sure allocation size matches stackfree16() size below
        lpwm32mpex->Parm16.WndProc.lParam = stackalloc16(sizeof(WINDOWPOS16));

        putwindowpos16( (VPWINDOWPOS16)lpwm32mpex->Parm16.WndProc.lParam, lParam );

    }
    else {
        getwindowpos16( (VPWINDOWPOS16)lpwm32mpex->Parm16.WndProc.lParam, lParam );
        if(lpwm32mpex->Parm16.WndProc.lParam) {
            stackfree16((VPVOID) lpwm32mpex->Parm16.WndProc.lParam,
                        sizeof(WINDOWPOS16));
        }
    }

    return (TRUE);
}

// This function thunks the message,
//
// WM_COPYDATA
//

BOOL FASTCALL WM32CopyData (LPWM32MSGPARAMEX lpwm32mpex)
{

    HAND16  h16;
    HAND16  hMem16;
    VPVOID  vpCDS16;
    VPVOID  vpData16;
    LPBYTE  lpMem16;
    PCOPYDATASTRUCT lpCDS32;
    PCOPYDATASTRUCT lpCDS16;
    PCPDATA pTemp;


    if (lpwm32mpex->fThunk) {
        lpwm32mpex->Parm16.WndProc.wParam = GETHWND16(lpwm32mpex->uParam);

        if (vpCDS16 = CopyDataFindData16 (GETHWND16(lpwm32mpex->hwnd), lpwm32mpex->Parm16.WndProc.wParam, lpwm32mpex->lParam)) {
            lpwm32mpex->Parm16.WndProc.lParam = vpCDS16;
        }
        else {
            vpCDS16 = GlobalAllocLock16(GMEM_DDESHARE, sizeof(COPYDATASTRUCT), &h16);
            if (vpCDS16) {
                GETMISCPTR(vpCDS16, lpCDS16);
                lpCDS32 = (PCOPYDATASTRUCT) lpwm32mpex->lParam;
                lpCDS16->dwData = lpCDS32->dwData;
                if (lpCDS16->cbData = lpCDS32->cbData) {

                    FREEMISCPTR(lpCDS16);

                    vpData16 = GlobalAllocLock16(GMEM_DDESHARE, lpCDS32->cbData, &hMem16);
                    GETMISCPTR(vpData16, lpMem16);
                    if (lpMem16 && lpCDS32->lpData) {
                        RtlCopyMemory(lpMem16, lpCDS32->lpData, lpCDS32->cbData);
                        CopyDataAddNode (GETHWND16(lpwm32mpex->hwnd), lpwm32mpex->Parm16.WndProc.wParam, vpData16, (DWORD) lpCDS32->lpData, 0);
                    }
                    FREEMISCPTR(lpMem16);

                    GETMISCPTR(vpCDS16, lpCDS16);
                    lpCDS16->lpData = (PVOID) vpData16;
                }
                else {
                    lpCDS16->lpData = NULL;
                }
                FREEMISCPTR(lpCDS16);
            }

            lpwm32mpex->Parm16.WndProc.lParam = vpCDS16;
            CopyDataAddNode (GETHWND16(lpwm32mpex->hwnd), lpwm32mpex->Parm16.WndProc.wParam, vpCDS16, lpwm32mpex->lParam, 0);
        }
    }
    else {
        if (lpwm32mpex->fFree) {
            pTemp = CopyDataFindData32 (GETHWND16(lpwm32mpex->hwnd), GETHWND16(lpwm32mpex->uParam), lpwm32mpex->Parm16.WndProc.lParam);
            if (pTemp && (!(pTemp->Flags))) {
                GETMISCPTR(lpwm32mpex->Parm16.WndProc.lParam, lpCDS16);
                GlobalUnlockFree16 ((VPVOID)lpCDS16->lpData);
                CopyDataDeleteNode (GETHWND16(lpwm32mpex->hwnd), lpwm32mpex->Parm16.WndProc.wParam, (DWORD) ((PCOPYDATASTRUCT)lpwm32mpex->lParam)->lpData);
                GlobalUnlockFree16 ((VPVOID)lpwm32mpex->Parm16.WndProc.lParam);
                CopyDataDeleteNode (GETHWND16(lpwm32mpex->hwnd), lpwm32mpex->Parm16.WndProc.wParam, (DWORD) lpwm32mpex->lParam);
                FREEMISCPTR(lpCDS16);
            }
        }
    }

    return (TRUE);
}

// This function thunks the message,
//
// WM_WINHELP
//

BOOL FASTCALL WM32WinHelp (LPWM32MSGPARAMEX lpwm32mpex)
{
    static WORD msgWinHelp = 0;
    if (lpwm32mpex->fThunk) {
        lpwm32mpex->Parm16.WndProc.wMsg   = msgWinHelp ? msgWinHelp : (msgWinHelp = (WORD)RegisterWindowMessage("WM_WINHELP"));
        lpwm32mpex->Parm16.WndProc.wParam = GETHWND16(lpwm32mpex->uParam);
        if (lpwm32mpex->lParam) {
            // lpwm32mpex->lParam is LPHLP - however we need only the firstword,ie the size of data

            HAND16  hMem16;
            VPVOID  vp;
            LPBYTE  lpT;
            WORD cb;

            cb = ((LPHLP)lpwm32mpex->lParam)->cbData;
            if (vp = GlobalAllocLock16(GMEM_DDESHARE | GMEM_MOVEABLE, cb, &hMem16)) {
                GETMISCPTR(vp, lpT);
                RtlCopyMemory(lpT, (PVOID)lpwm32mpex->lParam, cb);
                FREEMISCPTR(lpT);
            }
            lpwm32mpex->Parm16.WndProc.lParam = hMem16;
            lpwm32mpex->dwParam = vp;
        }
    }
    else {
        // Make sure WinHelp is in the foreground
        SetForegroundWindow(lpwm32mpex->hwnd);
        if (lpwm32mpex->Parm16.WndProc.lParam) {
            GlobalUnlockFree16((VPVOID)lpwm32mpex->dwParam);
        }
    }

    return (TRUE);
}

//
// Thunk the undocumented MM_CALCSCROLL MDI message. Message has no parameters,
// but has different message values; 32-bit msg: 0x3F, 16-bit msg: 0x10AC.
//
BOOL FASTCALL WM32MMCalcScroll (LPWM32MSGPARAMEX lpwm32mpex)
{
    if ( lpwm32mpex->fThunk ) {
        lpwm32mpex->Parm16.WndProc.wMsg = (WORD) WIN31_MM_CALCSCROLL;
    }

    return (TRUE);
}

// Calculate the size of the structure passed with WM_NOTIFY based
// on the code field in NMHDR.
// NOTE: Do NOT rely on the documentation for the size of the struct passed in
//       lParam.  In some cases the struct is actually part of a larger struct
//       and we need to copy all of it. See PSN_xxxx codes in comctl32\prsht.c
//       They are documented to be NMHDR but are really a PSHNOTIFY which has
//       NMHDR as the first field.  Also watch for some of the WIDE UNICODE
//       char cases -- the struct may or may not be the same size as the ANSI
//       version of the struct.
// Heaven help us when they start adding more of these codes!


UINT GetNMHDRextensionSize(LPNMHDR pnmhdr32)
{
#ifdef DEBUG
    char  szLabel[40] = "    WOW:WM_NOTIFY code: ";
#endif


    // Caller already checked against NM_LAST.
    if (pnmhdr32->code >= LVN_LAST) {
        LOGDEBUG(2,("%sLVN_ %x\n", szLabel, pnmhdr32->code));
        switch (pnmhdr32->code) {

            case LVN_ITEMCHANGING:
            case LVN_ITEMCHANGED:
            case LVN_INSERTITEM:
            case LVN_DELETEITEM:
            case LVN_DELETEALLITEMS:
            case LVN_COLUMNCLICK:
            case LVN_BEGINDRAG:
            case LVN_BEGINRDRAG:
            case LVN_HOTTRACK:
                return sizeof(NM_LISTVIEW);

            case LVN_BEGINLABELEDITA:
            case LVN_ENDLABELEDITA:
            case LVN_GETDISPINFOA:
            case LVN_SETDISPINFOA:
            case LVN_BEGINLABELEDITW:
            case LVN_ENDLABELEDITW:
            case LVN_GETDISPINFOW:
            case LVN_SETDISPINFOW:
                return sizeof(LV_DISPINFO);

            case LVN_KEYDOWN:
                return sizeof(LV_KEYDOWN);

            case LVN_ODCACHEHINT:
                return sizeof(NM_CACHEHINT);

            case LVN_ODFINDITEMA:
            case LVN_ODFINDITEMW:
                return sizeof(NM_FINDITEM);

            case LVN_ODSTATECHANGED:
                return sizeof(NM_ODSTATECHANGE);

            case LVN_ITEMACTIVATE:
                return sizeof(NMKEY);

            default:
                goto unknown_nmhdr_code;
        }
    }

    if (pnmhdr32->code >= PSN_LAST) {
        LOGDEBUG(2,("%sPSN_ %x\n", szLabel, pnmhdr32->code));
        switch (pnmhdr32->code) {
            case PSN_SETACTIVE:
            case PSN_KILLACTIVE:
            case PSN_APPLY:
            case PSN_RESET:
            case PSN_HELP:
            case PSN_WIZBACK:
            case PSN_WIZNEXT:
            case PSN_WIZFINISH:
            case PSN_QUERYCANCEL:
            case PSN_TRANSLATEACCELERATOR:
            case PSN_QUERYINITIALFOCUS:
            case PSN_HASHELP:          // this one "is dead" - RaymondC
                return sizeof(PSHNOTIFY);

            case PSN_GETOBJECT:
                return sizeof(NMOBJECTNOTIFY);

            case PSN_LASTCHANCEAPPLY:  // this is undocumented
                return sizeof(NMHDR);  // (in widows\inc\prshtp.h)

            default:
                goto unknown_nmhdr_code;
        }
    }

    if (pnmhdr32->code >= HDN_LAST) {
        LOGDEBUG(2,("%sHDN_ %x\n", szLabel, pnmhdr32->code));
        switch (pnmhdr32->code) {
            case HDN_ITEMCHANGINGA:
            case HDN_ITEMCHANGEDA:
            case HDN_ITEMCLICKA:
            case HDN_DIVIDERDBLCLICKA:
            case HDN_BEGINTRACKA:
            case HDN_ENDTRACKA:
            case HDN_TRACKA:
            case HDN_ITEMCHANGINGW:
            case HDN_ITEMCHANGEDW:
            case HDN_ITEMCLICKW:
            case HDN_DIVIDERDBLCLICKW:
            case HDN_BEGINTRACKW:
            case HDN_ENDTRACKW:
            case HDN_TRACKW:
            case HDN_BEGINDRAG:
            case HDN_ENDDRAG:
            case HDN_ITEMDBLCLICKA:
            case HDN_ITEMDBLCLICKW:
                return sizeof(HD_NOTIFY);

            case HDN_GETDISPINFOA:
            case HDN_GETDISPINFOW:
                return sizeof(NMHDDISPINFO);

            default:
                goto unknown_nmhdr_code;
        }
    }

    if (pnmhdr32->code >= TVN_LAST) {
        LOGDEBUG(2,("%sTVN_ %x\n", szLabel, pnmhdr32->code));
        switch (pnmhdr32->code) {
            case TVN_SELCHANGINGA:
            case TVN_SELCHANGEDA:
            case TVN_ITEMEXPANDINGA:
            case TVN_ITEMEXPANDEDA:
            case TVN_BEGINDRAGA:
            case TVN_BEGINRDRAGA:
            case TVN_DELETEITEMA:
            case TVN_SELCHANGINGW:
            case TVN_SELCHANGEDW:
            case TVN_ITEMEXPANDINGW:
            case TVN_ITEMEXPANDEDW:
            case TVN_BEGINDRAGW:
            case TVN_BEGINRDRAGW:
            case TVN_DELETEITEMW:
            case TVN_SINGLEEXPAND:
                return sizeof(NM_TREEVIEW);

            case TVN_GETDISPINFOA:
            case TVN_SETDISPINFOA:
            case TVN_BEGINLABELEDITA:
            case TVN_ENDLABELEDITA:
            case TVN_GETDISPINFOW:
            case TVN_SETDISPINFOW:
            case TVN_BEGINLABELEDITW:
            case TVN_ENDLABELEDITW:
                return sizeof(TV_DISPINFO);

            case TVN_KEYDOWN:
                return sizeof(TV_KEYDOWN);

            case TVN_GETINFOTIPA:
            case TVN_GETINFOTIPW:
                return sizeof(NMTVGETINFOTIP);

            default:
                goto unknown_nmhdr_code;
        }
    }

    if (pnmhdr32->code >= TTN_LAST) {
        LOGDEBUG(2,("%sTTN_ %x\n", szLabel, pnmhdr32->code));
        switch (pnmhdr32->code) {
            case TTN_NEEDTEXTA:        // (aka TTN_GETDISPINFO)
                return sizeof(TOOLTIPTEXTA);

            case TTN_NEEDTEXTW:
                return sizeof(TOOLTIPTEXTW);

            case TTN_SHOW:
            case TTN_POP:
                return sizeof(NMHDR);

            default:
                goto unknown_nmhdr_code;
        }
    }

    if (pnmhdr32->code >= TCN_LAST) {
        LOGDEBUG(2,("%sTCN_ %x\n", szLabel, pnmhdr32->code));
        switch (pnmhdr32->code) {
            case TCN_KEYDOWN:
                return sizeof(TC_KEYDOWN);

            case TCN_SELCHANGE:
            case TCN_SELCHANGING:
                return sizeof(NMHDR);

            default:
                goto unknown_nmhdr_code;
        }
    }

    if (pnmhdr32->code >= UDN_LAST) {
        LOGDEBUG(2,("%sUDN_ %x\n", szLabel, pnmhdr32->code));
        switch (pnmhdr32->code) {
            case UDN_DELTAPOS:
                return sizeof(NM_UPDOWN);

            default:
                goto unknown_nmhdr_code;
        }
    }

unknown_nmhdr_code:
    LOGDEBUG(LOG_ALWAYS, ("WOW:GetNMHDRextensionSize unknown nmhdr->code: %d!\n", pnmhdr32->code));
    WOW32ASSERT(FALSE);
    return sizeof(NMHDR);  // the first field of most of the structs is NMHDR
}





//
// This function thunks the 32-bit message WM_NOTIFY.  This message existed
// but was undocumented in Win3.1.  Win95 thunks it by translating lParam
// from a flat to 16:16 pointer without thunking the contents.  That's tricky
// for us since on RISC we can't map random linear memory into the VDM without
// a lot of overhead. We'll use the code field in NMHDR to calculate the size
// of the passed structure.
//
BOOL FASTCALL WM32Notify(LPWM32MSGPARAMEX lpwm32mpex)
{
    LPNMHDR pnmhdr32;
    LPNMHDR pnmhdr16;

    if (lpwm32mpex->fThunk) {

        pnmhdr32 = (LPNMHDR) lpwm32mpex->Parm16.WndProc.lParam;

        // Save the original 32-bit flat pointer for unthunking.
        lpwm32mpex->dwTmp[0] = (DWORD) pnmhdr32;

        // If this 32bit message came from WOW, we have the original
        // 16:16 lparam squirrelled away. (mapped to the original 32-bit lParam)
        // Note: If the mapping is found, the ref count gets incremented.
        lpwm32mpex->Parm16.WndProc.lParam = (LONG)GetParam16(lpwm32mpex->lParam);

        // if we don't already have a 16:16 ptr for this -- create one
        // This means we are seeing this message for the 1st time -- coming
        // from the 32-bit world.
        if ( ! lpwm32mpex->Parm16.WndProc.lParam) {
            if (pnmhdr32->code >= NM_LAST) {
                lpwm32mpex->dwParam = sizeof(NMHDR);
            }
            else {
                lpwm32mpex->dwParam = GetNMHDRextensionSize(pnmhdr32);
            }

            // be sure allocation size matches stackfree16() size below
            lpwm32mpex->dwTmp[1] = (DWORD)lpwm32mpex->dwParam;
            lpwm32mpex->Parm16.WndProc.lParam = stackalloc16(lpwm32mpex->dwTmp[1]);
            GETVDMPTR(lpwm32mpex->Parm16.WndProc.lParam, lpwm32mpex->dwParam, pnmhdr16);
            CopyMemory(pnmhdr16, pnmhdr32, lpwm32mpex->dwParam);
            FREEVDMPTR(pnmhdr16);

        // else don't allocate (or free) another 16:16 ptr
        } else {

            // set to FALSE so we don't free this thing too soon
            lpwm32mpex->fFree = FALSE;
        }

    } else {

        if (lpwm32mpex->fFree) {
            GETVDMPTR(lpwm32mpex->Parm16.WndProc.lParam, lpwm32mpex->dwParam, pnmhdr16);
            // retrieve original 32-bit pointer
            pnmhdr32 = (LPNMHDR) lpwm32mpex->dwTmp[0];
            CopyMemory(pnmhdr32, pnmhdr16, lpwm32mpex->dwParam);
            FREEVDMPTR(pnmhdr16);
            if(lpwm32mpex->Parm16.WndProc.lParam) {
                stackfree16(lpwm32mpex->Parm16.WndProc.lParam, lpwm32mpex->dwTmp[1]);
            }
        } else {

            // Decrement the ref count. If the ref count goes to zero, the
            // mapping is nuked.
            DeleteParamMap(lpwm32mpex->Parm16.WndProc.lParam, PARAM_16, NULL);
        }
    }

    return TRUE;
}

// This function thunks the 32-bit message WM_NOTIFYWOW.
// uParam dictates where the notification should be dispatched.
//


BOOL FASTCALL WM32NotifyWow(LPWM32MSGPARAMEX lpwm32mpex)
{
    switch (lpwm32mpex->uParam) {
        case WMNW_UPDATEFINDREPLACE:
           if (lpwm32mpex->fThunk) {
                // Update the 16-bit FINDREPLACE struct.
                lpwm32mpex->Parm16.WndProc.lParam = WCD32UpdateFindReplaceTextAndFlags(lpwm32mpex->hwnd, lpwm32mpex->lParam);
                lpwm32mpex->Parm16.WndProc.wMsg = msgFINDREPLACE;
                return(TRUE);
            }
            break;

        default:
            LOGDEBUG(LOG_ALWAYS, ("WOW::WM32NotifyWow: Unknown dispatch parameter!\n"));
            WOW32ASSERT (FALSE);

    }

    return (FALSE);
}

//
//  In ThunkMsg16 we use the data in 32->16 message thunk table to optimize
//  thunking process based on 'WM32NoThunking'.
//
//  This is place holder for those messages which need nothunking on 32-16
//  trasitions but need some kind of thunking on 16->32 transistions.
//
//  So this marks the message as 'this message needs 16-32 thunking but
//  not 32-16 thunking'
//
//                                            - nanduri

BOOL FASTCALL WM32Thunk16To32(LPWM32MSGPARAMEX lpwm32mpex)
{
    return (TRUE);
}
#ifdef FE_IME
//
// This function thunks the messages,
//
//  WM_IME_REPORT

/*
BOOL FASTCALL WM32IMEReport (HWND hwnd, UINT uMsg, UINT uParam, LONG lParam,
          PWORD pwMsgNew, PWORD pwParamNew, PLONG plParamNew,
          PLONG plReturn, BOOL fThunk, LPWM32MSGPARAMEX lpwm32mpex)
*/
BOOL FASTCALL WM32IMEReport (LPWM32MSGPARAMEX lpwm32mpex)
{
//lpwm32mpex->hwnd
//lpwm32mpex->uMsg
//lpwm32mpex->uParam
//lpwm32mpex->lParam
//lpwm32mpex->Param16.WndProc.wMsg
//lpwm32mpex->Param16.WndProc.wParam
//lpwm32mpex->Param16.WndProc.lParam
//&lpwm32mpex->lReturn
//lpwm32mpex->fThunk
//

    INT     cb;
    INT     i;
    HMEM16  hMem16 = 0;
    LPBYTE  lpMem32 = 0;
    LPBYTE  lpMem16 = 0;
    VPVOID  vp;


    if (lpwm32mpex->fThunk) {
   if (lpwm32mpex->uParam == IR_STRING) {
       lpMem32 = GlobalLock((HANDLE)lpwm32mpex->lParam);
            if (lpMem32 == NULL)
                goto Err;

       cb = strlen( lpMem32 ) + 1;
       if (!(lpwm32mpex->Parm16.WndProc.lParam = GlobalAllocLock16(GMEM_MOVEABLE | GMEM_SHARE | GMEM_ZEROINIT, cb, &hMem16 )))
      goto Err;
       putstr16((VPSZ)lpwm32mpex->Parm16.WndProc.lParam, lpMem32, cb);
            LOW( lpwm32mpex->Parm16.WndProc.lParam ) = hMem16;
            HIW( lpwm32mpex->Parm16.WndProc.lParam ) = 0;         // must be zero
            GlobalUnlock( (HANDLE)lpwm32mpex->lParam );
            GlobalUnlock16( hMem16 );
   }
        /**** IR_STRINGEX ****/
        else if ( lpwm32mpex->uParam == IR_STRINGEX ) {
            LPSTRINGEXSTRUCT    pss32;
            PSTRINGEXSTRUCT16  pss16;

            lpMem32 = GlobalLock((HANDLE)lpwm32mpex->lParam);
            if (lpMem32 == NULL)
                goto Err;

            pss32 = (LPSTRINGEXSTRUCT)lpMem32;

            cb = pss32->dwSize;
            if ( cb >= ( 64 * K )) {
                // It's problem !
                LOGDEBUG(0,(" WOW:: WM_IME_REPORT:IR_STRINGEX data size must be less than 64K on WOW. cb = %d\n", cb ));
                /** goto Err; **/
            }
            // Shuld I pack size of this structure ?
       if (!(vp = GlobalAllocLock16(GMEM_MOVEABLE | GMEM_SHARE | GMEM_ZEROINIT, cb, &hMem16 )))
      return FALSE;

            GETVDMPTR(vp, cb, lpMem16 );
            pss16 = (PSTRINGEXSTRUCT16)lpMem16;

            STOREDWORD( pss16->dwSize, pss32->dwSize );
            STOREWORD( pss16->uDeterminePos, pss32->uDeterminePos );
            STOREWORD( pss16->uDetermineDelimPos, pss32->uDetermineDelimPos );
            STOREWORD( pss16->uYomiPos, pss32->uYomiPos );
            STOREWORD( pss16->uYomiDelimPos, pss32->uYomiDelimPos );

            if ( pss32->uDeterminePos ) {
                cb = strlen( (LPBYTE)&lpMem32[ pss32->uDeterminePos ] );
                RtlCopyMemory( &lpMem16[ pss16->uDeterminePos ],
                               &lpMem32[ pss32->uDeterminePos ], cb + 1 );
                if ( pss32->uDetermineDelimPos ) {
                    for ( i = 0; pss32->uDetermineDelimPos + i * sizeof(INT) < pss32->dwSize; i++ ) {
                        WORDOF16( lpMem16[ pss16->uDetermineDelimPos ], i ) = (WORD)
                        INTOF( lpMem32[ pss32->uDetermineDelimPos ], i );
                        if ( INTOF( lpMem32[ pss32->uDetermineDelimPos ], i ) >= cb )
                            break;
                    }
                }
            }

            if ( pss32->uYomiPos ) {
                cb = strlen( (LPBYTE)&lpMem32[ pss32->uYomiPos ] );
                RtlCopyMemory( &lpMem16[ pss16->uYomiPos ],
                               &lpMem32[ pss32->uYomiPos ], cb + 1 );
                if ( pss32->uYomiDelimPos ) {
                    for ( i = 0; pss32->uYomiDelimPos + i * sizeof(INT) < pss32->dwSize; i++ ) {
                        WORDOF16( lpMem16[ pss16->uYomiDelimPos ], i ) = (WORD)
                        INTOF( lpMem32[ pss32->uYomiDelimPos ], i );
                        if ( INTOF( lpMem32[ pss32->uYomiDelimPos ], i ) >= cb )
                            break;
                    }
                }
            }

            FLUSHVDMPTR(vp, cb, lpMem16);
            FREEVDMPTR(lpMem16);

            LOW( lpwm32mpex->Parm16.WndProc.lParam ) = hMem16;
            HIW( lpwm32mpex->Parm16.WndProc.lParam ) = 0;         // must be zero
            GlobalUnlock( (HANDLE)lpwm32mpex->lParam );
            GlobalUnlock16( hMem16 );
        }
        /**** IR_UNDETERMINE ****/
   else if (lpwm32mpex->uParam == IR_UNDETERMINE) {
            PUNDETERMINESTRUCT16  pus16;
            LPUNDETERMINESTRUCT    pus32;

            lpMem32 = GlobalLock( (HANDLE)lpwm32mpex->lParam );
            if (lpMem32 == NULL)
                goto Err;

            pus32 = (LPUNDETERMINESTRUCT)lpMem32;
            cb = pus32->dwSize;

            if ( cb >= ( 64 * K )) {
                // It's problem !
                LOGDEBUG(0,(" WOW:: WM_IME_REPORT:IR_UNDETERMINE data size must be less than 64K on WOW. cb = %d\n", cb ));
                /** goto Err; **/
            }
       if (!( vp = GlobalAllocLock16(GMEM_MOVEABLE | GMEM_SHARE | GMEM_ZEROINIT, cb, &hMem16 )))
      goto Err;

            GETVDMPTR(vp, cb, lpMem16 );
            pus16 = (PUNDETERMINESTRUCT16)lpMem16;

            STOREDWORD( pus16->dwSize,            (pus32)->dwSize );
            STOREWORD( pus16->uDefIMESize,        (pus32)->uDefIMESize );
            STOREWORD( pus16->uDefIMEPos,         (pus32)->uDefIMEPos );
            STOREWORD( pus16->uUndetTextLen,      (pus32)->uUndetTextLen );
            STOREWORD( pus16->uUndetTextPos,      (pus32)->uUndetTextPos );
            STOREWORD( pus16->uUndetAttrPos,      (pus32)->uUndetAttrPos );
            STOREWORD( pus16->uCursorPos,         (pus32)->uCursorPos );
            STOREWORD( pus16->uDeltaStart,        (pus32)->uDeltaStart );
            STOREWORD( pus16->uDetermineTextLen,  (pus32)->uDetermineTextLen );
            STOREWORD( pus16->uDetermineTextPos,  (pus32)->uDetermineTextPos );
            STOREWORD( pus16->uDetermineDelimPos, (pus32)->uDetermineDelimPos );
            STOREWORD( pus16->uYomiTextLen,       (pus32)->uYomiTextLen );
            STOREWORD( pus16->uYomiTextPos,       (pus32)->uYomiTextPos );
            STOREWORD( pus16->uYomiDelimPos,      (pus32)->uYomiDelimPos );


            // dada copy
            RtlCopyMemory( &lpMem16[ sizeof(UNDETERMINESTRUCT) ],
                      &lpMem32[ sizeof(UNDETERMINESTRUCT) ],
                      cb - sizeof( UNDETERMINESTRUCT ));

            //adjustment
            if ( pus32->uDetermineDelimPos ) {
                cb = pus32->uDetermineTextLen;
                for ( i = 0; pus32->uDetermineDelimPos + i * sizeof(INT) < pus32->dwSize; i++ ) {
                    INTOF( lpMem16[ pus16->uDetermineDelimPos ], i ) = 0;
                    WORDOF16( lpMem16[ pus16->uDetermineDelimPos ], i ) = (WORD)
                    INTOF( lpMem32[ pus32->uDetermineDelimPos ], i );
                    if ( INTOF( lpMem32[ pus32->uDetermineDelimPos ], i ) >= cb )
                        break;
                }
            }
            if ( pus32->uYomiDelimPos ) {
                cb = pus32->uYomiTextLen;
                for ( i = 0; pus32->uYomiDelimPos + i * sizeof(INT) < pus32->dwSize; i++ ) {
                    INTOF( lpMem16[ pus16->uYomiDelimPos ], i ) = 0;
                    WORDOF16( lpMem16[ pus16->uYomiDelimPos ], i ) = (WORD)
                    INTOF( lpMem32[ pus32->uYomiDelimPos ], i );
                    if ( INTOF( lpMem32[ pus32->uYomiDelimPos ], i ) >= cb )
                        break;
                }
            }

            FLUSHVDMPTR(vp, cb, lpMem16);
            FREEVDMPTR(lpMem16);

            LOW( lpwm32mpex->Parm16.WndProc.lParam ) = hMem16;
            HIW( lpwm32mpex->Parm16.WndProc.lParam ) = 0;         // must be zero
            GlobalUnlock( (HANDLE)lpwm32mpex->lParam );
            GlobalUnlock16( hMem16 );

   }
    }
    else { // fThunk
   if (lpwm32mpex->Parm16.WndProc.lParam) {
       GlobalUnlockFree16(GlobalLock16(LOW(lpwm32mpex->Parm16.WndProc.lParam), NULL));
        }
    }
    return (TRUE);

Err:
    if ( lpMem32 && lpwm32mpex->lParam )
        GlobalUnlock( (HANDLE)lpwm32mpex->lParam );
    return FALSE;

}
#endif  // FE_IME
