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


#define OEMRESOURCE

#include "precomp.h"
#pragma hdrstop


MODNAME(wuser.c);

extern HANDLE hmodWOW32;


/*++
    HDC BeginPaint(<hwnd>, <lpPaint>)
    HWND <hwnd>;
    LPPAINTSTRUCT <lpPaint>;

    The %BeginPaint% function prepares the given window for painting and fills
    the paint structure pointed to by the <lpPaint> parameter with information
    about the painting.

    The paint structure contains a handle to the device context for the window,
    a %RECT% structure that contains the smallest rectangle that completely
    encloses the update region, and a flag that specifies whether or not the
    background has been erased.

    The %BeginPaint% function automatically sets the clipping region of the
    device context to exclude any area outside the update region. The update
    region is set by the %InvalidateRect% or %InvalidateRgn% functions and by
    the system after sizing, moving, creating, scrolling, or any other operation
    that affects the client area. If the update region is marked for erasing,
    %BeginPaint% sends a WM_ERASEBKGND message to the window.

    An application should not call the %BeginPaint% function except in response
    to a WM_PAINT message. Each %BeginPaint% call must have a matching call to
    the %EndPaint% function.

    <hwnd>
        Identifies the window to be repainted.

    <lpPaint>
        Points to the %PAINTSTRUCT% structure that is to receive painting
        information, such as the device context for the window and the update
        rectangle.

    The return value identifies the device context for the specified window.

    If the caret is in the area to be painted, the %BeginPaint% function
    automatically hides the caret to prevent it from being erased.
--*/

ULONG FASTCALL WU32BeginPaint(PVDMFRAME pFrame)
{
    ULONG ul;
    PAINTSTRUCT t2;
    register PBEGINPAINT16 parg16;
    VPVOID  vpPaint;

    GETARGPTR(pFrame, sizeof(BEGINPAINT16), parg16);
    vpPaint = parg16->vpPaint;

    ul = GETHDC16(BeginPaint(
    HWND32(parg16->hwnd),
    &t2
    ));

    putpaintstruct16(vpPaint, &t2);
    W32FixPaintRect (vpPaint, &t2);

    FREEARGPTR(parg16);
    RETURN(ul);
}


/*++
    HICON CreateIcon(<hInstance>, <nWidth>, <nHeight>, <nPlanes>,
        <nBitsPixel>, <lpANDbits>, <lpXORbits>)
    HANDLE <hInstance>;
    int <nWidth>;
    int <nHeight>;
    BYTE <nPlanes>;
    BYTE <nBitsPixel>;
    LPSTR <lpANDbits>;
    LPSTR <lpXORbits>;

    This function creates an icon that has specified width, height, colors, and
    bit patterns.

    <hInstance>
        Identifies an instance of the module creating the icon.

    <nWidth>
        Specifies the width in pixels of the icon.

    <nHeight>
        Specifies the height in pixels of the icon.

    <nPlanes>
        Specifies the number of planes in the XOR mask of the icon.

    <nBitsPixel>
        Specifies the number of bits per pixel in the XOR mask of the icon.

    <lpANDbits>
        Points to an array of bytes that contains the bit values for the AND
        mask of the icon. This array must specify a monochrome mask.

    <lpXORbits>
        Points to an array of bytes that contains the bit values for the XOR
        mask of the icon. This can be the bits of a monochrome or
        device-dependent color bitmap.

    The return value identifies an icon if the function is successful.
    Otherwise, it is NULL.
--*/

ULONG FASTCALL WU32CreateIcon(PVDMFRAME pFrame)
{
    ULONG   ul;
    register PCREATEICON16 parg16;
    int     nWidth;
    int     nHeight;
    BYTE    nPlanes;
    BYTE    nBitsPixel;
    DWORD   nBytesAND;
    DWORD   nBytesXOR;
    LPBYTE  lpBitsAND;
    LPBYTE  lpBitsXOR;
    int     ScanLen16;

    HANDLE  h32;
    HAND16  h16;
    HAND16  hInst16;

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

    hInst16    = parg16->f1;
    nWidth     = INT32(parg16->f2);
    nHeight    = INT32(parg16->f3);

    /*
    ** Convert the AND mask bits
    */
    nPlanes    = 1;     /* MONOCHROME BITMAP */
    nBitsPixel = 1;     /* MONOCHROME BITMAP */
    ScanLen16 = (((nWidth*nBitsPixel)+15)/16) * 2 ;  // bytes/scan in 16 bit world
    nBytesAND = ScanLen16*nHeight*nPlanes;

    GETVDMPTR(parg16->f6, nBytesAND, lpBitsAND);


    /*
    ** Convert the XOR mask bits
    */
    nPlanes    = BYTE32(parg16->f4);
    nBitsPixel = BYTE32(parg16->f5);

    ScanLen16 = (((nWidth*nBitsPixel)+15)/16) * 2 ;  // bytes/scan in 16 bit world
    nBytesXOR = ScanLen16*nHeight*nPlanes;

    GETVDMPTR(parg16->f7, nBytesXOR, lpBitsXOR);


    h32 = (HANDLE)CreateIcon(HMODINST32(hInst16),
                 nWidth,
                 nHeight,
                 nPlanes,
                 nBitsPixel,
                 lpBitsAND,
                 lpBitsXOR);

    if (h32) {
        h16 = (HAND16)W32Create16BitCursorIcon(hInst16,
                            nWidth/2, nHeight/2,
                            nWidth, nHeight, nPlanes, nBitsPixel,
                            lpBitsAND, lpBitsXOR,
                            nBytesAND, nBytesXOR);

        ul  = SetupCursorIconAlias(hInst16, h32, h16,
                                   HANDLE_TYPE_ICON, NULL, (WORD)NULL);
    } else {
        ul = 0;
    }

    FREEPSZPTR(lpBitsAND);
    FREEPSZPTR(lpBitsXOR);
    FREEARGPTR(parg16);
    RETURN(ul);
}


/*++
    BOOL DestroyIcon(<hIcon>)
    HICON <hIcon>;

    This function destroys an icon that was previously created by the
    %CreateIcon% function and frees any memory that the icon occupied. It should
    not be used to destroy any icon that was not created with the %CreateIcon%
    function.

    <hIcon>
        Identifies the icon to be destroyed. The icon must not be in current
        use.

    The return value is TRUE if the function was successful. It is FALSE if
    the function failed.
--*/

ULONG FASTCALL WU32DestroyIcon(PVDMFRAME pFrame)
{
    ULONG ul;
    register PDESTROYICON16 parg16;

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

    if (ul = GETBOOL16(DestroyIcon(HICON32(parg16->f1))))
        FREEHICON16(parg16->f1);

    FREEARGPTR(parg16);
    RETURN(ul);
}


ULONG FASTCALL WU32DragObject(PVDMFRAME pFrame)
{
    ULONG ul;
    register PDRAGOBJECT16 parg16;

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

    ul = (ULONG) DragObject(HWND32(parg16->f1),
                            HWND32(parg16->f2),
                            UINT32(parg16->f3),
                            DWORD32(parg16->f4),
                            HCURSOR32(parg16->f5)
    );

    FREEARGPTR(parg16);
    RETURN(ul);
}


ULONG FASTCALL WU32DragDetect(PVDMFRAME pFrame)
{
    ULONG ul;
    POINT pt;
    register PDRAGDETECT16 parg16;

    GETARGPTR(pFrame, sizeof(*parg16), parg16);
    COPYPOINT16(parg16->pt, pt);

    ul = (ULONG) DragDetect(
                     HWND32(parg16->hwnd),
                     pt
                     );

    FREEARGPTR(parg16);
    RETURN(ul);
}

/*++
    void DrawFocusRect(<hDC>, <lpRect>)
    HDC <hDC>;
    LPRECT <lpRect>;

    The %DrawFocusRect% function draws a rectangle in the style used to indicate
    focus.

    <hDC>
        Identifies the device context.

    <lpRect>
        Points to a %RECT% structure that specifies the
        coordinates of the rectangle to be drawn.

    This function does not return a value.

    Since this is an XOR function, calling this function a second time with the
    same rectangle removes the rectangle from the display.

    The rectangle drawn by this function cannot be scrolled. To scroll an area
    containing a rectangle drawn by this function, call %DrawFocusRect% to
    remove the rectangle from the display, scroll the area, and then call
    %DrawFocusRect% to draw the rectangle in the new position.
--*/

ULONG FASTCALL WU32DrawFocusRect(PVDMFRAME pFrame)
{
    RECT t2;
    register PDRAWFOCUSRECT16 parg16;

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

    WOW32VERIFY(GETRECT16(parg16->f2, &t2));

    DrawFocusRect(
    HDC32(parg16->f1),
    &t2
    );

    FREEARGPTR(parg16);
    RETURN(0);
}


/*++
    BOOL DrawIcon(<hDC>, <X>, <Y>, <hIcon>)
    HDC <hDC>;
    int <X>;
    int <Y>;
    HICON <hIcon>;

    The %DrawIcon% function draws an icon on the specified device. The
    %DrawIcon% function places the icon's upper-left corner at the location
    specified by the <X> and <Y> parameters. The location is subject to the
    current mapping mode of the device context.

    <hDC>
        Identifies the device context for a window.

    <X>
        Specifies the logical <x>-coordinate of the upper-left corner of
        the icon.

    <Y>
        Specifies the logical <y>-coordinate of the upper-left corner of
        the icon.

    <hIcon>
        Identifies the icon to be drawn.

    The return value specifies the outcome of the function. It is TRUE if the
    function is successful. Otherwise, it is FALSE.

    The icon resource must have been previously loaded by using the %LoadIcon%
    function. The MM_TEXT mapping mode must be selected prior to using this
    function.
--*/

ULONG FASTCALL WU32DrawIcon(PVDMFRAME pFrame)
{
    ULONG ul;
    register PDRAWICON16 parg16;

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

    ul = GETBOOL16(DrawIcon(
    HDC32(parg16->f1),
    INT32(parg16->f2),
    INT32(parg16->f3),
    HICON32(parg16->f4)
    ));

    FREEARGPTR(parg16);
    RETURN(ul);
}


/*++
    int DrawText(<hDC>, <lpString>, <nCount>, <lpRect>, <wFormat>)
    HDC <hDC>;
    LPSTR <lpString>;
    int <nCount>;
    LPRECT <lpRect>;
    WORD <wFormat>;

    The %DrawText% function draws formatted text in the rectangle specified by
    the <lpRect> parameter. It formats text by expanding tabs into appropriate
    spaces, justifying text to the left, right, or center of the given
    rectangle, and breaking text into lines that fit within the given
    rectangle. The type of formatting is specified by the <wFormat> parameter.

    The %DrawText% function uses the device context's selected font, text color,
    and background color to draw the text. Unless the DT_NOCLIP format is used,
    %DrawText% clips the text so that the text does not appear outside the given
    rectangle. All formatting is assumed to have multiple lines unless the
    DT_SINGLELINE format is given.

    <hDC>
        Identifies the device context.

    <lpString>
        Points to the string to be drawn. If the <nCount> parameter is -1, the
        string must be null-terminated.

    <nCount>
        Specifies the number of bytes in the string. If <nCount> is -1,
        then <lpString> is assumed to be a long pointer to a null-terminated
        string and %DrawText% computes the character count automatically.

    <lpRect>
        Points to a %RECT% structure that contains the rectangle
        (in logical coordinates) in which the text is to be formatted.

    <wFormat>
        Specifies the method of formatting the text. It can be any
        combination of the following values:

    DT_BOTTOM
        Specifies bottom-justified text. This value must be combined with
        DT_SINGLELINE.

    DT_CALCRECT
        Determines the width and height of the rectangle. If there are multiple
        lines of text, %DrawText% will use the width of the rectangle pointed to
        by the <lpRect> parameter and extend the base of the rectangle to bound
        the last line of text. If there is only one line of text, %DrawText%
        will modify the right side of the rectangle so that it bounds the last
        character in the line. In either case, %DrawText% returns the height of
        the formatted text but does not draw the text.

    DT_CENTER
        Centers text horizontally.

    DT_EXPANDTABS
        Expands tab characters. The default number of characters per tab is
        eight.

    DT_EXTERNALLEADING
        Includes the font external leading in line height. Normally, external
        leading is not included in the height of a line of text.

    DT_LEFT
        Aligns text flush-left.

    DT_NOCLIP
        Draws without clipping. %DrawText% is somewhat faster when DT_NOCLIP is
        used.

    DT_NOPREFIX
        Turns off processing of prefix characters. Normally, %DrawText%
        interprets the mnemonic-prefix character & as a directive to
        underscore the character that follows, and the mnemonic-prefix
        characters && as a directive to print a single &. By specifying
        DT_NOPREFIX, this processing is turned off.

    DT_RIGHT
        Aligns text flush-right.

    DT_SINGLELINE
        Specifies single line only. Carriage returns and linefeeds do not break
        the line.

    DT_TABSTOP
        Sets tab stops. The high-order byte of the <wFormat> parameter is the
        number of characters for each tab. The default number of characters per
        tab is eight.

    DT_TOP
        Specifies top-justified text (single line only).

    DT_VCENTER
        Specifies vertically centered text (single line only).

    DT_WORDBREAK
        Specifies word breaking. Lines are automatically broken between words if
        a word would extend past the edge of the rectangle specified by the
        <lpRect> parameter. A carriage return/line sequence will also break the
        line.

        Note that the DT_CALCRECT, DT_EXTERNALLEADING, DT_INTERNAL, DT_NOCLIP,
        and DT_NOPREFIX values cannot be used with the DT_TABSTOP value:

    The return value specifies the height of the text.

    If the selected font is too large for the specified rectangle, the
    %DrawText% function does not attempt to substitute a smaller font.
--*/

ULONG FASTCALL WU32DrawText(PVDMFRAME pFrame)
{
    ULONG ul;
    PSTR pstr2;
    RECT t4;
    register PDRAWTEXT16 parg16;

    GETARGPTR(pFrame, sizeof(DRAWTEXT16), parg16);
    GETVARSTRPTR(parg16->vpString, INT32(parg16->nCount), pstr2);

    WOW32VERIFY(GETRECT16(parg16->vpRect, &t4));

    ul = GETINT16(DrawText(
    HDC32(parg16->hdc),
    pstr2,
    INT32(parg16->nCount),
    &t4,
    WORD32(parg16->wFormat)
    ));

    PUTRECT16(parg16->vpRect, &t4);

    FREESTRPTR(pstr2);
    FREEARGPTR(parg16);
    RETURN(ul);
}


/*++
    BOOL EnableHardwareInput(<bEnableInput>)
    BOOL <bEnableInput>;

    The %EnableHardwareInput% function disables mouse and keyboard input. The
    input is saved if the <bEnableInput> parameter is TRUE and discarded if it
    is FALSE.

    <bEnableInput>
        Specifies that the function should save input if the bEnableInput
        parameter is set to a nonzero value; specifies that the function should
        discard input if the bEnableInput parameter is set to zero.

    The return value specifies whether mouse and keyboard input is disabled. It
    is TRUE if input was previously enabled. Otherwise, it is FALSE. The
    default return value is TRUE.
--*/

ULONG FASTCALL WU32EnableHardwareInput(PVDMFRAME pFrame)
{
#ifdef ONLY_API16
    ULONG ul;
    register PENABLEHARDWAREINPUT16 parg16;

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

    ul = GETBOOL16(EnableHardwareInput(
    BOOL32(parg16->f1)
    ));

    FREEARGPTR(parg16);
    RETURN(ul);
#else
    UNREFERENCED_PARAMETER(pFrame);

    return TRUE;
#endif

}


/*++
    void EndPaint(<hwnd>, <lpPaint>)
    HWND <hwnd>;
    LPPAINTSTRUCT <lpPaint>;

    The %EndPaint% function marks the end of painting in the given window. The
    %EndPaint% function is required for each call to the %BeginPaint% function,
    but only after painting is complete.

    <hwnd>
        Identifies the window that is repainted.

    <lpPaint>
        Points to a %PAINTSTRUCT% structure that contains the painting
        information retrieved by the %BeginPaint% function.

    This function does not return a value.

    If the caret was hidden by the %BeginPaint% function, %EndPaint% restores
    the caret to the screen.
--*/

ULONG FASTCALL WU32EndPaint(PVDMFRAME pFrame)
{
    PAINTSTRUCT t2;
    register PENDPAINT16 parg16;

    GETARGPTR(pFrame, sizeof(ENDPAINT16), parg16);
    getpaintstruct16(parg16->vpPaint, &t2);

    EndPaint(
    HWND32(parg16->hwnd),
    &t2
    );

    FREEARGPTR(parg16);
    RETURN(0);
}


#define MAX_WIN16_PROP_TEXT 256     /* Taken from Win 3.1 - winprops.c */

static VPVOID   vpEnumPropsProc;
static VPVOID   vpString;

INT W32EnumPropsFunc( HWND hwnd, LPSTR lpString, HANDLE hData )
{
    PARM16 Parm16;
    LONG lReturn;
    VPVOID vp;

    if ( HIWORD(lpString) == 0 ) {
        vp = (DWORD)lpString;
    } else {
        INT cb;

        vp = vpString;
        cb = strlen(lpString)+1;
        if ( cb > MAX_WIN16_PROP_TEXT-1 ) {
            cb = MAX_WIN16_PROP_TEXT-1;
        }
        putstr16(vpString, lpString, cb);
    }

    Parm16.EnumPropsProc.hwnd = GETHWND16(hwnd);
    Parm16.EnumPropsProc.vpString = vp;
    Parm16.EnumPropsProc.hData = GETHANDLE16(hData);

    CallBack16(RET_ENUMPROPSPROC, &Parm16, vpEnumPropsProc, (PVPVOID)&lReturn);

    return (SHORT)lReturn;
}

/*++
    int EnumProps(<hwnd>, <lpEnumFunc>)
    HWND <hwnd>;
    FARPROC <lpEnumFunc>;

    The %EnumProps% function enumerates all entries in the property list of the
    specified window. It enumerates the entries by passing them, one by one, to
    the callback function specified by <lpEnumFunc>. %EnumProps% continues until
    the last entry is enumerated or the callback function returns zero.

    <hwnd>
        Identifies the window whose property list is to be enumerated.

    <lpEnumFunc>
        Specifies the procedure-instance address of the callback function.
        See the following Comments section for details.

    The return value specifies the last value returned by the callback function.
    It is -1 if the function did not find a property for enumeration.

    An application can remove only those properties which it has added. It
    should not remove properties added by other applications or by Windows
    itself.

    The following restrictions apply to the callback function:

    1   The callback function must not yield control or do anything that might
        yield control to other tasks.

    2   The callback function can call the %RemoveProp% function. However, the
        %RemoveProp% function can remove only the property passed to the
        callback function through the callback function's parameters.

    3   A callback function should not attempt to add properties.

    The address passed in the <lpEnumFunc> parameter must be created by using
    the %MakeProcInstance% function.

    Fixed Data Segments:

    The callback function must use the Pascal calling convention and must be
    declared %FAR%. In applications and dynamic libraries with fixed data
    segments and in dynamic libraries with moveable data segments that do not
    contain a stack, the callback function must have the form shown below.

    Callback Function:

    int  FAR PASCAL <EnumFunc>(<hwnd>, <lpString>, <hData>)
    HWND <hwnd>;
    LPSTR <lpString>;
    HANDLE <hData>;

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

    <hwnd>
        Identifies a handle to the window that contains the property list.

    <lpString>
        Points to the null-terminated string associated with the data handle
        when the application called the%SetProp% function to set the property.
        If the application passed an atom instead of a string to the %SetProp%
        function, the<lpString> parameter contains the atom in its low-order
        word, and the high-order word is zero.

    <hData>
        Identifies the data handle.

    The callback function can carry out any desired task. It must return a
    nonzero value to continue enumeration, or a zero value to stop it.

    Moveable Data Segments:

    The callback function must use the Pascal calling convention and must be
    declared %FAR%. In applications with moveable data segments and in dynamic
    libraries whose moveable data segments also contain a stack, the callback
    function must have the form shown below.

    Callback Function:

    int  FAR PASCAL <EnumFunc>(<hwnd>, <nDummy>, <pString>, <hData>)
    HWND <hwnd>;
    WORD <nDummy>;
    PSTR <pString>;
    HANDLE <hData>;

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

    <hwnd>
        Identifies a handle to the window that contains the property list.

    <nDummy>
        Specifies a dummy parameter.

    <pString>
        Points to the null-terminated string associated with the data handle
        when the application called the %SetProp% function to set the property.
        If the application passed an atom instead of a string to the %SetProp%
        function, the <pString> parameter contains the atom.

    <hData>
        Identifies the data handle.

    The callback function can carry out any desired task. It should return a
    nonzero value to continue enumeration, or a zero value to stop it.

    The alternate form above is required since movement of the data will
    invalidate any long pointer to a variable on the stack, such as the
    <lpString> parameter. The data segment typically moves if the callback
    function allocates more space in the local heap than is currently
    available.
--*/

ULONG FASTCALL WU32EnumProps(PVDMFRAME pFrame)
{
    ULONG ul;
    HWND    hwnd;
    register PENUMPROPS16 parg16;

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

    hwnd            = HWND32(parg16->f1);
    vpEnumPropsProc =        parg16->f2;

    vpString = malloc16(MAX_WIN16_PROP_TEXT);
    // 16-bit memory may have moved - invalidate flat pointers
    FREEARGPTR(parg16);
    FREEVDMPTR(pFrame);

    if (vpString) {
        ul = GETINT16(EnumProps(hwnd,(PROPENUMPROC)W32EnumPropsFunc));
        free16(vpString);

    } else {
        ul = (ULONG)-1;
    }

    RETURN(ul);
}



/*++
    int ExcludeUpdateRgn(<hDC>, <hwnd>)
    HANDLE <hDC>;
    HWND <hwnd>;

    The %ExcludeUpdateRgn% function prevents drawing within invalid areas of a
    window by excluding an updated region in the window from a clipping region.

    <hDC>
        Identifies the device context associated with the clipping region.

    <hwnd>
        Identifies the window being updated.

    The return value is the type of excluded region. It can be any one of the
    following values:

    COMPLEXREGION   The region has overlapping borders.
    ERROR           No region was created.
    NULLREGION      The region is empty.
    SIMPLEREGION    The region has no overlapping borders.
--*/

ULONG FASTCALL WU32ExcludeUpdateRgn(PVDMFRAME pFrame)
{
    ULONG ul;
    register PEXCLUDEUPDATERGN16 parg16;

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

    ul = GETINT16(ExcludeUpdateRgn(
    HDC32(parg16->f1),
    HWND32(parg16->f2)
    ));

    FREEARGPTR(parg16);
    RETURN(ul);
}

/*++
    int FillWindow(<hWndParent>, <hWnd>, <hDC>, <hBrush>)
    HWND <hWndParent>;
    HWND  <hWnd>;
    HDC <hDC>;
    HBRUSH <hBrush>;

    The %FillWindow% function paints a given window by using the specified
    brush.

    <hWndParent>
        Identifies the parent of the window to be painted.

    <hWnd>
        Identifies the window to be painted.

    <hDC>
        Identifies the device context.

    <hBrush>
        Identifies the brush used to fill the rectangle.

--*/

ULONG FASTCALL WU32FillWindow(PVDMFRAME pFrame)
{
    register PFILLWINDOW16 parg16;

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

    (pfnOut.pfnFillWindow)(
    HWND32(parg16->f1),
    HWND32(parg16->f2),
    HDC32(parg16->f3),
    HBRUSH32(parg16->f4)
    );

    FREEARGPTR(parg16);
    RETURN(0);
}


/*++
    int FillRect(<hDC>, <lpRect>, <hBrush>)
    HDC <hDC>;
    LPRECT <lpRect>;
    HBRUSH <hBrush>;

    The %FillRect% function fills a given rectangle by using the specified
    brush. The %FillRect% function fills the complete rectangle, including the
    left and top borders, but does not fill the right and bottom borders.

    <hDC>
        Identifies the device context.

    <lpRect>
        Points to a %RECT% structure that contains the logical
        coordinates of the rectangle to be filled.

    <hBrush>
        Identifies the brush used to fill the rectangle.

    Although the %FillRect% function return type is an integer, the return value
    is not used and has no meaning.

    The brush must have been created previously by using either the
    %CreateHatchBrush%, %CreatePatternBrush%, or %CreateSolidBrush% function, or
    retrieved using the %GetStockObject% function.

    When filling the specified rectangle, the %FillRect% function does not
    include the rectangle's right and bottom sides. GDI fills a rectangle up to,
    but does not include, the right column and bottom row, regardless of the
    current mapping mode.

    %FillRect% compares the values of the %top%, %bottom%, %left%, and %right%
    members of the specified rectangle. If %bottom% is less than or equal to
    %top%, or if %right% is less than or equal to %left%, the rectangle is not
    drawn.
--*/

ULONG FASTCALL WU32FillRect(PVDMFRAME pFrame)
{
    ULONG ul;
    RECT t2;
    register PFILLRECT16 parg16;

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

    WOW32VERIFY(GETRECT16(parg16->f2, &t2));

    ul = GETINT16(FillRect(
    HDC32(parg16->f1),
    &t2,
    HBRUSH32(parg16->f3)
    ));

    FREEARGPTR(parg16);
    RETURN(ul);
}


/*++
    int FrameRect(<hDC>, <lpRect>, <hBrush>)
    HDC <hDC>;
    LPRECT <lpRect>;
    HBRUSH <hBrush>;

    The %FrameRect% function draws a border around the rectangle specified by
    the <lpRect> parameter. The %FrameRect% function uses the given brush to
    draw the border. The width and height of the border is always one logical
    unit.

    <hDC>
        Identifies the device context of the window.

    <lpRect>
        Points to a %RECT% structure that contains the logical
        coordinates of the upper-left and lower-right corners of the rectangle.

    <hBrush>
        Identifies the brush to be used for framing the rectangle.

    Although the return value type is integer, its contents should be ignored.

    The brush identified by the <hBrush> parameter must have been created
    previously by using the %CreateHatchBrush%, %CreatePatternBrush%, or
    %CreateSolidBrush% function.

    If the %bottom% member is less than or equal to the %top% member, or if the
    %right% member is less than or equal to the %left% member, the rectangle is
    not drawn.
--*/

ULONG FASTCALL WU32FrameRect(PVDMFRAME pFrame)
{
    ULONG ul;
    RECT t2;
    register PFRAMERECT16 parg16;

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

    WOW32VERIFY(GETRECT16(parg16->f2, &t2));

    ul = GETINT16(FrameRect(
    HDC32(parg16->f1),
    &t2,
    HBRUSH32(parg16->f3)
    ));

    FREEARGPTR(parg16);
    RETURN(ul);
}


/*++
    int GetAsyncKeyState(<vKey>)
    int <vKey>;

    The %GetAsyncKeyState% function determines whether a key is up or down at
    the time the function is called, and whether the key was pressed after a
    previous call to the %GetAsyncKeyState% function. If the most significant
    bit of the return value is set, the key is currently down; if the least
    significant bit is set, the key was pressed after a previous call to the
    function.

    <vKey>
        Specifies one of 256 possible virtual-key code values.

    The return value specifies whether the key was pressed since the last call
    to %GetAsyncKeyState% and whether the key is currently up or down. If the
    most significant bit is set, the key is down, and if the least significant
    bit is set, the key was pressed after a preceding %GetAsyncKeyState% call.
--*/

ULONG FASTCALL WU32GetAsyncKeyState(PVDMFRAME pFrame)
{
    ULONG ul;
    register PGETASYNCKEYSTATE16 parg16;

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

    ul = GETINT16(GetAsyncKeyState(
    INT32(parg16->f1)
    ));

    FREEARGPTR(parg16);
    RETURN(ul);
}


/*++
    HWND GetCapture(VOID)

    The %GetCapture% function retrieves a handle that identifies the window that
    has the mouse capture. Only one window has the mouse capture at any given
    time; this window receives mouse input whether or not the cursor is within
    its borders.

    This function has no parameters.

    The return value identifies the window that has the mouse capture; it is
    NULL if no window has the mouse capture.

    A window receives the mouse capture when its handle is passed as the <hwnd>
    parameter of the %SetCapture% function.
--*/

ULONG FASTCALL WU32GetCapture(PVDMFRAME pFrame)
{
    ULONG ul;

    UNREFERENCED_PARAMETER(pFrame);

    ul = GETHWND16(GetCapture());

    RETURN(ul);
}


/*++
    HDC GetDC(<hwnd>)
    HWND <hwnd>;

    The %GetDC% function retrieves a handle to a display context for the client
    area of the given window. The display context can be used in subsequent GDI
    functions to draw in the client area.

    The %GetDC% function retrieves a common, class, or private display context
    depending on the class style specified for the given window. For common
    display contexts, %GetDC% assigns default attributes to the context each
    time it is retrieved. For class and private contexts, %GetDC% leaves the
    previously assigned attributes unchanged.

    <hwnd>
        Identifies the window whose display context is to be retrieved.

    The return value identifies the display context for the given window's
    client area if the function is successful. Otherwise, it is NULL.

    After painting with a common display context, the %ReleaseDC% function must
    be called to release the context. Class and private display contexts do not
    have to be released. Since only five common display contexts are available
    at any given time, failure to release a display context can prevent other
    applications from accessing a display context.
--*/

ULONG FASTCALL WU32GetDC(PVDMFRAME pFrame)
{
    ULONG ul;
    register PGETDC16 parg16;
    HAND16 htask16 = pFrame->wTDB;

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

    if (CACHENOTEMPTY()) {
        ReleaseCachedDCs(htask16, parg16->f1, 0, 0, SRCHDC_TASK16_HWND16);
    }

    CURRENTPTD()->ulLastDesktophDC = 0;
    
    ul = GETHDC16(GetDC(
    HWND32(parg16->f1)
    ));

    if (ul) {
// Some apps such as MSWORKS and MS PUBLISHER use some wizard code that accepts
// a hDC or a hWnd as a parameter and attempt to figure out what type of handle
// it is by using the IsWindow() call. Since both handles come from different
// handle spaces they may end up the same value and this wizard code will end
// up writing to the DC for a random window. By ORing in a 1 we ensure that the
// handle types will never share the same value since all hWnds are even. Note
// that this hack is also made in WG32CreateCompatibleDC()
//
// Note that there are some apps that use the lower 2 bits of the hDC for their
// own purposes.
        if (CURRENTPTD()->dwWOWCompatFlags & WOWCF_UNIQUEHDCHWND) {
            ul = ul | 1;
        } else if ((CURRENTPTD()->dwWOWCompatFlagsEx & WOWCFEX_FIXDCFONT4MENUSIZE) &&
	          (parg16->f1 == 0)) {
// WP tutorial assumes that the font selected in the hDC for desktop window
// (ie, result of GetDC(NULL)) is the same font as the font selected for 
// drawing the menu. Unfortunetly in SUR this is not true as the user can
// select any font for the menu. So we remember the hDC returned for GetDC(0)
// and check for it in GetTextExtentPoint. If the app does try to use it we
// find the hDC for the current menu window and substitute that. When the app
// does another GetDC or ReleaseDC we forget the hDC returned for the original
// GetDC(0).
            CURRENTPTD()->ulLastDesktophDC = ul;
        }
	
	
	
        StoreDC(htask16, parg16->f1, (HAND16)ul);
    }

    FREEARGPTR(parg16);
    RETURN(ul);
}


/*++
    WORD GetDoubleClickTime(VOID)

    The %GetDoubleClickTime% function retrieves the current double-click time
    for the mouse. A double-click is a series of two clicks of the mouse button,
    the second occurring within a specified time after the first. The
    double-click time is the maximum number of milliseconds that may occur
    between the first and second click of a double-click.

    This function has no parameters.

    The return value specifies the current double-click time (in milliseconds).
--*/

ULONG FASTCALL WU32GetDoubleClickTime(PVDMFRAME pFrame)
{
    ULONG ul;

    UNREFERENCED_PARAMETER(pFrame);

    ul = GETWORD16(GetDoubleClickTime());

    RETURN(ul);
}


/*++
    HWND GetFocus(VOID)

    The %GetFocus% function retrieves the handle of the window that currently
    owns the input focus.

    This function has no parameters.

    The return value identifies the window that currently owns the focus if the
    function is successful. Otherwise, it is NULL.
--*/

ULONG FASTCALL WU32GetFocus(PVDMFRAME pFrame)
{
    ULONG ul;

    UNREFERENCED_PARAMETER(pFrame);

    ul = GETHWND16(GetFocus());

    RETURN(ul);
}


/*++
    BOOL GetInputState(VOID)

    The %GetInputState% function determines whether there are mouse, keyboard,
    or timer events in the system queue that require processing. An event is a
    record that describes interrupt-level input. Mouse events occur when a user
    moves the mouse or clicks a mouse button. Keyboard events occur when a user
    presses one or more keys. Timer events occur after a specified number of
    clock ticks. The system queue is the location in which Windows stores mouse,
    keyboard, and timer events.

    This function has no parameters.

    The return value specifies whether mouse, keyboard or timer input occurs. It
    is TRUE if input is detected. Otherwise, it is FALSE.
--*/

ULONG FASTCALL WU32GetInputState(PVDMFRAME pFrame)
{
    ULONG ul;

    UNREFERENCED_PARAMETER(pFrame);

    ul = GETBOOL16(GetInputState());

    RETURN(ul);
}


/*++
    HWND GetLastActivePopup(<hwndOwner>)
    HWND <hwndOwner>;

    The %GetLastActivePopup% function determines which pop-up window owned by
    the window identified by the <hwndOwner> parameter was most recently active.

    <hwndOwner>
        Identifies the owner window.

    The return value identifies the most-recently active pop-up window. The
    return value will be <hwndOwner> if any of the following conditions are
    met:

    o   The window identified by <hwndOwner> itself was most recently active.

    o   The window identified by <hwndOwner> does not own any pop-up windows.

    o   The window identified by <hwndOwner> is not a top-level window or is
        owned by another window.
--*/

ULONG FASTCALL WU32GetLastActivePopup(PVDMFRAME pFrame)
{
    ULONG ul;
    register PGETLASTACTIVEPOPUP16 parg16;

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

    ul = GETHWND16(GetLastActivePopup(HWND32(parg16->f1)));

    FREEARGPTR(parg16);
    RETURN(ul);
}


/*++
    HANDLE GetProp(<hwnd>, <lpString>)
    HWND <hwnd>;
    LPSTR <lpString>;

    The %GetProp% function retrieves a data handle from the property list of the
    specified window. The character string pointed to by the <lpString>
    parameter identifies the handle to be retrieved. The string and handle are
    assumed to have been added to the property list by using the %SetProp%
    function.

    <hwnd>
        Identifies the window whose property list is to be searched.

    <lpString>
        Points to a null-terminated string or an atom that identifies a string.
        If an atom is given, it must have been created previously by using the
        %AddAtom% function. The atom, a 16-bit value, must be placed in the
        low-order word of the <lpString> parameter; the high-order word must be
        set to zero.

    The return value identifies the associated data handle if the property list
    contains the given string. Otherwise, it is NULL.

    The value retrieved by the %GetProp% function can be any 16-bit value useful
    to the application.
--*/

ULONG FASTCALL WU32GetProp(PVDMFRAME pFrame)
{
    ULONG ul;
    PSZ psz2;
    register PGETPROP16 parg16;

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

    ul = (HAND16)GetProp(
    HWND32(parg16->f1),
    psz2
    );

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


/*++
    int GetScrollPos(<hwnd>, <nBar>)
    HWND <hwnd>;
    int <nBar>;

    The %GetScrollPos% function retrieves the current position of a scroll-bar
    thumb. The current position is a relative value that depends on the current
    scrolling range. For example, if the scrolling range is 0 to 100 and the
    thumb is in the middle of the bar, the current position is 50.

    <hwnd>
        Identifies a window that has standard scroll bars or a scroll-bar
        control, depending on the value of the nBar parameter.

    <nBar>
        Specifies the scroll bar to examine. It can be one of the
        following values:

    SB_CTL
        Retrieves the position of a scroll-bar control. In this case, the hwnd
        parameter must be the window handle of a scroll-bar control.

    SB_HORZ
        Retrieves the position of a window's horizontal scroll bar.

    SB_VERT
        Retrieves the position of a window's vertical scroll bar.

    The return value specifies the current position of the scroll-bar thumb.
--*/

ULONG FASTCALL WU32GetScrollPos(PVDMFRAME pFrame)
{
    ULONG ul;
    register PGETSCROLLPOS16 parg16;

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

    ul = GETINT16(GetScrollPos(
    HWND32(parg16->f1),
    INT32(parg16->f2)
    ));

    FREEARGPTR(parg16);
    RETURN(ul);
}


/*++
    void GetScrollRange(<hwnd>, <nBar>, <lpMinPos>, <lpMaxPos>)
    HWND <hwnd>;
    int <nBar>;
    LPINT <lpMinPos>;
    LPINT <lpMaxPos>;

    The %GetScrollRange% function copies the current minimum and maximum
    scroll-bar positions for the given scroll bar to the locations specified by
    the <lpMinPos> and <lpMaxPos> parameters. If the given window does not have
    standard scroll bars or is not a scroll-bar control, then the
    %GetScrollRange% function copies zero to <lpMinPos> and <lpMaxPos>.

    <hwnd>
        Identifies a window that has standard scroll bars or a scroll-bar
        control, depending on the value of the nBar parameter.

    <nBar>
        Specifies an integer value that identifies which scroll bar to
        retrieve. It can be one of the following values:

    SB_CTL
        Retrieves the position of a scroll-bar control; in this case, the hwnd
        parameter must be the handle of a scroll-bar control.

    SB_HORZ
        Retrieves the position of a window's horizontal scroll bar.

    SB_VERT
        Retrieves the position of a window's vertical scroll bar.

    <lpMinPos>
        Points to the integer variable that is to receive the minimum
        position.

    <lpMaxPos>
        Points to the integer variable that is to receive the maximum
        position.

    This function does not return a value.

    The default range for a standard scroll bar is 0 to 100. The default range
    for a scroll-bar control is empty (both values are zero).
--*/

ULONG FASTCALL WU32GetScrollRange(PVDMFRAME pFrame)
{
    INT t3;
    INT t4;
    register PGETSCROLLRANGE16 parg16;

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

    GetScrollRange(
    HWND32(parg16->f1),
    INT32(parg16->f2),
    &t3,
    &t4
    );

    PUTINT16(parg16->f3, t3);
    PUTINT16(parg16->f4, t4);
    FREEARGPTR(parg16);
    RETURN(0);
}


/*++
    ULONG GetTimerResolution(VOID)


    This function has no parameters.

    The Win 3.0 & 3.1 code just return 1000.

    Contacts on this: NeilK DarrinM

    The return value is always 1000.

--*/

ULONG FASTCALL WU32GetTimerResolution(PVDMFRAME pFrame)
{

    UNREFERENCED_PARAMETER(pFrame);

    RETURN(1000L);
}


/*++
    BOOL GetUpdateRect(<hwnd>, <lpRect>, <bErase>)
    HWND <hwnd>;
    LPRECT <lpRect>;
    BOOL <bErase>;

    The %GetUpdateRect% function retrieves the coordinates of the smallest
    rectangle that completely encloses the update region of the given window. If
    the window was created with the CS_OWNDC style and the mapping mode is not
    MM_TEXT, the %GetUpdateRect% function gives the rectangle in logical
    coordinates. Otherwise, %GetUpdateRect% gives the rectangle in client
    coordinates. If there is no update region, %GetUpdateRect% makes the
    rectangle empty (sets all coordinates to zero).

    The <bErase> parameter specifies whether %GetUpdateRect% should erase the
    background of the update region. If <bErase> is TRUE and the update region
    is not empty, the background is erased. To erase the background,
    %GetUpdateRect% sends a WM_ERASEBKGND message to the given window.

    <hwnd>
        Identifies the window whose update region is to be retrieved.

    <lpRect>
        Points to the %RECT% structure that is to receive the
        client coordinates of the enclosing rectangle.

    <bErase>
        Specifies whether the background in the update region is to be
        erased.

    The return value specifies the status of the update region of the given
    window. It is TRUE if the update region is not empty. Otherwise, it is
    FALSE.

    The update rectangle retrieved by the %BeginPaint% function is identical to
    that retrieved by the %GetUpdateRect% function.

    %BeginPaint% automatically validates the update region, so any call to
    %GetUpdateRect% made immediately after the %BeginPaint% call retrieves an
    empty update region.
--*/

ULONG FASTCALL WU32GetUpdateRect(PVDMFRAME pFrame)
{
    ULONG ul;
    RECT t2;
    register PGETUPDATERECT16 parg16;

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

    ul = GETBOOL16(GetUpdateRect(
    HWND32(parg16->f1),
    &t2,
    BOOL32(parg16->f3)
    ));


    PUTRECT16(parg16->f2, &t2);
    FREEARGPTR(parg16);
    RETURN(ul);
}


/*++
    int GetUpdateRgn(<hwnd>, <hRgn>, <fErase>)
    HWND <hwnd>;
    HRGN <hRgn>;
    BOOL <fErase>;

    The %GetUpdateRgn% function copies a window's update region into a region
    identified by the <hRgn> parameter. The coordinates of this region are
    relative to the upper-left corner of the window (client coordinates).

    <hwnd>
        Identifies the window that contains the region to be updated.

    <hRgn>
        Identifies the update region.

    <fErase>
        Specifies whether or not the window background should be erased
        and nonclient areas of child windows should be drawn. If it is zero, no
        drawing is done.

    The return value specifies a short-integer flag that indicates the type of
    resulting region. It can be any one of the following values:

    COMPLEXREGION   The region has overlapping borders.
    ERROR           No region was created.
    NULLREGION      The region is empty.
    SIMPLEREGION    The region has no overlapping borders.

    %BeginPaint% automatically validates the update region, so any call to
    %GetUpdateRgn% made immediately after the %BeginPaint% call retrieves an
    empty update region.
--*/

ULONG FASTCALL WU32GetUpdateRgn(PVDMFRAME pFrame)
{
    ULONG ul;
    register PGETUPDATERGN16 parg16;

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

    ul = GETINT16(GetUpdateRgn(
    HWND32(parg16->f1),
    HRGN32(parg16->f2),
    BOOL32(parg16->f3)
    ));

    FREEARGPTR(parg16);
    RETURN(ul);
}


ULONG FASTCALL WU32GlobalAddAtom(PVDMFRAME pFrame)
{
    ULONG ul;
    PSZ psz1;
    UINT dw1;
    register PGLOBALADDATOM16 parg16;

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

    dw1 = UINT32(parg16->f1);

    if (!HIWORD(dw1)) {

        //
        // If the hiword is zero, it's not a pointer.
        // Instead, it's an integer and we either return
        // the integer passed (if it's not a valid atom
        // value), or zero (if it is a valid atom value).
        //

        if (!dw1 || dw1 >= 0xc000) {
            ul = 0;
        } else {
            ul = dw1;
        }

    } else {

        GETPSZPTR(parg16->f1, psz1);

        ul = GETATOM16(GlobalAddAtom(
                 psz1
                 ));

        FREEPSZPTR(psz1);

    }

    FREEARGPTR(parg16);
    RETURN(ul);
}


ULONG FASTCALL WU32GlobalDeleteAtom(PVDMFRAME pFrame)
{

    // Envoy viewer (part of PerfectOffice) has a bug in GlobalDeleteAtom
    // where it expects the wrong return value (the app thought 0 was
    // failure while its for success). This causes the app to go in an
    // infinite loop trying to delete a global object. This app works on
    // Win3.1 because Win3.1 returns some garbage in AX if the atom is
    // already deleted which takes this app out of the loop. On Win95 and
    // NT3.51 that is not the case and 0 is always returned. The following
    // comaptibility fix mimics the win3.1 behavior for this app.

    ULONG ul;
    static USHORT envoyHandle16=0;
    static BOOL   fFoundEnvoyAtom = FALSE;
    BOOL    IsEnvoy;
    CHAR  envoyString [32];
    register PGLOBALDELETEATOM16 parg16;

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

    IsEnvoy = (CURRENTPTD()->dwWOWCompatFlagsEx & WOWCFEX_GLOBALDELETEATOM);
    if (IsEnvoy){
        if (!fFoundEnvoyAtom && GlobalGetAtomName (ATOM32(parg16->f1),
                               envoyString,
                               32) &&
                !_stricmp (envoyString, "SomeEnvoyViewerIsRunning")) {
            envoyHandle16 = parg16->f1;
        }

    }
    ul = GETATOM16(GlobalDeleteAtom(
    ATOM32(parg16->f1)
    ));

    if (IsEnvoy){
        if (envoyHandle16 && !fFoundEnvoyAtom) {
            fFoundEnvoyAtom = TRUE;
        }
        else if (fFoundEnvoyAtom) {
            if (envoyHandle16 == parg16->f1) {
                envoyHandle16 = 0;
                fFoundEnvoyAtom = FALSE;
                ul = parg16->f1;
            }
        }
    }

    FREEARGPTR(parg16);
    RETURN(ul);
}


ULONG FASTCALL WU32GlobalFindAtom(PVDMFRAME pFrame)
{
    ULONG ul;
    PSZ psz1;
    register PGLOBALFINDATOM16 parg16;

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

    ul = GETATOM16(GlobalFindAtom(
    psz1
    ));

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


ULONG FASTCALL WU32GlobalGetAtomName(PVDMFRAME pFrame)
{
    ULONG ul;
    PSZ psz2;
    register PGLOBALGETATOMNAME16 parg16;

    GETARGPTR(pFrame, sizeof(GLOBALGETATOMNAME16), parg16);
    ALLOCVDMPTR(parg16->f2, parg16->f3, psz2);

    if (parg16->f1) {
        ul = GETWORD16(GlobalGetAtomName(ATOM32(parg16->f1),
                                         psz2,
                                         INT32(parg16->f3)));

        FLUSHVDMPTR(parg16->f2, strlen(psz2)+1, psz2);
    }
    else {
        *psz2 = '\0';
    }


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


/*++
    BOOL GrayString(<hDC>, <hBrush>, <lpOutputFunc>, <lpData>, <nCount>, <X>,
        <Y>, <nWidth>, <nHeight>)
    HDC <hDC>;
    HBRUSH <hBrush>;
    FARPROC <lpOutputFunc>;
    DWORD <lpData>;
    int <nCount>;
    int <X>;
    int <Y>;
    int <nWidth>;
    int <nHeight>;

    The %GrayString% function draws gray text at the given location. The
    %GrayString% function draws gray text by writing the text in a memory
    bitmap, graying the bitmap, and then copying the bitmap to the display. The
    function grays the text regardless of the selected brush and
    background. %GrayString% uses the font currently selected for the device
    context specified by the <hDC> parameter.

    If the <lpOutputFunc> parameter is NULL, GDI uses the %TextOut% function,
    and the <lpData> parameter is assumed to be a long pointer to the character
    string to be output. If the characters to be output cannot be handled by
    %TextOut% (for example, the string is stored as a bitmap), the application
    must supply its own output function.

    <hDC>
        Identifies the device context.

    <hBrush>
        Identifies the brush to be used for graying.

    <lpOutputFunc>
        Is the procedure-instance address of the application-supplied
        function that will draw the string, or, if the %TextOut% function is to
        be used to draw the string, it is a NULL pointer. See the following
        Comments section for details.

    <lpData>
        Specifies a long pointer to data to be passed to the output
        function. If the <lpOutputFunc> parameter is NULL, <lpData> must be a
        long pointer to the string to be output.

    <nCount>
        Specifies the number of characters to be output. If the <nCount>
        parameter is zero, %GrayString% calculates the length of the string
        (assuming that <lpData> is a pointer to the string). If <nCount> is -1
        and the function pointed to by <lpOutputFunc> returns zero, the image is
        shown but not grayed.

    <X>
        Specifies the logical <x>-coordinate of the starting position of
        the rectangle that encloses the string.

    <Y>
        Specifies the logical <y>-coordinate of the starting position of
        the rectangle that encloses the string.

    <nWidth>
        Specifies the width (in logical units) of the rectangle that
        encloses the string. If the <nWidth> parameter is zero, %GrayString%
        calculates the width of the area, assuming <lpData> is a pointer to the
        string.

    <nHeight>
        Specifies the height (in logical units) of the rectangle that
        encloses the string. If the <nHeight> parameter is zero, %GrayString%
        calculates the height of the area, assuming <lpData> is a pointer to the
        string.


    The return value specifies the outcome of the function. It is TRUE if the
    string is drawn. A return value of FALSE means that either the %TextOut%
    function or the application-supplied output function returned FALSE, or
    there was insufficient memory to create a memory bitmap for graying.

    An application can draw grayed strings on devices that support a solid gray
    color, without calling the %GrayString% function. The system color
    COLOR_GRAYTEXT is the solid-gray system color used to draw disabled text.
    The application can call the %GetSysColor% function to retrieve the color
    value of COLOR_GRAYTEXT. If the color is other than zero (black), the
    application can call the %SetTextColor% to set the text color to the color
    value and then draw the string directly. If the retrieved color is black,
    the application must call %GrayString% to gray the text.

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

    Callback Function:

    BOOL FAR PASCAL <OutputFunc>(<hDC>, <lpData>, <nCount>)
    HDC <hDC>;
    DWORD <lpData>;
    int <nCount>;

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

    <hDC>
        Identifies a memory device context with a bitmap of at least the width
        and height specified by the nWidth and nHeight parameters,
        respectively.

    <lpData>
        Points to the character string to be drawn.

    <nCount>
        Specifies the number of characters to be output.

    The return value must be TRUE to indicate success. Otherwise, it is FALSE.

    This output function (<OutputFunc>) must draw an image relative to the
    coordinates (0,0) rather than (<X,Y>). The address passed as the
    <lpOutputFunc> parameter must be created by using the %MakeProcInstance%
    function, and the output function name must be exported; it must be
    explicitly defined in an %EXPORTS% statement of the application's
    module-definition file.

    The MM_TEXT mapping mode must be selected before using this function.
--*/

BOOL W32GrayStringProc(HDC hDC,PGRAYSTRINGDATA pGray, int n) {
    INT iReturn;
    PARM16 Parm16;

    WOW32ASSERT(pGray);

    if (pGray->fResetLengthToZero)
        n = 0;

    LOGDEBUG(12,("    Graystring callback function, n = %d, hdc = %lx, %lx\n",n,hDC,pGray->dwUserParam));

    Parm16.GrayStringProc.n = (SHORT)n;
    Parm16.GrayStringProc.data = pGray->dwUserParam;
    pGray->hdc=Parm16.GrayStringProc.hdc = GETHDC16(hDC);
    CallBack16(RET_GRAYSTRINGPROC, &Parm16, pGray->vpfnGrayStringProc, (PVPVOID)&iReturn);

    LOGDEBUG(12,("    Graystring callback function returns %x\n",iReturn));
    return (BOOL)((SHORT)iReturn);
}

ULONG FASTCALL WU32GrayString(PVDMFRAME pFrame)
{
    ULONG          ul=0;
    PSZ            psz2;
    HDC            hdc;
    INT            n,wid,hgt;
    VPVOID         vpfn;
    VPVOID         vpstr;
    GRAYSTRINGDATA Gray;
    register PGRAYSTRING16 parg16;

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

    hdc=HDC32(parg16->f1);

    vpfn = DWORD32(parg16->f3);

    vpstr = DWORD32(parg16->f4);

    n=INT32(parg16->f5);

    wid=INT32(parg16->f8);
    hgt=INT32(parg16->f9);


    if ( HIWORD(vpfn) ) {       // SQLWin/repowin passes junk in low word

        Gray.fResetLengthToZero = FALSE;

        if( n==0 ) {

            n = 1;              // Prevent USER from doing strlen on &Gray below

            if ( HIWORD(vpstr) != 0 ) {  // Blow off small integers right away

                GETVDMPTR(vpstr, 0, psz2); // This might assert on mips, ignore it!

                if ( psz2 ) {
                    try {
                        n = strlen(psz2);
                        if (!n) {
                            n = 1;
                            Gray.fResetLengthToZero = TRUE;
                        }

                    } except( EXCEPTION_EXECUTE_HANDLER ) {
                    }
                }

                FREEVDMPTR( psz2 );
            }
        }

        if ( wid == 0 || hgt == 0) {
            if ( HIWORD(vpstr) != 0 ) {
                GETVDMPTR(vpstr, 0, psz2); // This might assert on mips, ignore it!

                if (psz2) {
                    SIZE size;

                    try {
                        GetTextExtentPointA(hdc, (LPCSTR)psz2, n, &size);
                        wid = size.cx;
                        hgt = size.cy;
                    } except (EXCEPTION_EXECUTE_HANDLER) {
                    }
                }

                FREEVDMPTR( psz2 );
            }
        }

        Gray.vpfnGrayStringProc = DWORD32(parg16->f3);
        Gray.dwUserParam        = vpstr;
        Gray.hdc = 0;

        LOGDEBUG(12,("    Graystring with callback %lx  n,w,h = %d,%d,%d\n",
                 vpstr,n,wid,hgt));


        ul = GETBOOL16(GrayString(hdc,
                                  HBRUSH32(parg16->f2),
                                  (GRAYSTRINGPROC)W32GrayStringProc,
                                  (DWORD)&Gray,
                                  n,
                                  INT32(parg16->f6),
                                  INT32(parg16->f7),
                                  wid,
                                  hgt));

        if( Gray.hdc ) {
            FREEHOBJ16(Gray.hdc);
        }

    } else {

       GETPSZPTR(vpstr, psz2);

#ifdef DOESNT_USER_DO_THIS
        if( n==0 ) {
            n=strlen(psz2);
        }
        if( ((wid == 0) || (hgt == 0)) ) {
            GetTextExtentPoint(hdc,psz2,n,&sz);

            wid=sz.cx;
            hgt=sz.cy;
        }
#endif
        ul = GETBOOL16(GrayString(hdc,
                                  HBRUSH32(parg16->f2),
                                  NULL,
                                  (DWORD)psz2,
                                  n,
                                  INT32(parg16->f6),
                                  INT32(parg16->f7),
                                  wid,
                                  hgt));

        FREEPSZPTR(psz2);
    }

    FREEARGPTR(parg16);
    RETURN(ul);
}



/*++
    void InvalidateRect(<hwnd>, <lpRect>, <bErase>)
    HWND <hwnd>;
    LPRECT <lpRect>;
    BOOL <bErase>;

    The %InvalidateRect% function invalidates the client area within the given
    rectangle by adding that rectangle to the window's update region. The
    invalidated rectangle, along with all other areas in the update region, is
    marked for painting when the next WM_PAINT message occurs. The invalidated
    areas accumulate in the update region until the region is processed when the
    next WM_PAINT message occurs, or the region is validated by using the
    %ValidateRect% or %ValidateRgn% function.

    The <bErase> parameter specifies whether the background within the update
    area is to be erased when the update region is processed. If <bErase> is
    TRUE, the background is erased when the %BeginPaint% function is called;
    if <bErase> is FALSE, the background remains unchanged. If <bErase> is
    TRUE for any part of the update region, the background in the entire
    region is erased, not just in the given part.

    <hwnd>
        Identifies the window whose update region is to be modified.

    <lpRect>
        Points to a %RECT% structure that contains the rectangle
        (in client coordinates) to be added to the update region. If the
        <lpRect> parameter is NULL, the entire client area is added to the
        region.

    <bErase>
        Specifies whether the background within the update region is to
        be erased.

    This function does not return a value.

    Windows sends a WM_PAINT message to a window whenever its update region is
    not empty and there are no other messages in the application queue for that
    window.
--*/

ULONG FASTCALL WU32InvalidateRect(PVDMFRAME pFrame)
{
    RECT t2, *p2;
    register PINVALIDATERECT16 parg16;

    GETARGPTR(pFrame, sizeof(INVALIDATERECT16), parg16);
    p2 = GETRECT16(parg16->f2, &t2);

    InvalidateRect(
    HWND32(parg16->f1),
    p2,
    BOOL32(parg16->f3)
    );

    FREEARGPTR(parg16);
    RETURN(1);    // Win 3.x always returned 1 as a side-effect of jmping to
                  // IRedrawWindow [core\user\wmupdate.c] - MarkRi 5/93
}


/*++
    void InvalidateRgn(<hwnd>, <hRgn>, <bErase>)
    HWND <hwnd>;
    HRGN <hRgn>;
    BOOL <bErase>;

    The %InvalidateRgn% function invalidates the client area within the given
    region by adding it to the current update region of the given window. The
    invalidated region, along with all other areas in the update region, is
    marked for painting when the next WM_PAINT message occurs. The invalidated
    areas accumulate in the update region until the region is processed when the
    next WM_PAINT message occurs, or the region is validated by using the
    %ValidateRect% or %ValidateRgn% function.

    The <bErase> parameter specifies whether the background within the update
    area is to be erased when the update region is processed. If <bErase> is
    TRUE, the background is erased when the %BeginPaint% function is called; if
    <bErase> is FALSE, the background remains unchanged. If <bErase> is TRUE for
    any part of the update region, the background in the entire region is
    erased, not just in the given part.

    <hwnd>
        Identifies the window whose update region is to be modified.

    <hRgn>
        Identifies the region to be added to the update region. The
        region is assumed to have client coordinates.

    <bErase>
        Specifies whether the background within the update region is to
        be erased.

    This function does not return a value.

    Windows sends a WM_PAINT message to a window whenever its update region is
    not empty and there are no other messages in the application queue for that
    window.

    The given region must have been previously created by using one of the
    region functions (for more information, see Chapter 1, Window Manager
    Interface Functions).
--*/

ULONG FASTCALL WU32InvalidateRgn(PVDMFRAME pFrame)
{
    register PINVALIDATERGN16 parg16;

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

    InvalidateRgn(
    HWND32(parg16->f1),
    HRGN32(parg16->f2),
    BOOL32(parg16->f3)
    );

    FREEARGPTR(parg16);
    RETURN(1);    // Win 3.x always returned 1 as a side-effect of jmping to
                  // IRedrawWindow [core\user\wmupdate.c] - MarkRi 5/93
}


/*++
    void InvertRect(<hDC>, <lpRect>)
    HDC <hDC>;
    LPRECT <lpRect>;

    The %InvertRect% function inverts the contents of the given rectangle. On
    monochrome displays, the %InvertRect% function makes white pixels black, and
    black pixels white. On color displays, the inversion depends on how colors
    are generated for the display. Calling %InvertRect% twice with the same
    rectangle restores the display to its previous colors.

    <hDC>
        Identifies the device context.

    <lpRect>
        Points to a %RECT% structure that contains the logical coordinates of
        the rectangle to be inverted.

    This function does not return a value.

    The %InvertRect% function compares the values of the %top%, %bottom%,
    %left%, and %right% members of the specified rectangle. If %bottom% is less
    than or equal to %top%, or if %right% is less than or equal to %left%, the
    rectangle is not drawn.
--*/

ULONG FASTCALL WU32InvertRect(PVDMFRAME pFrame)
{
    RECT t2;
    register PINVERTRECT16 parg16;

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

    WOW32VERIFY(GETRECT16(parg16->f2, &t2));

    InvertRect(
    HDC32(parg16->f1),
    &t2
    );

    FREEARGPTR(parg16);
    RETURN(0);
}


ULONG FASTCALL WU32LoadBitmap(PVDMFRAME pFrame)
{
    ULONG ul = 0;
    PSZ psz2;
    register PLOADBITMAP16 parg16;
    LPBYTE pResData = NULL;

    GETARGPTR(pFrame, sizeof(LOADBITMAP16), parg16);
    GETPSZIDPTR(parg16->f2, psz2);
    GETMISCPTR(parg16->f3, pResData);

    ul = GETHBITMAP16((pfnOut.pfnWOWLoadBitmapA)(HINSTRES32(parg16->f1),
                                     psz2,
                                     pResData,
                                     parg16->f4));

    FREEMISCPTR(pResData);
    FREEPSZIDPTR(psz2);
    FREEARGPTR(parg16);
    RETURN(ul);
}


ULONG FASTCALL WU32WOWGetIdFromDirectory(PVDMFRAME pFrame)
{
    ULONG ul;
    LPBYTE pResData = NULL;
    register PWOWGETIDFROMDIRECTORY16 parg16;

    GETARGPTR(pFrame, sizeof(WOWGETIDFROMDIRECTORY16), parg16);
    GETMISCPTR (parg16->f1, pResData);

    ul = (ULONG) MAKEINTRESOURCE((pfnOut.pfnWOWGetIdFromDirectory)(pResData,
                parg16->f2));     // RT_ICON or CURSOR


    FREEMISCPTR(pResData);
    FREEARGPTR(parg16);
    RETURN(ul);
}



/*++
    void MessageBeep(<wType>)
    WORD <wType>;

    The %MessageBeep% function generates a beep at the system speaker.

    <wType>
        Is not used. It should be set to zero.

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

ULONG FASTCALL WU32MessageBeep(PVDMFRAME pFrame)
{
    register PMESSAGEBEEP16 parg16;

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

    MessageBeep(
    WORD32(parg16->f1)
    );

    FREEARGPTR(parg16);
    RETURN(0);
}


/*++
    BOOL OpenIcon(<hwnd>)
    HWND <hwnd>;

    The %OpenIcon% function activates and displays an iconic (minimized)
    window. Windows restores it to its original size and position.

    <hwnd>
        Identifies the window.

    The return value specifies the outcome of the function. It is TRUE if the
    function is successful. Otherwise, it is FALSE.
--*/

ULONG FASTCALL WU32OpenIcon(PVDMFRAME pFrame)
{
    ULONG ul;
    register POPENICON16 parg16;

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

    ul = GETBOOL16(OpenIcon(
    HWND32(parg16->f1)
    ));

    FREEARGPTR(parg16);
    RETURN(ul);
}


/*++
    void ReleaseCapture(VOID)

    The %ReleaseCapture% function releases the mouse capture and restores normal
    input processing. A window with the mouse capture receives all mouse input
    regardless of the position of the cursor.

    This function has no parameters.

    This function does not return a value.

    An application calls this function after calling the %SetCapture% function.
--*/

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

    ReleaseCapture();

    RETURN(0);
}


/*++
    int ReleaseDC(<hwnd>, <hDC>)
    HWND <hwnd>;
    HDC <hDC>;

    The %ReleaseDC% function releases a device context, freeing it for use by
    other applications. The effect of the %ReleaseDC% function depends on the
    device-context type. It only frees common and window device contexts. It has
    no effect on class or private device contexts.

    <hwnd>
        Identifies the window whose device context is to be released.

    <hDC>
        Identifies the device context to be released.

    The return value specifies whether the device context is released. It is 1
    if the device context is released. Otherwise, it is zero.

    The application must call the %ReleaseDC% function for each call to the
    %GetWindowDC% function and for each call to the %GetDC% function that
    retrieves a common device context.
--*/

ULONG FASTCALL WU32ReleaseDC(PVDMFRAME pFrame)
{
    ULONG ul;
    register PRELEASEDC16 parg16;
    HAND16 htask16 = CURRENTPTD()->htask16;

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

    CURRENTPTD()->ulLastDesktophDC = 0;

    CacheReleasedDC(htask16, parg16->f1, parg16->f2);
    ul = TRUE;

    FREEARGPTR(parg16);
    RETURN(ul);
}


/*++
    HANDLE RemoveProp(<hwnd>, <lpString>)
    HWND <hwnd>;
    LPSTR <lpString>;

    The %RemoveProp% function removes an entry from the property list of the
    specified window. The character string specified by the <lpString> parameter
    identifies the entry to be removed.

    The %RemoveProp% function returns the data handle associated with the string
    so that the application can free the data associated with the handle.

    <hwnd>
        Identifies the window whose property list is to be changed.

    <lpString>
        Points to a null-terminated string or to an atom that identifies a
        string. If an atom is given, it must have been previously created by
        means of the %AddAtom% function. The atom, a 16-bit value, must be
        placed in the low-order word of <lpString>; the high-order word must be
        zero.

    The return value identifies the given string. It is NULL if the string
    cannot be found in the given property list.

    An application must free the data handles associated with entries removed
    from a property list. The application should only remove those properties
    which it added to the property list.
--*/

ULONG FASTCALL WU32RemoveProp(PVDMFRAME pFrame)
{
    ULONG ul;
    PSZ psz2;
    register PREMOVEPROP16 parg16;

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

    ul = (HAND16)RemoveProp(
    HWND32(parg16->f1),
    psz2
    );

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


/*++
    BOOL ScrollDC(<hDC>, <dx>, <dy>, <lprcScroll>, <lprcClip>, <hrgnUpdate>,
        <lprcUpdate>)
    HDC <hDC>;
    int <dx>;
    int <dy>;
    LPRECT <lprcScroll>;
    LPRECT <lprcClip>;
    HRGN <hrgnUpdate>;
    LPRECT <lprcUpdate>;

    The %ScrollDC% function scrolls a rectangle of bits horizontally and
    vertically. The <lprcScroll> parameter points to the rectangle to be
    scrolled, the <dx> parameter specifies the number of units to be scrolled
    horizontally, and the <dy> parameter specifies the number of units to be
    scrolled vertically.

    <hDC>
        Identifies the device context that contains the bits to be scrolled.

    <dx>
        Specifies the number of horizontal scroll units.

    <dy>
        Specifies the number of vertical scroll units.

    <lprcScroll>
        Points to the %RECT% structure that contains the
        coordinates of the scrolling rectangle.

    <lprcClip>
        Points to the %RECT% structure that contains the
        coordinates of the clipping rectangle. When this rectangle is smaller
        than the original pointed to by <lprcScroll>, scrolling occurs only in
        the smaller rectangle.

    <hrgnUpdate>
        Identifies the region uncovered by the scrolling process. The
        %ScrollDC% function defines this region; it is not necessarily a
        rectangle.

    <lprcUpdate>
        Points to the %RECT% structure that, upon return, contains
        the coordinates of the rectangle that bounds the scrolling update
        region. This is the largest rectangular area that requires repainting.

    This value specifies the outcome of the function. It is TRUE if scrolling is
    executed. Otherwise, it is FALSE.

    If the <lprcUpdate> parameter is NULL, Windows does not compute the update
    rectangle. If both the <hrgnUpdate> and <lprcUpdate> parameters are NULL,
    Windows does not compute the update region. If <hrgnUpdate> is not NULL,
    Windows assumes that it contains a valid region handle to the region
    uncovered by the scrolling process (defined by the %ScrollDC% function).

    An application should use the %ScrollWindow% function when it is necessary
    to scroll the entire client area of a window. Otherwise, it should use
    %ScrollDC%.
--*/

ULONG FASTCALL WU32ScrollDC(PVDMFRAME pFrame)
{
    ULONG ul;
    RECT t4;
    RECT t5;
    RECT t7;
    register PSCROLLDC16 parg16;

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

    ul = GETBOOL16(ScrollDC(
    HDC32(parg16->f1),
    INT32(parg16->f2),
    INT32(parg16->f3),
    GETRECT16(parg16->f4, &t4),
    GETRECT16(parg16->f5, &t5),
    HRGN32(parg16->f6),
    &t7
    ));

    PUTRECT16(parg16->f7, &t7);
    FREEARGPTR(parg16);
    RETURN(ul);
}


/*++
    HWND SetCapture(<hwnd>)
    HWND <hwnd>;

    The %SetCapture% function causes all subsequent mouse input to be sent to
    the window specified by the <hwnd> parameter, regardless of the position of
    the cursor.

    <hwnd>
        Identifies the window that is to receive the mouse input.

    The return value identifies the window that previously received all mouse
    input. It is NULL if there is no such window.

    When the window no longer requires all mouse input, the application should
    call the %ReleaseCapture% function so that other windows can receive mouse
    input.
--*/

ULONG FASTCALL WU32SetCapture(PVDMFRAME pFrame)
{
    ULONG ul;
    register PSETCAPTURE16 parg16;

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

    // MS Works Ver 3.0B has an unintialized local variable. We need to make
    // sure it see's a positive int value in the location on the stack where we
    // write the 32-bit thunk address for fast dispatching to this thunk.

    if (CURRENTPTD()->dwWOWCompatFlagsEx & WOWCFEX_SETCAPSTACK) {
        // wCallID has already been used for dispatch so we can overwrite it.
        pFrame->wCallID = 0x100;
    }

    ul = GETHWND16(SetCapture(HWND32(parg16->f1)));

    FREEARGPTR(parg16);
    RETURN(ul);
}


/*++
    void SetDoubleClickTime(<wCount>)
    WORD <wCount>;

    The %SetDoubleClickTime% function sets the double-click time for the
    mouse. A double-click is a series of two clicks of the mouse button, the
    second occurring within a specified time after the first. The double-click
    time is the maximum number of milliseconds that may occur between the first
    and second clicks of a double-click.

    <wCount>
        Specifies the number of milliseconds that can occur between
        double-clicks.

    This function does not return a value.

    If the <wCount> parameter is set to zero, Windows will use the default
    double-click time of 500 milliseconds.

    The %SetDoubleClickTime% function alters the double-click time for all
    windows in the system.
--*/

ULONG FASTCALL WU32SetDoubleClickTime(PVDMFRAME pFrame)
{
    register PSETDOUBLECLICKTIME16 parg16;

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

    SetDoubleClickTime(
    WORD32(parg16->f1)
    );

    FREEARGPTR(parg16);
    RETURN(0);
}

ULONG FASTCALL WU32SetEventHook(PVDMFRAME pFrame)
{
    PTD     ptd;
    PTDB    pTDB;
    DWORD   dwButtonPushed;
    CHAR    szErrorMessage[200];
    char    szModName[9];
    char    szTitle[100];
    register PSETEVENTHOOK16 parg16;

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

    // Retail Build

    ptd = CURRENTPTD();
    if (ptd->dwFlags & TDF_FORCETASKEXIT) {
        goto SetEventHookExit;
    }

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

    RtlZeroMemory(szModName, sizeof(szModName));
    RtlCopyMemory(szModName, pTDB->TDB_ModName, sizeof(szModName)-1);

    if (!LoadString(hmodWOW32, iszEventHook,
                    szErrorMessage, sizeof(szErrorMessage)/sizeof(CHAR)))
    {
        szErrorMessage[0] = 0;
    }
    if (!LoadString(hmodWOW32, iszApplication,
                    szTitle, sizeof(szTitle)/sizeof(CHAR)))
    {
        szTitle[0] = 0;
    }
    strcat(szTitle, szModName);

    dwButtonPushed = WOWSysErrorBox(
            szTitle,
            szErrorMessage,
            SEB_CLOSE | SEB_DEFBUTTON,
            0,
            SEB_IGNORE
            );

    if (dwButtonPushed != 3) {
        //
        // If user typed Cancel or Any of the above fail,
        // force the task to die.
        //

        GETFRAMEPTR(ptd->vpStack, pFrame);
        pFrame->wRetID = RET_FORCETASKEXIT;

        ptd->dwFlags |= TDF_FORCETASKEXIT;
    }

SetEventHookExit:
    FREEARGPTR(parg16);
    RETURN(0);
}


/*++
    HWND SetFocus(<hwnd>)
    HWND <hwnd>;

    The %SetFocus% function assigns the input focus to the window specified by
    the <hwnd> parameter. The input focus directs all subsequent keyboard input
    to the given window. The window, if any, that previously had the input focus
    loses it. If <hwnd> is NULL, key strokes are ignored.

    The %SetFocus% function sends a WM_KILLFOCUS message to the window that
    loses the input focus and a WM_SETFOCUS message to the window that receives
    the input focus. It also activates either the window that receives the focus
    or the parent of the window that receives the focus.

    <hwnd>
        Identifies the window to receive the keyboard input.

    The return value identifies the window that previously had the input focus.
    It is NULL if there is no such window.

    If a window is active but doesn't have the focus (that is, no window has the
    focus), any key pressed will produce the WM_SYSCHAR, WM_SYSKEYDOWN, or
    WM_SYSKEYUP message. If the VK_MENU key is also pressed, the <lParam>
    parameter of the message will have bit 30 set. Otherwise, the messages that
    are produced do <not> have this bit set.
--*/

ULONG FASTCALL WU32SetFocus(PVDMFRAME pFrame)
{
    ULONG ul;
    register PSETFOCUS16 parg16;

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

    ul = GETHWND16(SetFocus(HWND32(parg16->f1)));

    FREEARGPTR(parg16);
    RETURN(ul);
}


/*++
    void SetKeyboardState(<lpKeyState>)
    LPBYTE <lpKeyState>;

    The %SetKeyboardState% function copies the 256 bytes pointed to by the
    <lpKeyState> parameter into the Windows keyboard-state table.

    <lpKeyState>
        Points to an array of 256 bytes that contains keyboard key states.

    This function does not return a value.

    In many cases, an application should call the %GetKeyboardState% function
    first to initialize the 256-byte array. The application should then change
    the desired bytes.

    %SetKeyboardState% sets the LEDs and BIOS flags for the ^NUMLOCK^,
    ^CAPSLOCK^, and ^SCROLL LOCK^ keys according to the toggle state of the
    VK_NUMLOCK, VK_CAPITAL, and VK_OEM_SCROLL entries of the array.

    For more information, see the description of %GetKeyboardState%, earlier in
    this chapter.
--*/

ULONG FASTCALL WU32SetKeyboardState(PVDMFRAME pFrame)
{
    PBYTE p1;
    register PSETKEYBOARDSTATE16 parg16;

    GETARGPTR(pFrame, sizeof(SETKEYBOARDSTATE16), parg16);
    GETVDMPTR(parg16->f1, 256, p1);

    SetKeyboardState(
    p1
    );

    FREEVDMPTR(p1);
    FREEARGPTR(parg16);
    RETURN(0);
}


/*++
    BOOL SetProp(<hwnd>, <lpString>, <hData>)
    HWND <hwnd>;
    LPSTR <lpString>;
    HANDLE <hData>;

    The %SetProp% function adds a new entry or changes an existing entry in the
    property list of the specified window. The %SetProp% function adds a new
    entry to the list if the character string specified by the <lpString>
    parameter does not already exist in the list. The new entry contains the
    string and the handle. Otherwise, the function replaces the string's current
    handle with the one specified by the <hData> parameter.

    The <hData> parameter can contain any 16-bit value useful to the
    application.

    <hwnd>
        Identifies the window whose property list is to receive the new entry.

    <lpString>
        Points to a null-terminated string or an atom that identifies a string.
        If an atom is given, it must have been previously created by using the
        %AddAtom% function. The atom, a 16-bit value, must be placed in the
        low-order word of <lpString>; the high-order word must be zero.

    <hData>
        Identifies a data handle to be copied to the property list.

    The return value specifies the outcome of the function. It is TRUE if the
    data handle and string are added to the property list. Otherwise, it is
    FALSE.

    The application is responsible for removing all entries it has added to the
    property list before destroying the window (that is, before the application
    processes the WM_DESTROY message). The %RemoveProp% function must be used to
    remove entries from a property list.
--*/

ULONG FASTCALL WU32SetProp(PVDMFRAME pFrame)
{
    ULONG ul;
    PSZ psz2;
    register PSETPROP16 parg16;

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

    // don't do any aliasing of the data handle, it can be anything.

    ul = GETBOOL16(SetProp(
    HWND32(parg16->f1),
    psz2,
    (HAND32)parg16->f3
    ));

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


/*++
    int SetScrollPos(<hwnd>, <nBar>, <nPos>, <bRedraw>)
    HWND <hwnd>;
    int <nBar>;
    int <nPos>;
    BOOL <bRedraw>;

    The %SetScrollPos% function sets the current position of a scroll-bar thumb
    to that specified by the <nPos> parameter and, if specified, redraws the
    scroll bar to reflect the new position.

    <hwnd>
        Identifies the window whose scroll bar is to be set.

    <nBar>
        Specifies the scroll bar to be set. It can be one of the following
        values:

    SB_CTL
        Sets the position of a scroll-bar control. In this case, the hwnd
        parameter must be the handle of a scroll-bar control.

    SB_HORZ
        Sets a window's horizontal scroll-bar position.

    SB_VERT
        Sets a window's vertical scroll-bar position.

    <nPos>
        Specifies the new position. It must be within the scrolling range.

    <bRedraw>
        Specifies whether the scroll bar should be redrawn to reflect the
        new position. If the <bRedraw> parameter is TRUE, the scroll bar is
        redrawn. If it is FALSE, it is not redrawn.

    The return value specifies the previous position of the scroll-bar thumb.

    Setting the <bRedraw> parameter to zero is useful whenever the scroll bar
    will be redrawn by a subsequent call to another function.
--*/

ULONG FASTCALL WU32SetScrollPos(PVDMFRAME pFrame)
{
    ULONG ul;
    register PSETSCROLLPOS16 parg16;

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

    ul = GETINT16(SetScrollPos(
    HWND32(parg16->f1),
    INT32(parg16->f2),
    INT32(parg16->f3),
    BOOL32(parg16->f4)
    ));

    FREEARGPTR(parg16);
    RETURN(ul);
}


/*++
    void SetScrollRange(<hwnd>, <nBar>, <nMinPos>, <nMaxPos>, <bRedraw>)
    HWND <hwnd>;
    int <nBar>;
    int <nMinPos>;
    int <nMaxPos>;
    BOOL <bRedraw>;

    The %SetScrollRange% function sets minimum and maximum position values for
    the given scroll bar. It can also be used to hide or show standard scroll
    bars by setting the <nMinPos> and <nMaxPos> parameters to zero.

    <hwnd>
        Identifies a window or a scroll-bar control, depending on the value of
        the nBar parameter.

    <nBar>
        the scroll bar to be set. It can be one of the following
        values:

    SB_CTL
        Sets the range of a scroll-bar control. In this case, the hwnd parameter
        must be the handle of a scroll-bar control.

    SB_HORZ
        Sets a window's horizontal scroll-bar range.

    SB_VERT
        Sets a window's vertical scroll-bar range.

    <nMinPos>
        Specifies the minimum scrolling position.

    <nMaxPos>
        Specifies the maximum scrolling position.

    <bRedraw>
        Specifies whether or not the scroll bar should be redrawn to
        reflect the change. If the <bRedraw> parameter is TRUE, the scroll
        bar is redrawn. If it is FALSE, it is not redrawn.

    This function does not return a value.

    An application should not call this function to hide a scroll bar while
    processing a scroll-bar notification message.

    If %SetScrollRange% immediately follows the %SetScrollPos% function, the
    <bRedraw> parameter in %SetScrollPos% should be set to zero to prevent the
    scroll bar from being drawn twice.

    The difference between the values specified by the <nMinPos> and <nMaxPos>
    parameters must not be greater than 32,767.
--*/

ULONG FASTCALL WU32SetScrollRange(PVDMFRAME pFrame)
{
    register PSETSCROLLRANGE16 parg16;

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

    SetScrollRange(
    HWND32(parg16->f1),
    INT32(parg16->f2),
    INT32(parg16->f3),
    INT32(parg16->f4),
    BOOL32(parg16->f5)
    );

    FREEARGPTR(parg16);
    RETURN(0);
}


/*++
    void SetSysColors(<cDspElements>, <aiDspElements>, <aRgbValues>)
    int <cDspElements>;
    LPINT <aiDspElements>;
    LPDWORD <aRgbValues>;

    The %SetSysColors% function sets the system colors for one or more display
    elements. Display elements are the various parts of a window and the Windows
    display that appear on the system display screen.

    The %SetSysColors% function sends a WM_SYSCOLORCHANGE message to all windows
    to inform them of the change in color. It also directs Windows to repaint
    the affected portions of all currently visible windows.

    <cDspElements>
        Specifies the number of display elements in the <aiDspElements> array.

    <aiDspElements>
        Points to an array of integers that specify the display elements
        to be changed. For a list of possible display elements, see the following
        "Comments" section.

    <aRgbValues>
        Points to an array of unsigned long integers that contains the new RGB
        color value for each display element in the <aiDspElements> array.

    This function does not return a value.

    The %SetSysColors% function changes the current Windows session only. The
    new colors are not saved when Windows terminates.

    The following is the list of display elements that may be used in the array
    of display elements pointed to by the <aiDspElements> parameter:

    COLOR_ACTIVEBORDER
        Active window border.

    COLOR_ACTIVECAPTION
        Active window caption.

    COLOR_APPWORKSPACE
        Background color of multiple document interface (MDI) applications.

    COLOR_BACKGROUND
        Desktop.

    COLOR_BTNFACE
        Face shading on push buttons.

    COLOR_BTNSHADOW
        Edge shading on push buttons.

    COLOR_BTNTEXT
        Text on push buttons.

    COLOR_CAPTIONTEXT
        Text in caption, size box, scroll-bar arrow box.

    COLOR_GRAYTEXT
        Grayed (disabled) text. This color is set to 0 if the current display
        driver does not support a solid gray color.

    COLOR_HIGHLIGHT
        Items selected item in a control.

    COLOR_HIGHLIGHTTEXT
        Text of item selected in a control.

    COLOR_INACTIVEBORDER
        Inactive window border.

    COLOR_INACTIVECAPTION
        Inactive window caption.

    COLOR_INACTIVECAPTIONTEXT
        Color of text in an inactive caption.

    COLOR_MENU
        Menu background.

    COLOR_MENUTEXT
        Text in menus.

    COLOR_SCROLLBAR
        Scroll-bar gray area.

    COLOR_WINDOW
        Window background.

    COLOR_WINDOWFRAME
        Window frame.

    COLOR_WINDOWTEXT
        Text in windows.
--*/

#define SSC_BUF_SIZE	    256

ULONG FASTCALL WU32SetSysColors(PVDMFRAME pFrame)
{
    PINT p2;
    PDWORD p3;
    register PSETSYSCOLORS16 parg16;
    INT BufElements[SSC_BUF_SIZE];

    GETARGPTR(pFrame, sizeof(SETSYSCOLORS16), parg16);
    p2 = STACKORHEAPALLOC(INT32(parg16->f1) * sizeof(INT), sizeof(BufElements), BufElements);
    getintarray16(parg16->f2, INT32(parg16->f1), p2);
    GETDWORDARRAY16(parg16->f3, INT32(parg16->f1), p3);

    if (SetSysColors(
        INT32(parg16->f1),
        p2,
        p3
        ) == FALSE) {
#ifndef i386
    PDWORD p4;
    ULONG   BufRGB [SSC_BUF_SIZE];

        // On RISC platforms, SetSysColors could fail if the third parameter
        // is unaligned. We need to check that and copy it to an aligned
        // buffer before making this call. Win16 SetSysColor never fails
        // so on x86 if this ever fails under NT, it will just pass through.

        if ((ULONG)p3 & 3) {

            p4 = STACKORHEAPALLOC(INT32(parg16->f1) * sizeof(INT), sizeof(BufRGB), BufRGB);

            RtlMoveMemory ((PVOID)p4, (CONST VOID *)p3,
                                       INT32(parg16->f1) * sizeof(ULONG));


            SetSysColors(
                INT32(parg16->f1),
                p2,
                p4
                );
            STACKORHEAPFREE(p4, BufRGB);
        }
#endif

    }

    FREEDWORDARRAY16(p3);
    STACKORHEAPFREE(p2, BufElements);
    FREEARGPTR(parg16);
    RETURN(0);
}

#ifdef NEVER
// this is implemented locally now

/*++
    HWND SetSysModalWindow(<hwnd>)
    HWND <hwnd>;

    The %SetSysModalWindow% function makes the specified window a system-modal
    window.

    <hwnd>
        Identifies the window to be made system modal.

    The return value identifies the window that was previously the system-modal
    window.

    If another window is made the active window (for example, the system-modal
    window creates a dialog box that becomes the active window), the active
    window becomes the system-modal window. When the original window becomes
    active again, it is system modal. To end the system-modal state, destroy the
    system-modal window.
--*/

ULONG FASTCALL WU32SetSysModalWindow(PVDMFRAME pFrame)
{
    RETURN(0);
}

#endif // NEVER

/*++
    void ShowOwnedPopups(<hwnd>, <fShow>)

    The %ShowOwnedPopups% function shows or hides all pop-up windows that are
    associated with the <hwnd> parameter. If the <fShow> parameter is TRUE,
    all hidden pop-up windows are shown; if <fShow> is FALSE, all visible pop-up
    windows are hidden.

    <hwnd>
        Identifies the window that owns the pop-up windows that are to be shown
        or hidden.

    <fShow>
        Specifies whether or not pop-up windows are hidden. It is TRUE if all
        hidden pop-up windows should be shown; it is FALSE if all visible pop-up
        windows should be hidden.

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

ULONG FASTCALL WU32ShowOwnedPopups(PVDMFRAME pFrame)
{
    register PSHOWOWNEDPOPUPS16 parg16;

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

    ShowOwnedPopups(
    HWND32(parg16->f1),
    BOOL32(parg16->f2)
    );

    FREEARGPTR(parg16);
    RETURN(0);
}


/*++
    BOOL SwapMouseButton(<bSwap>)

    The %SwapMouseButton% function reverses the meaning of left and right mouse
    buttons. If the <bSwap> parameter is TRUE, the left button generates
    right-button mouse messages and the right button generates left-button
    messages. If <bSwap> is FALSE, the buttons are restored to their original
    meaning.

    <bSwap>
        Specifies whether the button meanings are reversed or restored.

    The return value specifies the outcome of the function. It is TRUE if the
    fuction reversed the meaning of the mouse buttons. Otherwise, it is FALSE.

    Button swapping is provided as a convenience to people who use the mouse
    with their left hands. The %SwapMouseButton% function is usually called by
    the control panel only. Although applications are free to call the function,
    the mouse is a shared resource and reversing the meaning of the mouse button
    affects all applications.
--*/

ULONG FASTCALL WU32SwapMouseButton(PVDMFRAME pFrame)
{
    ULONG ul;
    register PSWAPMOUSEBUTTON16 parg16;

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

    ul = GETBOOL16(SwapMouseButton(
    BOOL32(parg16->f1)
    ));

    FREEARGPTR(parg16);
    RETURN(ul);
}


/*++
    void InvalidateRect(<hwnd>, <lpRect>, <bErase>)
    HWND <hwnd>;
    LPRECT <lpRect>;
    BOOL <bErase>;

    The %InvalidateRect% function invalidates the client area within the given
    rectangle by adding that rectangle to the window's update region. The
    invalidated rectangle, along with all other areas in the update region, is
    marked for painting when the next WM_PAINT message occurs. The invalidated
    areas accumulate in the update region until the region is processed when the
    next WM_PAINT message occurs, or the region is validated by using the
    %ValidateRect% or %ValidateRgn% function.

    The <bErase> parameter specifies whether the background within the update
    area is to be erased when the update region is processed. If <bErase> is
    TRUE, the background is erased when the %BeginPaint% function is called;
    if <bErase> is FALSE, the background remains unchanged. If <bErase> is
    TRUE for any part of the update region, the background in the entire
    region is erased, not just in the given part.

    <hwnd>
        Identifies the window whose update region is to be modified.

    <lpRect>
        Points to a %RECT% structure that contains the rectangle
        (in client coordinates) to be added to the update region. If the
        <lpRect> parameter is NULL, the entire client area is added to the
        region.

    <bErase>
        Specifies whether the background within the update region is to
        be erased.

    This function does not return a value.

    Windows sends a WM_PAINT message to a window whenever its update region is
    not empty and there are no other messages in the application queue for that
    window.
--*/

ULONG FASTCALL WU32ValidateRect(PVDMFRAME pFrame)
{
    RECT t2, *p2;
    register PVALIDATERECT16 parg16;

    GETARGPTR(pFrame, sizeof(VALIDATERECT16), parg16);
    p2 = GETRECT16(parg16->f2, &t2);

    ValidateRect(
    HWND32(parg16->f1),
    p2
    );

    FREEARGPTR(parg16);
    RETURN(1);    // Win 3.x always returned 1 as a side-effect of jmping to
                  // IRedrawWindow [core\user\wmupdate.c] - MarkRi 5/93
}


/*++
    void InvalidateRgn(<hwnd>, <hRgn>, <bErase>)
    HWND <hwnd>;
    HRGN <hRgn>;
    BOOL <bErase>;

    The %InvalidateRgn% function invalidates the client area within the given
    region by adding it to the current update region of the given window. The
    invalidated region, along with all other areas in the update region, is
    marked for painting when the next WM_PAINT message occurs. The invalidated
    areas accumulate in the update region until the region is processed when the
    next WM_PAINT message occurs, or the region is validated by using the
    %ValidateRect% or %ValidateRgn% function.

    The <bErase> parameter specifies whether the background within the update
    area is to be erased when the update region is processed. If <bErase> is
    TRUE, the background is erased when the %BeginPaint% function is called; if
    <bErase> is FALSE, the background remains unchanged. If <bErase> is TRUE for
    any part of the update region, the background in the entire region is
    erased, not just in the given part.

    <hwnd>
        Identifies the window whose update region is to be modified.

    <hRgn>
        Identifies the region to be added to the update region. The
        region is assumed to have client coordinates.

    <bErase>
        Specifies whether the background within the update region is to
        be erased.

    This function does not return a value.

    Windows sends a WM_PAINT message to a window whenever its update region is
    not empty and there are no other messages in the application queue for that
    window.

    The given region must have been previously created by using one of the
    region functions (for more information, see Chapter 1, Window Manager
    Interface Functions).
--*/

ULONG FASTCALL WU32ValidateRgn(PVDMFRAME pFrame)
{
    register PVALIDATERGN16 parg16;

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

    ValidateRgn(
    HWND32(parg16->f1),
    HRGN32(parg16->f2)
    );

    FREEARGPTR(parg16);
    RETURN(1);    // Win 3.x always returned 1 as a side-effect of jmping to
                  // IRedrawWindow [core\user\wmupdate.c] - MarkRi 5/93
}


/*++
    BOOL WinHelp(<hwnd>, <lpHelpFile>, <wCommand>, <dwData>)
    HWND <hwnd>;
    LPSTR <lpHelpFile>;
    WORD <wCommand>;
    DWORD <dwData>;

    This function invokes the Windows Help application and passes optional data
    indicating the nature of the help requested by the application. The
    application specifies the name and, where required, the directory path of
    the help file which the Help application is to display.

    <hwnd>
        Identifies the window requesting help.

    <lpHelpFile>
        Points to a null-terminated string containing the directory
        path, if needed, and the name of the help file which the Help
        application is to display.

    <wCommand>
        Specifies the type of help requested. It may be any one of the
        following values:

    HELP_CONTEXT
        Displays help for a particular context identified by a 32-bit unsigned
        integer value in dwData.

    HELP_HELPONHELP
        Displays help for using the help application itself. If the <wCommand>
        parameter is set to HELP_HELPONHELP, %WinHelp% ignores the
        <lpHelpFile> and <dwData> parameters.

    HELP_INDEX
        Displays the index of the specified help file. An application should use
        this value only for help files with a single index. It should not use
        this value with HELP_SETINDEX.

    HELP_MULTIKEY
        Displays help for a key word in an alternate keyword table.

    HELP_QUIT
        Notifies the help application that the specified help file is no longer
        in use.

    HELP_SETINDEX
        Sets the context specified by the <dwData> parameter as the current
        index for the help file specified by the <lpHelpFile> parameter. This
        index remains current until the user accesses a different help file. To
        help ensure that the correct index remains set, the application should
        call %WinHelp% with <wCommand> set to HELP_SETINDEX (with <dwData>
        specifying the corresponding context identifier) following each call to
        %WinHelp% with <wCommand> set to HELP_CONTEXT. An application should use
        this value only for help files with more than one index. It should not
        use this value with HELP_INDEX.

    <dwData>
        %DWORD% Specifies the context or key word of the help requested. If
        <wCommand> is HELP_CONTEXT, <dwData> is a 32-bit unsigned integer
        containing a context-identifier number. If <wCommand> is HELP_KEY,
        <dwData> is a long pointer to a null-terminated string that contains a
        key word identifying the help topic. If <wCommand> is HELP_MULTIKEY,
        <dwData> is a long pointer to a %MULTIKEYHELP% structure.
        Otherwise, <dwData> is ignored and should be set to NULL.

    The return value specifies the outcome of the function. It is TRUE if the
    function was successful. Otherwise it is FALSE.

    The application must call %WinHelp% with <wCommand> set to HELP_QUIT before
    closing the window that requested the help. The Help application will not
    actually terminate until all applications that have requested help have
    called %WinHelp% with <wCommand> set to HELP_QUIT.
--*/

#if 0
// this function no longer thunked
ULONG FASTCALL WU32WinHelp(PVDMFRAME pFrame)
{
    ULONG ul;
    PSZ psz2;
    register PWINHELP16 parg16;
    DWORD dwCommand;
    DWORD dwData;
    UINT  cb;
    MULTIKEYHELP *lpmkey;
    PMULTIKEYHELP16 pmkey16;
    HELPWININFO     hwinfo;
    PHELPWININFO16  phwinfo16;

    GETARGPTR(pFrame, sizeof(WINHELP16), parg16);
    GETPSZPTR(parg16->f2, psz2);
    dwCommand = WORD32(parg16->f3);

    switch (dwCommand) {
        case HELP_KEY:
        case HELP_COMMAND:
        case HELP_PARTIALKEY:
            GETPSZPTR(parg16->f4, (PSZ)dwData);
            break;

        case HELP_MULTIKEY:
            GETVDMPTR(parg16->f4, sizeof(MULTIKEYHELP16), pmkey16);
            cb = FETCHWORD(pmkey16->mkSize);
            FREEVDMPTR(pmkey16);
            GETVDMPTR(parg16->f4, cb, pmkey16);

            //
            // It is my understanding that 'mkSize' is the total
            // data length and NOT just sizeof(MULTIKEYHELP)
            //

            cb += sizeof(MULTIKEYHELP) - sizeof(MULTIKEYHELP16);
            lpmkey = (MULTIKEYHELP *)malloc_w(cb);
            if (lpmkey) {
                lpmkey->mkSize = cb;
                lpmkey->mkKeylist = pmkey16->mkKeylist;
                strcpy(lpmkey->szKeyphrase, pmkey16->szKeyphrase);
            }
            FREEVDMPTR(pmkey16);
            dwData = (DWORD)lpmkey;
            break;

        case HELP_SETWINPOS:
            GETVDMPTR(parg16->f4, sizeof(HELPWININFO16), phwinfo16);

            hwinfo.wStructSize = (int)(FETCHWORD(phwinfo16->wStructSize) +
                                 (sizeof(HELPWININFO) - sizeof(HELPWININFO16)));
            hwinfo.x           = (int)FETCHSHORT(phwinfo16->x);
            hwinfo.y           = (int)FETCHSHORT(phwinfo16->y);
            hwinfo.dx          = (int)FETCHSHORT(phwinfo16->dx);
            hwinfo.dy          = (int)FETCHSHORT(phwinfo16->dy);
            hwinfo.wMax        = (int)FETCHSHORT(phwinfo16->wMax);
            hwinfo.rgchMember[0] = (CHAR)phwinfo16->rgchMember[0];
            hwinfo.rgchMember[1] = (CHAR)phwinfo16->rgchMember[1];

            FREEVDMPTR(phwinfo16);
            dwData = (DWORD)&hwinfo;
            break;

        default:
            dwData = DWORD32(parg16->f4);
            break;
    }

    ul = GETBOOL16(WinHelp(HWND32(parg16->f1), psz2, dwCommand, dwData));

    switch (dwCommand) {
        case HELP_KEY:
        case HELP_COMMAND:
        case HELP_PARTIALKEY:
            FREEPSZPTR((PSZ)dwData);
            break;

        case HELP_MULTIKEY:
            if (lpmkey)
                free_w(lpmkey);
            break;

        case HELP_SETWINPOS:
            break;

        default:
            break;
    }


    FREEPSZPTR(psz2);
    FREEARGPTR(parg16);
    RETURN(ul);
}
#endif


#pragma pack(1)

//
// win16 Module Table structure (based off of ne header)
// see wow16\inc\newexe.inc
//

typedef struct _NE_MODULE {
    USHORT ne_magic;           // Magic number
    USHORT ne_usage;           // usage count of module
    USHORT ne_enttab;          // Offset of Entry Table
    USHORT ne_pnextexe;        // sel next module table
    USHORT ne_pautodata;       // offset autodata seg table
    USHORT ne_pfileinfo;       // offset load file info
    USHORT ne_flags;           // Flag word
    USHORT ne_autodata;        // Automatic data segment number
    USHORT ne_heap;            // Initial heap allocation
    USHORT ne_stack;           // Initial stack allocation
    ULONG  ne_csip;            // Initial CS:IP setting
    ULONG  ne_sssp;            // Initial SS:SP setting
    USHORT ne_cseg;            // Count of file segments
    USHORT ne_cmod;            // Entries in Module Reference Table
    USHORT ne_cbnrestab;       // Size of non-resident name table
    USHORT ne_segtab;          // Offset of Segment Table
    USHORT ne_rsrctab;         // Offset of Resource Table
    USHORT ne_restab;          // Offset of resident name table
    USHORT ne_modtab;          // Offset of Module Reference Table
    USHORT ne_imptab;          // Offset of Imported Names Table
    ULONG  ne_nrestab;         // Offset of Non-resident Names Table
    USHORT ne_cmovent;         // Count of movable entries
    USHORT ne_align;           // Segment alignment shift count
    USHORT ne_cres;            // Count of resource segments
    UCHAR  ne_exetyp;          // Target Operating system
    UCHAR  ne_flagsothers;     // Other .EXE flags
    USHORT ne_pretthunks;      // offset to return thunks
    USHORT ne_psegrefbytes;    // offset to segment ref. bytes
    USHORT ne_swaparea;        // Minimum code swap area size
    USHORT ne_expver;          // Expected Windows version number
} NEMODULE;
typedef NEMODULE UNALIGNED *PNEMODULE;

#pragma pack()

//
//   Performs Module cleanup (win31:tmdstroy.c\ModuleUnload())
//
void
ModuleUnload(
   HAND16  hModule16,
   BOOL fTaskExit
   )
{
   PNEMODULE pNeModule = SEGPTR(hModule16, 0);
   PTD ptd = CURRENTPTD();

   if (pNeModule->ne_usage == 1 || fTaskExit) {
       W32UnhookHooks(hModule16,FALSE);
   }

   if (fTaskExit) {
       ptd->dwFlags |= TDF_TASKCLEANUPDONE;
       (pfnOut.pfnWOWCleanup)(HINSTRES32(ptd->hInst16), (DWORD) ptd->htask16, NULL, 0);
   }

   if (pNeModule->ne_usage > 1) {
       return;
       }


    /*   WowCleanup, UserSrv private api
     *   It cleans up any USER objects created by this hModule, most notably
     *   classes, and subclassed windows.
     */
   (pfnOut.pfnWOWCleanup)((HANDLE)hModule16,
                          0,
                          (PNEMODULESEG)((ULONG)pNeModule + pNeModule->ne_segtab),
                          pNeModule->ne_cseg
                          );

   RemoveHmodFromCache(hModule16);

}




/*++
    BOOL SignalProc(<hwnd>, <lpHelpFile>, <wCommand>, <dwData>)
    HWND <hwnd>;
    LPSTR <lpHelpFile>;
    WORD <wCommand>;
    DWORD <dwData>;

    This function provides the communication link between KERNEL and USER.

--*/

#define SG_EXIT         0x0020
#define SG_LOAD_DLL     0x0040
#define SG_EXIT_DLL     0x0080
#define SG_GP_FAULT     0x0666



ULONG FASTCALL WU32SignalProc(PVDMFRAME pFrame)
{
    WORD    message;
    LONG    lparam;
    register PSIGNALPROC16 parg16;
    HAND16  hModule16;
    PTD     ptd;

    GETARGPTR(pFrame, sizeof(SIGNALPROC16), parg16);
    message   = FETCHWORD(parg16->f2);

    switch( message ) {
        case SG_EXIT:
        case SG_GP_FAULT:
            lparam    = FETCHDWORD(parg16->f4);
            ptd = CURRENTPTD();
            ptd->dwFlags |= TDF_INITCALLBACKSTACK | TDF_IGNOREINPUT;
            ModuleUnload(GetExePtr16((HAND16)HIWORD(lparam)), TRUE);
            FreeCursorIconAlias(ptd->htask16, CIALIAS_HTASK);
            break;

        case SG_LOAD_DLL:
            break;

        case SG_EXIT_DLL:
            hModule16 = FETCHWORD(parg16->f1);
            ModuleUnload(hModule16, FALSE);
            FreeCursorIconAlias(hModule16, CIALIAS_HMOD);
            break;
    }

    FREEARGPTR(parg16);
    RETURN(0);
}





// This routine checks the RECT structure (in PAINTSTRUCT) on BeginPaint
// call and updates its fields for maximum positive and minimum negative
// numbers for 16 bit apps to be compatible with win 3.1.
//

void W32FixPaintRect (VPVOID vpPaint, LPPAINTSTRUCT ps)
{
    SHORT i;
    PPAINTSTRUCT16 pps16;

    GETVDMPTR(vpPaint, sizeof(PAINTSTRUCT16), pps16);

    if (i = ConvertInt16 (ps->rcPaint.left)) {
        STORESHORT(pps16->rcPaint.left, i);
    }

    if (i = ConvertInt16 (ps->rcPaint.top)) {
        STORESHORT(pps16->rcPaint.top, i);
    }

    if (i = ConvertInt16 (ps->rcPaint.right)) {
        STORESHORT(pps16->rcPaint.right, i);
    }

    if (i = ConvertInt16 (ps->rcPaint.bottom)) {
        STORESHORT(pps16->rcPaint.bottom, i);
    }

    FLUSHVDMPTR(vpPaint, sizeof(PAINTSTRUCT16), pps16);
    FREEVDMPTR(pps16);
}

SHORT   ConvertInt16 (LONG x)
{
    if (x > (LONG)0x7fff)
        return((SHORT)0x7fff);

    if (x < (LONG)0xffff8000)
        return((SHORT)0x8000);

    return ((SHORT)0);
}
