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


#include "precomp.h"
#pragma hdrstop


MODNAME(wumsg.c);


extern HANDLE hmodWOW32;

// SendDlgItemMessage cache
HWND  hdlgSDIMCached = NULL ;

BOOL fWhoCalled = FALSE;



// DDE bit used in GetMessage and PeekMessage

#define fAckReq 0x8000
#define fRelease 0x2000



/*++
    BOOL CallMsgFilter(<lpMsg>, <nCode>)
    LPMSG <lpMsg>;
    int <nCode>;

    The %CallMsgFilter% function passes the given message and code to the
    current message filter function. The message filter function is an
    application-specified function that examines and modifies all messages. An
    application specifies the function by using the %SetWindowsHook% function.

    <lpMsg>
        Points to an %MSG% structure that contains the message to be
        filtered.

    <nCode>
        Specifies a code used by the filter function to determine how to
        process the message.

    The return value specifies the state of message processing. It is FALSE if
    the message should be processed. It is TRUE if the message should not be
    processed further.

    The %CallMsgFilter% function is usually called by Windows to let
    applications examine and control the flow of messages during internal
    processing in menus and scroll bars or when moving or sizing a window.

    Values given for the <nCode> parameter must not conflict with any of the
    MSGF_ and HC_ values passed by Windows to the message filter function.
--*/

ULONG FASTCALL WU32CallMsgFilter(PVDMFRAME pFrame)
{
    INT   f2;
    ULONG ul;
    MSG t1;
    VPMSG16 vpf1;
    register PCALLMSGFILTER16 parg16;
    MSGPARAMEX mpex;

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

    vpf1 = (VPMSG16)(parg16->f1);
    f2   = INT32(parg16->f2);

    getmsg16(vpf1, &t1, &mpex);

    // Note: getmsg16 may have caused 16-bit memory movement
    FREEARGPTR(pFrame);
    FREEARGPTR(parg16);

    BlockWOWIdle(TRUE);

    ul = GETBOOL16(CallMsgFilter(&t1, f2));

    // Note: Call to CallMsgFilter may have caused 16-bit memory to move

    BlockWOWIdle(FALSE);

    // we need to free the struct ret'd by PackDDElParam in the getmsg16 call
    // (actually the call is made in ThunkWMMsg16() which is called by getmsg16)
    if((t1.message >= WM_DDE_FIRST) && (t1.message <= WM_DDE_LAST)) {
        if(t1.message == WM_DDE_ACK       ||
           t1.message == WM_DDE_DATA      ||
           t1.message == WM_DDE_POKE      ||
           t1.message == WM_DDE_ADVISE )            {

            // make sure this isn't in response to an initiate message
            if(!WI32DDEInitiate((HWND16) mpex.Parm16.WndProc.hwnd)) {
                FreeDDElParam(t1.message, t1.lParam);
            }
        }
    }

    FREEMSG16(vpf1, &t1);

    FREEARGPTR(parg16);
    RETURN(ul);
}







/*++
    LONG CallWindowProc(<lpPrevWndFunc>, <hwnd>, <wMsg>, <wParam>, <lParam>)
    FARPROC <lpPrevWndFunc>;
    HWND <hwnd>;
    WORD <wMsg>;
    WORD <wParam>;
    DWORD <lParam>;

    The %CallWindowProc% function passes message information to the function
    specified by the <lpPrevWndFunc> parameter. The %CallWindowProc% function is
    used for window subclassing. Normally, all windows with the same class share
    the same window function. A subclass is a window or set of windows belonging
    to the same window class whose messages are intercepted and processed by
    another function (or functions) before being passed to the window function
    of that class.

    The %SetWindowLong% function creates the subclass by changing the window
    function associated with a particular window, causing Windows to call the
    new window function instead of the previous one. Any messages not processed
    by the new window function must be passed to the previous window function by
    calling %CallWindowProc%. This allows a chain of window functions to be
    created.

    <lpPrevWndFunc>
        Is the procedure-instance address of the previous window function.

    <hwnd>
        Identifies the window that receives the message.

    <wMsg>
        Specifies the message number.

    <wParam>
        Specifies additional message-dependent information.

    <lParam>
        Specifies additional message-dependent information.

    The return value specifies the result of the message processing. The
    possible return values depend on the message sent.
--*/

ULONG FASTCALL WU32CallWindowProc(PVDMFRAME pFrame)
{
    ULONG ul;
    PARM16 Parm16;
    register PCALLWINDOWPROC16 parg16;
    WORD  f2, f3, f4;
    LONG  f5;
    DWORD Proc16;
    DWORD Proc32;
    INT  iMsgThunkClass = 0;

    ul = FALSE;
    GETARGPTR(pFrame, sizeof(CALLWINDOWPROC16), parg16);

    Proc16 = DWORD32(parg16->f1);
    f2     = parg16->f2;
    f3     = WORD32(parg16->f3);
    f4     = WORD32(parg16->f4);
    f5     = LONG32(parg16->f5);

    Proc32 = IsThunkWindowProc(Proc16, &iMsgThunkClass);

    // Note: IsThunkWindowProc may have caused 16-bit memory movement
    FREEARGPTR(pFrame);
    FREEARGPTR(parg16);

    if (Proc32) {
        HWND hwnd;
        UINT uMsgNew;
        UINT uParamNew;
        LONG lParamNew;
        MSGPARAMEX mpex;

        mpex.Parm16.WndProc.hwnd   = f2;
        mpex.Parm16.WndProc.wMsg   = f3;
        mpex.Parm16.WndProc.wParam = f4;
        mpex.Parm16.WndProc.lParam = f5;
        mpex.iMsgThunkClass = iMsgThunkClass;

        if (hwnd = ThunkMsg16(&mpex)) {

            // Note: ThunkMsg16 may have caused 16-bit memory movement
            // But: we haven't refreshed them since freeing after IsThunkWindowProc above.
            // FREEARGPTR(pFrame);
            // FREEARGPTR(parg16);

            uMsgNew = mpex.uMsg;
            uParamNew = mpex.uParam;
            lParamNew = mpex.lParam;

            //
            // see comment in IsMDIChild()
            //

            if ((uMsgNew == WM_CREATE || uMsgNew == WM_NCCREATE) && iMsgThunkClass == WOWCLASS_MDICLIENT) {
                    FinishThunkingWMCreateMDI16(lParamNew,
                             (LPCLIENTCREATESTRUCT)((LPCREATESTRUCT)lParamNew + 1));
            }

            BlockWOWIdle(TRUE);

            ul = CallWindowProc((WNDPROC)Proc32, hwnd, uMsgNew,
                                                      uParamNew, lParamNew);
            BlockWOWIdle(FALSE);

            if ((uMsgNew == WM_CREATE || uMsgNew == WM_NCCREATE) && iMsgThunkClass == WOWCLASS_MDICLIENT) {
                StartUnThunkingWMCreateMDI16(lParamNew); // does nothing
            }

            if (MSG16NEEDSTHUNKING(&mpex)) {
                mpex.lReturn = ul;
                (mpex.lpfnUnThunk16)(&mpex);
                ul = mpex.lReturn;
            }
        }
    }
    else {
        Parm16.WndProc.hwnd   = f2;
        Parm16.WndProc.wMsg   = f3;
        Parm16.WndProc.wParam = f4;
        Parm16.WndProc.lParam = f5;
        Parm16.WndProc.hInst  = (WORD)GetWindowLong(HWND32(f2), GWL_HINSTANCE);
        CallBack16(RET_WNDPROC, &Parm16, VPFN32(Proc16), (PVPVOID)&ul);
    }

    FREEARGPTR(parg16);
    RETURN(ul);
}






/*++
    LONG DefDlgProc(<hDlg>, <wMsg>, <wParam>, <lParam>)
    HWND <hDlg>;
    WORD <wMsg>;
    WORD <wParam>;
    DWORD <lParam>;

    The %DefDlgProc% function provides default processing for any Windows
    messages that a dialog box with a private window class does not process.

    All window messages that are not explicitly processed by the window function
    must be passed to the %DefDlgProc% function, not the %DefWindowProc%
    function. This ensures that all messages not handled by their private window
    procedure will be handled properly.

    <hDlg>
        Identifies the dialog box.

    <wMsg>
        Specifies the message number.

    <wParam>
        Specifies 16 bits of additional message-dependent information.

    <lParam>
        Specifies 32 bits of additional message-dependent information.

    The return value specifies the result of the message processing and depends
    on the actual message sent.

    The source code for the %DefDlgProc% function is provided on the SDK disks.

    An application creates a dialog box by calling one of the following
    functions:

    %CreateDialog%
        Creates a modeless dialog box.

    %CreateDialogIndirect%
        Creates a modeless dialog box.

    %CreateDialogIndirectParam%
        Creates a modeless dialog box and passes data to it when it is created.

    %CreateDialogParam%
        Creates a modeless dialog box and passes data to it when it is created.

    %DialogBox%
        Creates a modal dialog box.

    %DialogBoxIndirect%
        Creates a modal dialog box.

    %DialogBoxIndirectParam%
        Creates a modal dialog box and passes data to it when it is created.

    %DialogBoxParam%
        Creates a modal dialog box and passes data to it when it is created.
--*/

ULONG FASTCALL WU32DefDlgProc(PVDMFRAME pFrame)
{
    HWND hdlg;
    MSGPARAMEX mpex;
    register PDEFDLGPROC16 parg16;

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

    mpex.lReturn = 0;
    mpex.Parm16.WndProc.hwnd   = parg16->f1;
    mpex.Parm16.WndProc.wMsg   = WORD32(parg16->f2);
    mpex.Parm16.WndProc.wParam = WORD32(parg16->f3);
    mpex.Parm16.WndProc.lParam = LONG32(parg16->f4);
    mpex.iMsgThunkClass = 0;

    if (hdlg = ThunkMsg16(&mpex)) {

        // Note: ThunkMsg16 may have caused 16-bit memory movement
        FREEARGPTR(pFrame);
        FREEARGPTR(parg16);

        BlockWOWIdle(TRUE);
        mpex.lReturn = DefDlgProc(hdlg, mpex.uMsg, mpex.uParam, mpex.lParam);
        BlockWOWIdle(FALSE);

        if (MSG16NEEDSTHUNKING(&mpex)) {
            (mpex.lpfnUnThunk16)(&mpex);
        }
    }

    FREEARGPTR(parg16);
    RETURN((ULONG)mpex.lReturn);
}









/*++
    LONG DefFrameProc(<hwnd>, <hwndMDIClient>, <wMsg>, <wParam>, <lParam>)
    HWND <hwnd>;
    HWND <hwndMDIClient>;
    WORD <wMsg>;
    WORD <wParam>;
    DWORD <lParam>;

    The %DefFrameProc% function provides default processing for any Windows
    messages that the window function of a multiple document interface (MDI)
    frame window does not process. All window messages that are not explicitly
    processed by the window function must be passed to the %DefFrameProc%
    function, not the %DefWindowProc% function.

    <hwnd>
        Identifies the MDI frame window.

    <hwndMDIClient>
        Identifies the MDI client window.

    <wMsg>
        Specifies the message number.

    <wParam>
        Specifies 16 bits of additional message-dependent information.

    <lParam>
        Specifies 32 bits of additional message-dependent information.

    The return value specifies the result of the message processing and depends
    on the actual message sent. If the <hwndMDIClient> parameter is NULL, the
    return value is the same as for the %DefWindowProc% function.

    Normally, when an application's window procedure does not handle a message,
    it passes the message to the %DefWindowProc% function, which processes the
    message. MDI applications use the %fDefFrameProc% and %DefMDIChildProc%
    functions instead of %DefWindowProc% to provide default message processing.
    All messages that an application would normally pass to %DefWindowProc%
    (such as nonclient messages and WM_SETTEXT) should be passed to
    %DefFrameProc% instead. In addition to these, %DefFrameProc% also handles
    the following messages:

    WM_COMMAND
        The frame window of an MDI application receives the WM_COMMAND message
        to activate a particular MDI child window. The window ID accompanying
        this message will be the ID of the MDI child window assigned by Windows,
        starting with the first ID specified by the application when it created
        the MDI client window. This value of the first ID must not conflict with
        menu-item IDs.

    WM_MENUCHAR
        When the ^ALTHYPHEN^ key is pressed, the control menu of the active MDI
        child window will be selected.

    WM_SETFOCUS
        %DefFrameProc% passes focus on to the MDI client, which in turn passes
        the focus on to the active MDI child window.

    WM_SIZE
        If the frame window procedure passes this message to %DefFrameProc%, the
        MDI client window will be resized to fit in the new client area. If the
        frame window procedure sizes the MDI client to a different size, it
        should not pass the message to %DefWindowProc%.
--*/

ULONG FASTCALL WU32DefFrameProc(PVDMFRAME pFrame)
{
    HWND hwnd, hwnd2;

    MSGPARAMEX mpex;
    register PDEFFRAMEPROC16 parg16;

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

    mpex.lReturn = 0;
    mpex.Parm16.WndProc.hwnd   = parg16->f1;
    mpex.Parm16.WndProc.wMsg   = WORD32(parg16->f3);
    mpex.Parm16.WndProc.wParam = WORD32(parg16->f4);
    mpex.Parm16.WndProc.lParam = LONG32(parg16->f5);
    mpex.iMsgThunkClass = 0;

    hwnd2 = HWND32(parg16->f2);

    if (hwnd = ThunkMsg16(&mpex)) {

        // Note: ThunkMsg16 may have caused 16-bit memory movement
        FREEARGPTR(pFrame);
        FREEARGPTR(parg16);

        if (mpex.uMsg == WM_CLIENTSHUTDOWN &&
            CURRENTPTD()->dwWOWCompatFlagsEx & WOWCFEX_IGNORECLIENTSHUTDOWN) {

            //
            // TurboCAD picks up an uninitialized stack variable as the
            // message number to pass to DefFrameProc.  In NT 3.51 it
            // got 0x907 so the call was a NOP.  In NT 4.0, because we
            // now save FS and GS in wow16call, they pick up the x86
            // flat FS, 0x3b, which also happens to be WM_CLIENTSHUTDOWN
            // on NT and Win95, an undocumented message.  DefFrameProc
            // passes the message to DefWindowProc, which does some
            // shutdown related processing which causes TurboCAD to fault
            // soon thereafter.
            //
            // We considered renumbering WM_CLIENTSHUTDOWN, but Win95 uses
            // it as well and some apps may have reverse-engineered the
            // value 3b and depend on seeing the message.
            //
            // So instead we eat the call to DefFrameProc here under
            // a compatibility bit.
            //                            -- DaveHart 31-May-96
            //

            mpex.lReturn = 0;

        } else {

            BlockWOWIdle(TRUE);
            mpex.lReturn = DefFrameProc(hwnd, hwnd2,
                                        mpex.uMsg, mpex.uParam, mpex.lParam);
            BlockWOWIdle(FALSE);
        }

        if (MSG16NEEDSTHUNKING(&mpex)) {
            (mpex.lpfnUnThunk16)(&mpex);
        }
    }

    FREEARGPTR(parg16);
    RETURN((ULONG)mpex.lReturn);
}










/*++
    LONG DefMDIChildProc(<hwnd>, <wMsg>, <wParam>, <lParam>)
    HWND <hwnd>;
    WORD <wMsg>;
    WORD <wParam>;
    DWORD <lParam>;

    The %DefMDIChildProc% function provides default processing for any Windows
    messages that the window function of a multiple document interface (MDI)
    child window does not process. All window messages that are not explicitly
    processed by the window function must be passed to the %DefMDIChildProc%
    function, not the %DefWindowProc% function.

    <hwnd>
        Identifies the MDI child window.

    <wMsg>
        Specifies the message number.

    <wParam>
        Specifies 16 bits of additional message-dependent information.

    <lParam>
        Specifies 32 bits of additional message-dependent information.

    The return value specifies the result of the message processing and depends
    on the actual message sent.

    This function assumes that the parent of the window identified by the <hwnd>
    parameter was created with the MDICLIENT class.

    Normally, when an application's window procedure does not handle a message,
    it passes the message to the %DefWindowProc% function, which processes the
    message. MDI applications use the %DefFrameProc% and %DefMDIChildProc%
    functions instead of %DefWindowProc% to provide default message processing.
    All messages that an application would normally pass to %DefWindowProc%
    (such as nonclient messages and WM_SETTEXT) should be passed to
    %DefMDIChildProc% instead. In addition to these, %DefMDIChildProc% also
    handles the following messages:

    WM_CHILDACTIVATE
        Performs activation processing when child windows are sized, moved, or
        shown. This message must be passed.

    WM_GETMINMAXINFO
        Calculates the size of a maximized MDI child window based on the current
        size of the MDI client window.

    WM_MENUCHAR
        Sends the key to the frame window.

    WM_MOVE
        Recalculates MDI client scroll bars, if they are present.

    WM_SETFOCUS
        Activates the child window if it is not the active MDI child.

    WM_SIZE
        Performs necessary operations when changing the size of a window,
        especially when maximizing or restoring an MDI child window. Failing to
        pass this message to %DefMDIChildProc% will produce highly undesirable
        results.

    WM_SYSCOMMAND
        Also handles the next window command.
--*/

ULONG FASTCALL WU32DefMDIChildProc(PVDMFRAME pFrame)
{
    HWND hwnd;
    register PDEFMDICHILDPROC16 parg16;
    MSGPARAMEX mpex;

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

    mpex.lReturn = 0;
    mpex.Parm16.WndProc.hwnd   = parg16->f1;
    mpex.Parm16.WndProc.wMsg   = WORD32(parg16->f2);
    mpex.Parm16.WndProc.wParam = WORD32(parg16->f3);
    mpex.Parm16.WndProc.lParam = LONG32(parg16->f4);
    mpex.iMsgThunkClass = 0;

    if (hwnd = ThunkMsg16(&mpex)) {

        // Note: ThunkMsg16 may have caused 16-bit memory movement
        FREEARGPTR(pFrame);
        FREEARGPTR(parg16);

        BlockWOWIdle(TRUE);
        mpex.lReturn = DefMDIChildProc(hwnd, mpex.uMsg, mpex.uParam,
                                                                mpex.lParam);
        BlockWOWIdle(FALSE);

        if (MSG16NEEDSTHUNKING(&mpex)) {
            (mpex.lpfnUnThunk16)(&mpex);
        }
    }

    FREEARGPTR(parg16);
    RETURN((ULONG)mpex.lReturn);
}










/*++
    LONG DefWindowProc(<hwnd>, <wMsg>, <wParam>, <lParam>)
    HWND <hwnd>;
    WORD <wMsg>;
    WORD <wParam>;
    DWORD <lParam>;

    The %DefWindowProc% function calls the default window procedure. The
    default window procedure provides default processing for any window messages
    that an application does not process. This function is used to ensure that
    every message is processed. It should be called with the same parameters as
    those received by the window procedure.

    <hwnd>
        Identifies the window that received the message.

    <wMsg>
        Specifies the message.

    <wParam>
        Specifies 16 bits of additional message-dependent information.

    <lParam>
        Specifies 32 bits of additional message-dependent information.

    The return value is dependent on the message that was passed to this
    function.
--*/

ULONG FASTCALL WU32DefWindowProc(PVDMFRAME pFrame)
{
    HWND hwnd;
    register PDEFWINDOWPROC16 parg16;
    MSGPARAMEX mpex;

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

    mpex.lReturn = 0;
    mpex.Parm16.WndProc.hwnd   = parg16->hwnd;
    mpex.Parm16.WndProc.wMsg   = WORD32(parg16->wMsg);
    mpex.Parm16.WndProc.wParam = WORD32(parg16->wParam);
    mpex.Parm16.WndProc.lParam = LONG32(parg16->lParam);
    mpex.iMsgThunkClass = 0;

    if (hwnd = ThunkMsg16(&mpex)) {

        // Note: ThunkMsg16 may have caused 16-bit memory movement
        FREEARGPTR(pFrame);
        FREEARGPTR(parg16);

        BlockWOWIdle(TRUE);
        mpex.lReturn = DefWindowProc(hwnd, mpex.uMsg, mpex.uParam, mpex.lParam);
        BlockWOWIdle(FALSE);

        if (MSG16NEEDSTHUNKING(&mpex)) {
            (mpex.lpfnUnThunk16)(&mpex);
        }
    }

    FREEARGPTR(parg16);
    RETURN((ULONG)mpex.lReturn);
}










/*++
    LONG DispatchMessage(<lpMsg>)
    LPMSG <lpMsg>;

    The %DispatchMessage% function passes the message in the %MSG% structure
    pointed to by the <lpMsg> parameter to the window function of the specified
    window.

    <lpMsg>
        Points to an %MSG% structure that contains message information from
        the Windows application queue.

        The structure must contain valid message values. If <lpMsg> points to a
        WM_TIMER message and the <lParam> parameter of the WM_TIMER message is
        not NULL, then the <lParam> parameter is the address of a function that
        is called instead of the window function.

    The return value specifies the value returned by the window function. Its
    meaning depends on the message being dispatched, but generally the return
    value is ignored.
--*/

ULONG FASTCALL WU32DispatchMessage(PVDMFRAME pFrame)
{
    ULONG ul;
    WORD  wTDB;
    MSG t1;
    register PDISPATCHMESSAGE16 parg16;
    MSGPARAMEX mpex;

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

    wTDB = pFrame->wTDB;

    getmsg16(parg16->f1, &t1, &mpex);

    // Note: getmsg16 may have caused 16-bit memory movement
    FREEARGPTR(pFrame);
    FREEARGPTR(parg16);

    if (CACHENOTEMPTY() && !(CURRENTPTD()->dwWOWCompatFlags & WOWCF_DONTRELEASECACHEDDC)) {

        ReleaseCachedDCs(wTDB, 0, 0, 0, SRCHDC_TASK16);
    }

    BlockWOWIdle(TRUE);

    ul = GETLONG16(DispatchMessage(&t1));

    BlockWOWIdle(FALSE);

    // WARNING Don't rely on any 32 bit flat pointers to 16 bit memory
    // After the dispatchmessage call.

    FREEARGPTR(parg16);
    RETURN(ul);
}







/*++
    BOOL GetMessage(<lpMsg>, <hwnd>, <wMsgFilterMin>, <wMsgFilterMax>)
    LPMSG <lpMsg>;
    HWND <hwnd>;
    WORD <wMsgFilterMin>;
    WORD <wMsgFilterMax>;

    The %GetMessage% function retrieves a message from the application queue and
    places the message in the structure pointed to by the <lpMsg> parameter. If
    no message is available, the %GetMessage% function yields control to other
    applications until a message becomes available.

    %GetMessage% retrieves only messages associated with the window specified by
    the <hwnd> parameter and within the range of message values given by the
    <wMsgFilterMin> and <wMsgFilterMax> parameters. If <hwnd> is NULL,
    %GetMessage% retrieves messages for any window that belongs to the
    application making the call. (The %GetMessage% function does not retrieve
    messages for windows that belong to other applications.) If <wMsgFilterMin>
    and <wMsgFilterMax> are both zero, %GetMessage% returns all available
    messages (no filtering is performed).

    The constants WM_KEYFIRST and WM_KEYLAST can be used as filter values to
    retrieve all messages related to keyboard input; the constants WM_MOUSEFIRST
    and WM_MOUSELAST can be used to retrieve all mouse-related messages.

    <lpMsg>
        Points to an %MSG% structure that contains message information from
        the Windows application queue.

    <hwnd>
        Identifies the window whose messages are to be examined. If
        <hwnd> is NULL, %GetMessage% retrieves messages for any window that
        belongs to the application making the call.

    <wMsgFilterMin>
        Specifies the integer value of the lowest message value to be
        retrieved.

    <wMsgFilterMax>
        Specifies the integer value of the highest message value to be
        retrieved.

    The return value is TRUE if a message other than WM_QUIT is retrieved. It is
    FALSE if the WM_QUIT message is retrieved.

    The return value is usually used to decide whether to terminate the
    application's main loop and exit the program.

    In addition to yielding control to other applications when no messages are
    available, the %GetMessage% and %PeekMessage% functions also yield control
    when WM_PAINT or WM_TIMER messages for other tasks are available.

    The %GetMessage%, %PeekMessage%, and %WaitMessage% functions are the only
    ways to let other applications run. If your application does not call any of
    these functions for long periods of time, other applications cannot run.

    When %GetMessage%, %PeekMessage%, and %WaitMessage% yield control to other
    applications, the stack and data segments of the application calling the
    function may move in memory to accommodate the changing memory requirements
    of other applications. If the application has stored long pointers to
    objects in the data or stack segment (that is, global or local variables),
    these pointers can become invalid after a call to %GetMessage%,
    %PeekMessage%, or %WaitMessage%. The <lpMsg> parameter of the called
    function remains valid in any case.
--*/

ULONG FASTCALL WU32GetMessage(PVDMFRAME pFrame)
{
    ULONG ul;
    MSG t1;
    VPMSG16  vpMsg;
    register PGETMESSAGE16 parg16;
    ULONG ulReturn;

    BlockWOWIdle(TRUE);

// NOTE: pFrame needs to be restored on all GOTO's to get_next_dde_message
get_next_dde_message:

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

    vpMsg = parg16->vpMsg;


    ul = GETBOOL16(GetMessage(&t1,
                              HWND32(parg16->hwnd),
                              WORD32(parg16->wMin),
                              WORD32(parg16->wMax)));

    // There Could have been a Task Switch Before GetMessage Returned so
    // Don't Trust any 32 bit flat pointers we have, memory could've been
    // compacted or moved.
    FREEARGPTR(parg16);
    FREEVDMPTR(pFrame);


#ifdef DEBUG
    if (t1.message == WM_TIMER) {
        WOW32ASSERT(HIWORD(t1.wParam) == 0);
    }
#endif

    ulReturn = putmsg16(vpMsg, &t1);

    // NOTE: Call to putmsg16 could've caused 16-bit memory movement

    if (((t1.message == WM_DDE_DATA) || (t1.message == WM_DDE_POKE)) && (!ulReturn)) {
        register PMSG16 pmsg16;
        DDEDATA *lpMem32;
        WORD Status;
        UINT dd;
        WORD ww;
        char szMsgBoxText[1024];
        char szCaption[256];

        GETVDMPTR(vpMsg, sizeof(MSG16), pmsg16);

        dd = FETCHDWORD(pmsg16->lParam);
        ww = FETCHWORD(pmsg16->wParam);

        lpMem32 = GlobalLock((HGLOBAL)dd);
        Status = (*((PWORD) lpMem32));
        GlobalUnlock((HGLOBAL)dd);

        (pfnOut.pfnFreeDDEData)((HANDLE)dd, TRUE, TRUE);

        GlobalDeleteAtom (ww);

        if ((Status & fAckReq) || (t1.message == WM_DDE_POKE)) {
            LoadString(hmodWOW32, iszOLEMemAllocFailedFatal, szMsgBoxText, sizeof szMsgBoxText);
            LoadString(hmodWOW32, iszSystemError, szCaption, sizeof szCaption);
            MessageBox(t1.hwnd, (LPCTSTR) szMsgBoxText, szCaption, MB_OK | MB_SETFOREGROUND | MB_TOPMOST);
            PostMessage((HWND) t1.wParam, WM_DDE_TERMINATE, (WPARAM)FULLHWND32((WORD)t1.hwnd), (LPARAM)0l);
        }
        else {
            LoadString(hmodWOW32, iszOLEMemAllocFailed, szMsgBoxText, sizeof szMsgBoxText);
            LoadString(hmodWOW32, iszSystemError, szCaption, sizeof szCaption);
            MessageBox(t1.hwnd, (LPCTSTR) szMsgBoxText, szCaption, MB_OK | MB_SETFOREGROUND | MB_TOPMOST);
        }

        FREEVDMPTR(pmsg16);

        // restore the frame ptr due to possible 16-bit memory movement
        GETFRAMEPTR(((PTD)CURRENTPTD())->vpStack, pFrame);

        goto get_next_dde_message;
    }

    BlockWOWIdle(FALSE);

    FREEARGPTR(parg16);
    FREEVDMPTR(pFrame);
    RETURN(ul);
}







/*++
    DWORD GetMessagePos(VOID)

    The %GetMessagePos% function returns a long value that represents the cursor
    position (in screen coordinates) when the last message obtained by the
    %GetMessage% function occurred.

    This function has no parameters.

    The return value specifies the <x>- and <y>-coordinates of the cursor
    position. The <x>-coordinate is in the low-order word, and the
    <y>-coordinate is in the high-order word. If the return value is assigned to
    a variable, the %MAKEPOINT% macro can be used to obtain a %POINT% structure
    from the return value; the %LOWORD% or %HIWORD% macro can be used to extract
    the <x>- or the <y>-coordinate.

    To obtain the current position of the cursor instead of the position when
    the last message occurred, use the %GetCursorPos% function.
--*/

ULONG FASTCALL WU32GetMessagePos(PVDMFRAME pFrame)
{
    ULONG ul;

    UNREFERENCED_PARAMETER(pFrame);

    ul = GETDWORD16(GetMessagePos());

    RETURN(ul);
}







/*++
    DWORD GetMessageTime(VOID)

    The %GetMessageTime% function returns the message time for the last message
    retrieved by the %GetMessage% function. The time is a long integer that
    specifies the elapsed time (in milliseconds) from the time the system was
    booted to the time the message was created (placed in the application
    queue).

    This function has no parameters.

    The return value specifies the message time.

    Do not assume that the return value is always increasing. The return value
    will wrap around to zero if the timer count exceeds the maximum value for
    long integers.

    To calculate time delays between messages, subtract the time of the second
    message from the time of the first message.
--*/

ULONG FASTCALL WU32GetMessageTime(PVDMFRAME pFrame)
{
    ULONG ul;

    UNREFERENCED_PARAMETER(pFrame);

    ul = GETLONG16(GetMessageTime());

    RETURN(ul);
}







/*++
    BOOL InSendMessage(VOID)

    The %InSendMessage% function specifies whether the current window function
    is processing a message that is passed to it through a call to the
    %SendMessage% function.

    This function has no parameters.

    The return value specifies the outcome of the function. It is TRUE if the
    window function is processing a message sent to it with %SendMessage%.
    Otherwise, it is FALSE.

    Applications use the %InSendMessage% function to determine how to handle
    errors that occur when an inactive window processes messages. For example,
    if the active window uses %SendMessage% to send a request for information to
    another window, the other window cannot become active until it returns
    control from the %SendMessage% call. The only method an inactive window has
    to inform the user of an error is to create a message box.
--*/

ULONG FASTCALL WU32InSendMessage(PVDMFRAME pFrame)
{
    ULONG ul;

    UNREFERENCED_PARAMETER(pFrame);

    ul = GETBOOL16(InSendMessage());

    RETURN(ul);
}







/*++
    BOOL PeekMessage(<lpMsg>, <hwnd>, <wMsgFilterMin>, <wMsgFilterMax>,
        <wRemoveMsg>)
    LPMSG <lpMsg>;
    HWND <hwnd>;
    WORD <wMsgFilterMin>;
    WORD <wMsgFilterMax>;
    WORD <wRemoveMsg>;

    The %PeekMessage% function checks the application queue for a message and
    places the message (if any) in the structure pointed to by the <lpMsg>
    parameter. Unlike the %GetMessage% function, the %PeekMessage% function does
    not wait for a message to be placed in the queue before returning. It does,
    however, yield control (if the PM_NOYIELD flag isn't set) and does not
    return control after the yield until Windows returns control to the
    application.

    %PeekMessage% retrieves only messages associated with the window specified
    by the <hwnd> parameter, or any of its children as specified by the
    %IsChild% function, and within the range of message values given by the
    <wMsgFilterMin> and <wMsgFilterMax> parameters. If <hwnd> is NULL,
    %PeekMessage% retrieves messages for any window that belongs to the
    application making the call. (The %PeekMessage% function does not retrieve
    messages for windows that belong to other applications.) If <hwnd> is -1,
    %PeekMessage% returns only messages with a <hwnd> of NULL as posted by the
    %PostAppMessage% function. If <wMsgFilterMin> and <wMsgFilterMax> are both
    zero, %PeekMessage% returns all available messages (no range filtering is
    performed).

    The WM_KEYFIRST and WM_KEYLAST flags can be used as filter values to
    retrieve all key messages; the WM_MOUSEFIRST and WM_MOUSELAST flags can be
    used to retrieve all mouse messages.

    <lpMsg>
        Points to an %MSG% structure that contains message information from
        the Windows application queue.

    <hwnd>
        Identifies the window whose messages are to be examined.

    <wMsgFilterMin>
        Specifies the value of the lowest message position to be
        examined.

    <wMsgFilterMax>
        Specifies the value of the highest message position to be
        examined.

    <wRemoveMsg>
        Specifies a combination of the flags described in the following
        list. PM_NOYIELD can be combined with either PM_NOREMOVE or PM_REMOVE:

    PM_NOREMOVE
        Messages are not removed from the queue after processing by
        PeekMessage.

    PM_NOYIELD
        Prevents the current task from halting and yielding system resources to
        another task.

    PM_REMOVE
        Messages are removed from the queue after processing by %PeekMessage%.

    The return value specifies whether or not a message is found. It is TRUE if
    a message is available. Otherwise, it is FALSE.

    %PeekMessage% does not remove WM_PAINT messages from the queue. The messages
    remain in the queue until processed. The %GetMessage%, %PeekMessage%, and
    %WaitMessage% functions yield control to other applications. These calls are
    the only way to let other applications run. If your application does not
    call any of these functions for long periods of time, other applications
    cannot run.

    When %GetMessage%, %PeekMessage%, and %WaitMessage% yield control to other
    applications, the stack and data segments of the application calling the
    function may move in memory to accommodate the changing memory requirements
    of other applications.

    If the application has stored long pointers to objects in the data or stack
    segment (global or local variables), and if they are unlocked, these
    pointers can become invalid after a call to %GetMessage%, %PeekMessage%, or
    %WaitMessage%. The <lpMsg> parameter of the called function remains valid in
    any case.
--*/

ULONG FASTCALL WU32PeekMessage(PVDMFRAME pFrame)
{
    ULONG ul;
    VPMSG16 vpf1;
    HANDLE  f2;
    WORD    f3, f4, f5;
    MSG t1;
    register PPEEKMESSAGE16 parg16;
    BOOL fNoYield;

    BlockWOWIdle(TRUE);

// NOTE: pFrame needs to be restored on all GOTO's to get_next_dde_message
get_next_dde_message:

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

    vpf1 = parg16->f1;
    f2   = HWND32(parg16->f2);
    f3   = WORD32(parg16->f3);
    f4   = WORD32(parg16->f4);
    f5   = parg16->f5;

    fNoYield = f5 & PM_NOYIELD;

    ul = GETBOOL16(PeekMessage(&t1, f2, f3, f4, f5));

    // There could've been a task switch before peekmessage returned
    // so Don't trust any 32 bit flat pointers we have, memory could
    // have been compacted or moved.
    FREEARGPTR(parg16);
    FREEVDMPTR(pFrame);

#ifdef DEBUG
    if (ul && t1.message == WM_TIMER) {
        WOW32ASSERT(HIWORD(t1.wParam) == 0);
    }
#endif

    // If PeekMessage returned NULL don't bother to copy anything back

    if (ul) {
        ULONG ulReturn;

        //
        // We need to set/reset fThunkDDEmsg (based on PM_REMOVE flag)
        // so that we know whether to call FreeDDElParam or not while
        // thunking 32 bit message to 16 bit message.
        //

        fThunkDDEmsg = (BOOL) (f5 & PM_REMOVE);
        ulReturn = putmsg16(vpf1, &t1);

        // There Could've been a Task Switch Before putmsg16 Returned so Don't
        // Trust any 32 bit flat pointers we have, memory could have been
        // compacted or moved.
        FREEARGPTR(parg16);
        FREEVDMPTR(pFrame);

        fThunkDDEmsg = TRUE;

        if (((t1.message == WM_DDE_DATA) || (t1.message == WM_DDE_POKE)) && (!ulReturn)) {
            register PMSG16 pmsg16;
            DDEDATA *lpMem32;
            WORD Status;
            UINT dd;
            WORD ww;
            char szMsgBoxText[1024];
            char szCaption[256];

            GETVDMPTR(vpf1, sizeof(MSG16), pmsg16);

            dd = FETCHDWORD(pmsg16->lParam);
            ww = FETCHWORD(pmsg16->wParam);

            lpMem32 = GlobalLock((HGLOBAL)dd);
            Status = (*((PWORD) lpMem32));
            GlobalUnlock((HGLOBAL)dd);

            (pfnOut.pfnFreeDDEData)((HANDLE)dd, TRUE, TRUE);

            GlobalDeleteAtom (ww);

            if (!(f5 & PM_REMOVE)) {

                ul = GETBOOL16(PeekMessage(&t1, f2, f3, f4, f5 | PM_REMOVE));

                // There could've been a task switch before peekmessage returned
                // so Don't trust any 32 bit flat pointers we have, memory could
                // have been compacted or moved.
                FREEARGPTR(parg16);
                FREEVDMPTR(pFrame);
                FREEVDMPTR(pmsg16);

                // uncomment if parg16 is ref'd before goto get_next_dde_message
                //GETFRAMEPTR(((PTD)CURRENTPTD())->vpStack, pFrame);
                //GETARGPTR(pFrame, sizeof(PEEKMESSAGE16), parg16);

                // uncomment if pmsg16 is ref'd before goto get_next_dde_message
                //GETVDMPTR(vpf1, sizeof(MSG16), pmsg16);
            }

            if ((Status & fAckReq) || (t1.message == WM_DDE_POKE)) {

                LoadString(hmodWOW32, iszOLEMemAllocFailedFatal, szMsgBoxText, sizeof szMsgBoxText);
                LoadString(hmodWOW32, iszSystemError, szCaption, sizeof szCaption);
                MessageBox(t1.hwnd, (LPCTSTR) szMsgBoxText, szCaption, MB_OK);
                PostMessage ((HWND) t1.wParam, WM_DDE_TERMINATE, (WPARAM)FULLHWND32((WORD)t1.hwnd), (LPARAM)0l);
            }
            else {
                LoadString(hmodWOW32, iszOLEMemAllocFailed, szMsgBoxText, sizeof szMsgBoxText);
                LoadString(hmodWOW32, iszSystemError, szCaption, sizeof szCaption);
                MessageBox(t1.hwnd, (LPCTSTR) szMsgBoxText, szCaption, MB_OK);
            }

            FREEVDMPTR(pmsg16);

            // restore the frame ptr due to possible 16-bit memory movement
            GETFRAMEPTR(((PTD)CURRENTPTD())->vpStack, pFrame);

            goto get_next_dde_message;
        }
    }
    else if (fNoYield && (CURRENTPTD()->dwWOWCompatFlags & WOWCF_SETNULLMESSAGE)) {

        // winproj (help.tutorial) calls peekmessage with PM_REMOVE and
        // PM_NOYIELD  and an lpmsg whose contents are uninitialized. However
        // even if peekmessage returns false, it checks if lpmsg->message is
        // WM_QUIT and if true exits. In WOW by pure coincidence the
        // unintialized lpmsg->message happens to be value 0x12, which is
        // WM_QUIT and thus the tutorial always exits after initialization.
        //
        // So we reset lpmsg->message to zero, if it was called with PM_NOYIELD
        // and if it happens to be WM_QUIT and if peekmessage returns zero.
        //
        //                                                       - nanduri

        // we don't need to reinitialize pFrame etc. 'cause peekmessage was
        // called with PM_NOYIELD and thus the 16bit memory couldn't have moved

        register PMSG16 pmsg16;
        GETVDMPTR(vpf1, sizeof(MSG16), pmsg16);
        if (pmsg16 && (pmsg16->message == WM_QUIT)) {
            pmsg16->message = 0;
        }
        FREEVDMPTR(pmsg16);
    }


    BlockWOWIdle(FALSE);

    FREEARGPTR(parg16);
    FREEVDMPTR(pFrame);
    RETURN(ul);
}







/*++
    BOOL PostAppMessage(<hTask>, <wMsg>, <wParam>, <lParam>)
    HANDLE <hTask>;
    WORD <wMsg>;
    WORD <wParam>;
    DWORD <lParam>;

    The %PostAppMessage% function posts a message to an application identified
    by a task handle, and then returns without waiting for the application to
    process the message. The application receiving the message obtains the
    message by calling the %GetMessage% or %PeekMessage% function. The <hwnd>
    parameter of the returned %MSG% structure is NULL.

    <hTask>
        Identifies the task that is to receive the message. The
        %GetCurrentTask% function returns this handle.

    <wMsg>
        Specifies the type of message posted.

    <wParam>
        Specifies additional message information.

    <lParam>
        Specifies additional message information.

    The return value specifies whether or not the message is posted. It is
    TRUE if the message is posted. Otherwise, it is FALSE.
--*/

ULONG FASTCALL WU32PostAppMessage(PVDMFRAME pFrame)
{
    register PPOSTAPPMESSAGE16 parg16;
    DWORD    f1;
    MSGPARAMEX mpex;

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

    mpex.lReturn = 0;
    mpex.Parm16.WndProc.hwnd   = 0;
    mpex.Parm16.WndProc.wMsg   = WORD32(parg16->f2);
    mpex.Parm16.WndProc.wParam = WORD32(parg16->f3);
    mpex.Parm16.WndProc.lParam = LONG32(parg16->f4);
    mpex.iMsgThunkClass = 0;

    f1 = THREADID32(parg16->f1);

    ThunkMsg16(&mpex);

    // Note: ThunkMsg16 may have caused 16-bit memory movement
    FREEARGPTR(pFrame);
    FREEARGPTR(parg16);

    mpex.lReturn = PostThreadMessage(f1, mpex.uMsg, mpex.uParam, mpex.lParam);

    if (MSG16NEEDSTHUNKING(&mpex)) {
        (mpex.lpfnUnThunk16)(&mpex);
    }

    FREEARGPTR(parg16);
    RETURN((ULONG)mpex.lReturn);
}







/*++
    BOOL PostMessage(<hwnd>, <msg>, <wParam>, <lParam>)
    HWND <hwnd>;
    WORD <msg>;
    WORD <wParam>;
    LONG <lParam>;

    The %PostMessage% function places a message in a window's application queue,
    and then returns without waiting for the corresponding window to process the
    message. Messages in a message queue are retrieved by calls to the
    %GetMessage% or %PeekMessage% function.

    .*
    .* DA's: the following parameters section should be identical to the
    .*       parameters section in the sdmsg.ref file. If there is a change
    .*       to this section, the identical change should be made in the other
    .*       file.
    .*

    <hwnd>
        Identifies the window that is to receive the message. If this parameter
        is 0xFFFF (-1), the message is sent to all top-level windows.

    <msg>
        Specifies the message to be sent.

    <wParam>
        Specifies additional message information. The contents of this
        parameter depends on the message being sent.

    <lParam>
        Specifies additional message information. The contents of this
        parameter depends on the message being sent.

    The return value is TRUE if the message is posted, or FALSE if it is not.

    An application should never use the %PostMessage% function to send a message
    to a control.

    .cmt
    27-Oct-1990 [ralphw]

    The following is a rewording of the previous documentation. However, it
    needs confirmation from development as to its technical accuracy before it
    can be released for public consumption.

    If the message is being sent to another application, and the <wParam> or
    <lParam> parameters are used to pass a handle or pointer to global memory,
    the memory should be allocated by the %GlobalAlloc% function using the
    GMEM_NOT_BANKED flag. In a system using expanded memory (EMS), this ensures
    that the memory is not in in a different bank of memory from the application
    using the memory.
    .endcmt
--*/

ULONG FASTCALL WU32PostMessage(PVDMFRAME pFrame)
{
    LONG l;
    UINT f2;
    WPARAM f3;
    LPARAM f4;
    HWND hwnd;
    register PPOSTMESSAGE16 parg16;
    MSGPARAMEX mpex;
    DWORD err = NO_ERROR;

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

    // Apps should never use PostMessage to post messages that have
    // pointers to structures, because those messages will show up in
    // GetMessage, and if GetMessage tries to thunk them (ie, tries to
    // call back to the 16-bit kernel to allocate some 16-bit memory to
    // copy the converted 32-bit structure into), we have no way of
    // knowing when to free that 16-bit memory.
    //
    // BUGBUG 22-Aug-91 JeffPar:  a flag should be added to ThunkMsg16
    // indicating whether or not such allocations are permissible;  this
    // flag should be passed on to all the ThunkXXMsg16 subfunctions,
    // and each of those subfunctions should assert the flag is false
    // whenever allocating 16-bit memory.


    //
    // Used by 16->32 DDE thunkers.
    //

    WOW32ASSERT(fWhoCalled == FALSE);
    fWhoCalled = WOWDDE_POSTMESSAGE;

    f2 = (UINT)WORD32(parg16->f2);
    f3 = (WPARAM)(WORD32(parg16->f3));
    f4 = (LPARAM)(LONG32(parg16->f4));

    mpex.lReturn = 0;
    mpex.Parm16.WndProc.hwnd   = parg16->f1;
    mpex.Parm16.WndProc.wMsg   = (WORD)f2;
    mpex.Parm16.WndProc.wParam = (WORD)f3;
    mpex.Parm16.WndProc.lParam = f4;
    mpex.iMsgThunkClass = 0;

    // The Reader.exe shipped with Lotus 123MM version has a message
    // synchronization problem.  Force proper synchronization by
    // converting this PostMessage call to a SendMessage().
    if ((f2 == WM_VSCROLL) &&
         ((f3 == SB_THUMBTRACK) || (f3 == SB_THUMBPOSITION)) &&
         (CURRENTPTD()->dwWOWCompatFlagsEx & WOWCFEX_SENDPOSTEDMSG) ) {

        l = (LONG)WU32SendMessage(pFrame);
        FREEARGPTR(parg16);
        RETURN((ULONG) l);
    }

    hwnd = ThunkMsg16(&mpex);

    // Note: ThunkMsg16 may have caused 16-bit memory movement
    FREEARGPTR(pFrame);
    FREEARGPTR(parg16);

    WOW32ASSERT(fWhoCalled == WOWDDE_POSTMESSAGE);
    fWhoCalled = FALSE;
    if (hwnd) {

        l = PostMessage(hwnd, mpex.uMsg, mpex.uParam, mpex.lParam);

        if (!l)
            err = GetLastError();

        mpex.lReturn = l;
        if (MSG16NEEDSTHUNKING(&mpex)) {
            (mpex.lpfnUnThunk16)(&mpex);
        }


        // If the post message failed, then the message was probably one
        // that has pointers and therefore can not be posted. (MetaDesign
        // tries to post these kind of messages.) If the destination was a
        // WOW app, then make it into a private message, and try the post
        // again.  We don't have to worry about thunking since both the source
        // and destination are in the WOW address space.

        if (err == ERROR_INVALID_PARAMETER) {
            PWW   pww;
            DWORD dwpid;

            pww = FindPWW(hwnd);

            // was added for WM_DRAWITEM messages which are probably intended
            // for owner drawn std-type classes.  see bug #2047 NTBUG4
            if (pww != NULL && GETICLASS(pww, hwnd) != WOWCLASS_WIN16) {

                // make sure we're in the same vdm process
                if (!(GetWindowThreadProcessId(hwnd, &dwpid) &&
                      (dwpid == GetCurrentProcessId()))) {
                          return 0;
                }

                mpex.lReturn = PostMessage(hwnd, f2 | WOWPRIVATEMSG, f3, f4);
            }
        }
    }

    FREEARGPTR(parg16);
    RETURN((ULONG)mpex.lReturn);
}







/*++
    void PostQuitMessage(<nExitCode>)
    int <nExitCode>;

    The %PostQuitMessage% function informs Windows that the application wishes
    to terminate execution. It is typically used in response to a WM_DESTROY
    message.

    The %PostQuitMessage% function posts a WM_QUIT message to the application
    and returns immediately; the function merely informs the system that the
    application wants to quit sometime in the future.

    When the application receives the WM_QUIT message, it should exit the
    message loop in the main function and return control to Windows. The exit
    code returned to Windows must be the <wParam> parameter of the WM_QUIT
    message.

    <nExitCode>
        Specifies an application exit code. It is used as the wParam parameter
        of the WM_QUIT message.

    This function does not return a value.
--*/

ULONG FASTCALL WU32PostQuitMessage(PVDMFRAME pFrame)
{
    register PPOSTQUITMESSAGE16 parg16;

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

    PostQuitMessage(INT32(parg16->wExitCode));

    FREEARGPTR(parg16);
    RETURN(0);
}







/*++
    WORD RegisterWindowMessage(<lpString>)
    LPSTR <lpString>;

    This function defines a new window message that is guaranteed to be unique
    throughout the system. The returned message value can be used when calling
    the %SendMessage% or %PostMessage% function.

    %RegisterWindowMessage% is typically used for communication between two
    cooperating applications.

    If the same message string is registered by two different applications, the
    same message value is returned. The message remains registered until the
    user ends the Windows session.

    <lpString>
        Points to the message string to be registered.

    The return value specifies the outcome of the function. It is an unsigned
    short integer within the range 0xC000 to 0xFFFF if the message is
    successfully registered. Otherwise, it is zero.

    Use the %RegisterWindowMessage% function only when the same message must be
    understood by more than one application. For sending private messages within
    an application, an application can use any integer within the range WM_USER
    to 0xBFFF.
--*/

ULONG FASTCALL WU32RegisterWindowMessage(PVDMFRAME pFrame)
{
    ULONG ul;
    PSZ psz1;
    register PREGISTERWINDOWMESSAGE16 parg16;

    GETARGPTR(pFrame, sizeof(REGISTERWINDOWMESSAGE16), parg16);
    GETPSZPTR(parg16->f1, psz1);

    ul = GETWORD16(RegisterWindowMessage(psz1));

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







/*++
    void ReplyMessage(<lReply>)
    LONG <lReply>;

    The %ReplyMessage% function is used to reply to a message sent through the
    %SendMessage% function without returning control to the function that called
    %SendMessage.%

    By calling this function, the window function that receives the message
    allows the task that called %SendMessage% to continue to execute as though
    the task that received the message had returned control. The task that calls
    %ReplyMessage% also continues to execute.

    Normally a task that calls %SendMessage% to send a message to another task
    will not continue executing until the window procedure that Windows calls to
    receive the message returns. However, if a task that is called to receive a
    message needs to perform some type of operation that might yield control
    (such as calling the %MessageBox% or %DialogBox% functions), Windows could
    be placed in a deadlock situation where the sending task needs to execute
    and process messages but cannot because it is waiting for %SendMessage% to
    return. An application can avoid this problem if the task receiving the
    message calls %ReplyMessage% before performing any operation that could
    cause the task to yield.

    The %ReplyMessage% function has no effect if the message was not sent
    through the %SendMessage% function or if the message was sent by the same
    task.

    <lReply>
        Specifies the result of the message processing. The possible values
        depend on the actual message sent.

    This function does not return a value.
--*/

ULONG FASTCALL WU32ReplyMessage(PVDMFRAME pFrame)
{
    register PREPLYMESSAGE16 parg16;

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

    ReplyMessage(LONG32(parg16->f1));

    // WARNING - Don't use any 32 bit flat pointers after call to ReplyMessage,
    //           other tasks might have run and made the pointers invalid.

    FREEARGPTR(parg16);
    RETURN(0);
}







/*++
    DWORD SendDlgItemMessage(<hDlg>, <nIDDlgItem>, <wMsg>, <wParam>, <lParam>)
    HWND <hDlg>;
    int <nIDDlgItem>;
    WORD <wMsg>;
    WORD <wParam>;
    DWORD <lParam>;

    The %SendDlgItemMessage% function sends a message to the control specified
    by the <nIDDlgItem> parameter within the dialog box specified by the <hDlg>
    parameter. The %SendDlgItemMessage% function does not return until the
    message has been processed.

    <hDlg>
        Identifies the dialog box that contains the control.

    <nIDDlgItem>
        Specifies the integer identifier of the dialog item that is to
        receive the message.

    <wMsg>
        Specifies the message value.

    <wParam>
        Specifies additional message information.

    <lParam>
        Specifies additional message information.

    The return value specifies the outcome of the function. It is the value
    returned by the control's window function, or zero if the control identifier
    is not valid.

    Using %SendDlgItemMessage% is identical to obtaining a handle to the given
    control and calling the %SendMessage% function.
--*/

#define W31EM_GETRECT (WM_USER+2)  // w31 EM_GETRECT != NT EM_GETRECT

ULONG FASTCALL WU32SendDlgItemMessage(PVDMFRAME pFrame)
{
    HWND hdlg, hwndItem, hwnd;
    register PSENDDLGITEMMESSAGE16 parg16;
    MSGPARAMEX mpex;

static HWND  hwndCached = NULL ;
static DWORD dwCachedItem = 0L ;

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

    // QuarkExpress v3.31 passes a hard coded 7fff:0000 as the pointer to the
    // RECT struct for EM_GETRECT message - W3.1 rejects it in validation layer
    if( (DWORD32(parg16->f5) == 0x7FFF0000)    &&
        (WORD32(parg16->f3) == W31EM_GETRECT)  &&
        (CURRENTPTD()->dwWOWCompatFlagsEx & WOWCFEX_BOGUSPOINTER) ) {

        FREEARGPTR(parg16);
        RETURN((ULONG)0);
    }

    // Need unique handle
    hdlg = (HWND)FULLHWND32(parg16->f1);

    //
    // Caching the hwnd for the dialog item because EForm will
    // call SendDlgItemMessage in a tight loop.
    //
    if ( hdlg == hdlgSDIMCached && WORD32(parg16->f2) == dwCachedItem ) {

        // Set from cached
        hwndItem = hwndCached ;
    }
    else {
        if ( hwndItem = GetDlgItem(hdlg, WORD32(parg16->f2)) ) {

            // and cache needed information
            hdlgSDIMCached     = hdlg ;
            hwndCached         = hwndItem ;
            dwCachedItem       = WORD32(parg16->f2) ;
        }
        else {
            FREEARGPTR(parg16);
            RETURN((ULONG)0);
        }
    }

    mpex.lReturn = 0;
    if (hwndItem) {
        mpex.Parm16.WndProc.hwnd   = GETHWND16(hwndItem);
        mpex.Parm16.WndProc.wMsg   = WORD32(parg16->f3);
        mpex.Parm16.WndProc.wParam = WORD32(parg16->f4);
        mpex.Parm16.WndProc.lParam = LONG32(parg16->f5);
        mpex.iMsgThunkClass = 0;

        if (hwnd = ThunkMsg16(&mpex)) {

            // Note: ThunkMsg16 may have caused memory movement
            FREEARGPTR(pFrame);
            FREEARGPTR(parg16);

            /*
            ** Since we already know which window the message is going to
            ** don't make USER32 look it up again. - MarkRi
            */
            mpex.lReturn = SendMessage(hwndItem, mpex.uMsg, mpex.uParam,
                                                                mpex.lParam);
            // to keep common dialog structs in sync (see wcommdlg.c)
            Check_ComDlg_pszptr(CURRENTPTD()->CommDlgTd,
                                (VPVOID)mpex.Parm16.WndProc.lParam);

            if (MSG16NEEDSTHUNKING(&mpex)) {
                (mpex.lpfnUnThunk16)(&mpex);
            }
        }
    }

    FREEARGPTR(parg16);
    RETURN((ULONG)mpex.lReturn);
}







/*++
    DWORD SendMessage(<hwnd>, <msg>, <wParam>, <lParam>)
    HWND <hwnd>;
    WORD <msg>;
    WORD <wParam>;
    LONG <lParam>;

    The %SendMessage% function sends a message to a window or windows. The
    %SendMessage% function calls the window procedure for the specified window,
    and does not return until that window procedure has processed the message.
    This is in contrast to the %PostMessage% function which places the message
    into the specified window's message queue and returns immediately.

    <hwnd>
        Identifies the window that is to receive the message. If this parameter
        is 0xFFFF (-1), the message is sent to all top-level windows.

    <msg>
        Specifies the message to be sent.

    <wParam>
        Specifies additional message information. The contents of this
        parameter depends on the message being sent.

    <lParam>
        Specifies additional message information. The contents of this
        parameter depends on the message being sent.

    The return value is the result returned by the invoked window procedure; its
    value depends on the message being sent.
--*/

ULONG FASTCALL WU32SendMessage(PVDMFRAME pFrame)
{
    // NOTE: This can be called directly by WU32PostMessage!!!

    HWND hwnd;
    register PSENDMESSAGE16 parg16;
    MSGPARAMEX mpex;
    HWND16 hwndOld;
    UINT uMsgOld;
    UINT uParamOld;
    LONG lParamOld;
#ifdef DBCS
    HMEM16 hMem16;
    LPSZ lpBuf16,lpBuf32;
#endif // DBCS

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

    hwndOld   = parg16->f1;
    uMsgOld   = WORD32(parg16->f2);
    uParamOld = WORD32(parg16->f3);
    lParamOld = LONG32(parg16->f4);

    //
    // Check for funky apps sending WM_SYSCOMMAND - SC_CLOSE to progman
    //
    if ( uMsgOld == WM_SYSCOMMAND && uParamOld == SC_CLOSE ) {
        if ( hwndOld == GETHWND16(hwndProgman) && hwndProgman != (HWND)0 ) {
            //
            // Now if shift key is down, they must be trying to save
            // settings in progman.
            //
            if ( GetKeyState( VK_SHIFT ) < 0 ) {
                uMsgOld = RegisterWindowMessage("SaveSettings");
            }
        }
    }


    //
    // This is for the apps that use DDE protocol wrongly, like AmiPro.
    //

    WOW32ASSERT(fWhoCalled == FALSE);
    fWhoCalled = WOWDDE_POSTMESSAGE;

    mpex.lReturn = 0;
    mpex.Parm16.WndProc.hwnd   = hwndOld;
    mpex.Parm16.WndProc.wMsg   = (WORD)uMsgOld;
#ifdef DBCS
    //
    // For WIN3.1J's BUG ?
    // SendMessage( hwnd, WM_GETTEXT, 2, lpBuffer )
    // if string is DBCS, return is DBCS-leadbyte.
    // KKSUZUKA:#1731
    // 1994.8.8 add by V-HIDEKK
    //
    if( uMsgOld == WM_GETTEXT && uParamOld == 2 ){
        mpex.Parm16.WndProc.wParam = (WORD)(uParamOld + 1);
        mpex.Parm16.WndProc.lParam = GlobalAllocLock16( GMEM_SHARE | GMEM_MOVEABLE, uParamOld +1, &hMem16 );
    }
    else {
        mpex.Parm16.WndProc.wParam = (WORD)uParamOld;
        mpex.Parm16.WndProc.lParam = lParamOld;
    }
#else // !DBCS
    mpex.Parm16.WndProc.wParam = (WORD)uParamOld;
    mpex.Parm16.WndProc.lParam = lParamOld;
#endif // !DBCS
    mpex.iMsgThunkClass = 0;

    hwnd = ThunkMsg16(&mpex);

    // Note: ThunkMsg16 may have caused memory movement
    FREEARGPTR(pFrame);
    FREEARGPTR(parg16);

    WOW32ASSERT(fWhoCalled == WOWDDE_POSTMESSAGE);
    fWhoCalled = FALSE;

    if (hwnd) {

        BlockWOWIdle(TRUE);

#ifdef DEBUG
        if ( WM_DDE_EXECUTE == mpex.uMsg ) {
             // comes handy when debugging shell shortcut problems
             LOGDEBUG(1,("dest %x, src%x, msg %s\n",hwnd,mpex.uParam,mpex.lParam));
        }             
#endif
        mpex.lReturn = SendMessage(hwnd, mpex.uMsg, mpex.uParam, mpex.lParam);

        BlockWOWIdle(FALSE);
#ifdef DBCS
    //
    // For WIN3.1J's BUG ?
    // SendMessage( hwnd, WM_GETTEXT, 2, lpBuffer )
    // if string is DBCS, return is DBCSLeadbyte.
    // KKSUZUKA:#1731
    // 1994.8.8 add by V-HIDEKK
    //
        if( uMsgOld == WM_GETTEXT && uParamOld == 2 ){

            GETVDMPTR(mpex.Parm16.WndProc.lParam,mpex.Parm16.WndProc.wParam,lpBuf32);
            GETVDMPTR(lParamOld,uParamOld,lpBuf16);
            lpBuf16[0] = lpBuf32[0];
            if( mpex.lReturn == 2 ){
                lpBuf16[1] = 0;
                mpex.lReturn = 1;
            }
            else {
                lpBuf16[1] = lpBuf32[1];
            }
            FREEVDMPTR(lpBuf16);
            FREEVDMPTR(lpBuf32);
            GlobalUnlockFree16( mpex.Parm16.WndProc.lParam );
            mpex.Parm16.WndProc.wParam = (WORD)uParamOld;
            mpex.Parm16.WndProc.lParam = lParamOld;
        }
#endif // DBCS


        WOW32ASSERT(fWhoCalled == FALSE);
        fWhoCalled = WOWDDE_POSTMESSAGE;
        if (MSG16NEEDSTHUNKING(&mpex)) {
            (mpex.lpfnUnThunk16)(&mpex);
        }
        WOW32ASSERT(fWhoCalled == WOWDDE_POSTMESSAGE);
        fWhoCalled = FALSE;
    }

    FREEARGPTR(parg16);
    RETURN((ULONG)mpex.lReturn);
}







/*++
    int TranslateAccelerator(<hwnd>, <hAccTable>, <lpMsg>)

    The %TranslateAccelerator% function processes keyboard accelerators for menu
    commands. The %TranslateAccelerator% function translates WM_KEYUP and
    WM_KEYDOWN messages to WM_COMMAND or WM_SYSCOMMAND messages, if there is an
    entry for the key in the application's accelerator table. The high-order
    word of the <lParam> parameter of the WM_COMMAND or WM_SYSCOMMAND message
    contains the value 1 to differentiate the message from messages sent by
    menus or controls.

    WM_COMMAND or WM_SYSCOMMAND messages are sent directly to the window, rather
    than being posted to the application queue. The %TranslateAccelerator%
    function does not return until the message is processed.

    Accelerator key strokes that are defined to select items from the system
    menu are translated into WM_SYSCOMMAND messages; all other accelerators are
    translated into WM_COMMAND messages.

    <hwnd>
        Identifies the window whose messages are to be translated.

    <hAccTable>
        %HANDLE% Identifies an accelerator table (loaded by using the
        %LoadAccelerators% function).

    <lpMsg>
        Points to a message retrieved by using the %GetMessage% or
        %PeekMessage% function. The message must be an %MSG% structure and
        contain message information from the Windows application queue.

    .cmt
    19-Sep-1990 [johnca]
    Doesn't this function really return a BOOL?
    .endcmt

    The return value specifies the outcome of the function. It is nonzero if
    translation occurs. Otherwise, it is zero.

    When %TranslateAccelerator% returns nonzero (meaning that the message is
    translated), the application should <not> process the message again by using
    the %TranslateMessage% function.

    Commands in accelerator tables do not have to correspond to menu items.

    If the accelerator command does correspond to a menu item, the application
    is sent WM_INITMENU and WM_INITMENUPOPUP messages, just as if the user were
    trying to display the menu. However, these messages are not sent if any of
    the following conditions are present:

    o   The window is disabled.

    o   The menu item is disabled.

    o   The command is not in the System menu and the window is minimized.

    o   A mouse capture is in effect (for more information, see the %SetCapture%
        function, earlier in this chapter).

    If the window is the active window and there is no keyboard focus (generally
    true if the window is minimized), then WM_SYSKEYUP and WM_SYSKEYDOWN
    messages are translated instead of WM_KEYUP and WM_KEYDOWN messages.

    If an accelerator key stroke that corresponds to a menu item occurs when the
    window that owns the menu is iconic, no WM_COMMAND message is sent. However,
    if an accelerator key stroke that does not match any of the items on the
    window's menu or the System menu occurs, a WM_COMMAND message is sent, even
    if the window is iconic.
--*/

ULONG FASTCALL WU32TranslateAccelerator(PVDMFRAME pFrame)
{
    ULONG ul;
    MSG t3;
    register PTRANSLATEACCELERATOR16 parg16;

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

    W32CopyMsgStruct(parg16->f3, &t3, TRUE);
    ul = GETINT16(TranslateAccelerator(HWND32(parg16->f1),
                                       HACCEL32(parg16->f2), &t3 ));

    FREEARGPTR(parg16);
    RETURN(ul);
}







/*++
    BOOL TranslateMDISysAccel(<hwndClient>, <lpMsg>)

    The %TranslateMDISysAccel% function processes keyboard accelerators for
    multiple document interface (MDI) child window System-menu commands. The
    %TranslateMDISysAccel% function translates WM_KEYUP and WM_KEYDOWN messages
    to WM_SYSCOMMAND messages. The high-order word of the <lParam> parameter of
    the WM_SYSCOMMAND message contains the value 1 to differentiate the message
    from messages sent by menus or controls.

    <hwndClient>
        Identifies the parent MDI client window.

    <lpMsg>
        Points to a message retrieved by using the %GetMessage% or
        %PeekMessage% function. The message must be an %MSG% structure and
        contain message information from the Windows application queue.

    The return value is TRUE if the function translated a message into a system
    command. Otherwise, it is FALSE.
--*/

ULONG FASTCALL WU32TranslateMDISysAccel(PVDMFRAME pFrame)
{
    ULONG ul;
    MSG t2;
    register PTRANSLATEMDISYSACCEL16 parg16;

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

    W32CopyMsgStruct(parg16->f2, &t2, TRUE);

    ul = GETBOOL16(TranslateMDISysAccel(HWND32(parg16->f1), &t2));

    FREEARGPTR(parg16);
    RETURN(ul);
}







/*++
    BOOL TranslateMessage(<lpMsg>)

    The %TranslateMessage% function translates virtual-key messages into
    character messages, as follows:

    o   WM_KEYDOWN/WM_KEYUP combinations produce a WM_CHAR or a WM_DEADCHAR
        message.

    o   WM_SYSKEYDOWN/WM_SYSKEYUP combinations produce a WM_SYSCHAR or a
        WM_SYSDEADCHAR message.

    The character messages are posted to the application queue, to be read the
    next time the application calls the %GetMessage% or %PeekMessage% function.

    <lpMsg>
        Points to a %MSG% structure retrieved through the GetMessage or
        PeekMessage function. The structure contains message information from
        the Windows application queue.

    The return value specifies the outcome of the function. It is TRUE if the
    message is translated (that is, character messages are posted to the
    application queue). Otherwise, it is FALSE.

    The %TranslateMessage% function does not modify the message given by the
    <lpMsg> parameter.

    %TranslateMessage% produces WM_CHAR messages only for keys which are mapped
    to ASCII characters by the keyboard driver.

    An application should not call %TranslateMessage% if the application
    processes virtual-key messages for some other purpose. For instance, an
    application should not call the %TranslateMessage% function if the
    %TranslateAccelerator% function returns TRUE.
--*/

ULONG FASTCALL WU32TranslateMessage(PVDMFRAME pFrame)
{
    ULONG ul;
    MSG t1;
    register PTRANSLATEMESSAGE16 parg16;

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

    W32CopyMsgStruct(parg16->f1, &t1, TRUE);

    ul = GETBOOL16(TranslateMessage( &t1 ));

    FREEARGPTR(parg16);
    RETURN(ul);
}







/*++
    void WaitMessage(VOID)

    The %WaitMessage% function yields control to other applications when an
    application has no other tasks to perform. The %WaitMessage% function
    suspends the application and does not return until a new message is placed
    in the application's queue.

    This function has no parameters.

    This function does not return a value.

    The %GetMessage%, %PeekMessage%, and %WaitMessage% functions yield control
    to other applications. These calls are the only way to let other
    applications run. If your application does not call any of these functions
    for long periods of time, other applications cannot run.

    When %GetMessage%, %PeekMessage%, and %WaitMessage% yield control to other
    applications, the stack and data segments of the application calling the
    function may move in memory to accommodate the changing memory requirements
    of other applications. If the application has stored long pointers to
    objects in the data or stack segment (that is, global or local variables),
    these pointers can become invalid after a call to %GetMessage%,
    %PeekMessage%, or %WaitMessage%.
--*/

ULONG FASTCALL WU32WaitMessage(PVDMFRAME pFrame)
{
    UNREFERENCED_PARAMETER(pFrame);

    BlockWOWIdle(TRUE);

    WaitMessage();

    BlockWOWIdle(FALSE);

    RETURN(0);
}
