/**************************************************************************\
* Module Name: server.c
*
* Copyright (c) 1985 - 1999, Microsoft Corporation
*
* Server support routines for the CSR stuff. This basically performs the
* startup/initialization for USER.
*
\**************************************************************************/

#include "precomp.h"
#pragma hdrstop

#include "prefetch.h"

extern WORD gDispatchTableValues;

BOOL gbUserInitialized;

/*
 * Initialization Routines (external).
 */
NTSTATUS     InitQEntryLookaside(VOID);
NTSTATUS     InitSMSLookaside(VOID);

NTSTATUS    InitCreateSharedSection(VOID);
NTSTATUS    InitCreateObjectDirectory(VOID);
BOOL        InitCreateUserSubsystem(VOID);
VOID        InitFunctionTables(VOID);
VOID        InitMessageTables(VOID);
VOID        InitWindowMsgTable(PBYTE*, PUINT, CONST WORD*);

VOID        VerifySyncOnlyMessages(VOID);
BOOL        InitOLEFormats(VOID);
NTSTATUS    Win32UserInitialize(VOID);

#pragma alloc_text(INIT, InitCreateSharedSection)
#pragma alloc_text(INIT, InitCreateUserCrit)
#pragma alloc_text(INIT, InitCreateObjectDirectory)
#pragma alloc_text(INIT, InitCreateUserSubsystem)
#pragma alloc_text(INIT, InitFunctionTables)
#pragma alloc_text(INIT, InitMessageTables)
#pragma alloc_text(INIT, InitWindowMsgTable)

#pragma alloc_text(INIT, VerifySyncOnlyMessages)
#pragma alloc_text(INIT, InitOLEFormats)
#pragma alloc_text(INIT, Win32UserInitialize)

/*
 * Constants pertaining to the user-initialization.
 */
#define USRINIT_SHAREDSECT_SIZE   32
#define USRINIT_ATOMBUCKET_SIZE   37

#define USRINIT_WINDOWSECT_SIZE  512
#define USRINIT_NOIOSECT_SIZE    128

#define USRINIT_SHAREDSECT_BUFF_SIZE     640
#define USRINIT_SHAREDSECT_READ_SIZE     (USRINIT_SHAREDSECT_BUFF_SIZE-33)


/***************************************************************************\
* Globals stored in the INIT section. These should only be accessed at
* load time!
\***************************************************************************/
#ifdef ALLOC_DATA_PRAGMA
#pragma data_seg("INIT$Data")
#endif

CONST WCHAR szCHECKPOINT_PROP_NAME[]  = L"SysCP";
CONST WCHAR szDDETRACK_PROP_NAME[]    = L"SysDT";
CONST WCHAR szQOS_PROP_NAME[]         = L"SysQOS";
CONST WCHAR szDDEIMP_PROP_NAME[]      = L"SysDDEI";
CONST WCHAR szWNDOBJ_PROP_NAME[]      = L"SysWNDO";
CONST WCHAR szIMELEVEL_PROP_NAME[]    = L"SysIMEL";
CONST WCHAR szLAYER_PROP_NAME[]       = L"SysLayer";
CONST WCHAR szUSER32[]                = L"USER32";
CONST WCHAR szCONTEXTHELPIDPROP[]     = L"SysCH";
CONST WCHAR szICONSM_PROP_NAME[]      = L"SysICS";
CONST WCHAR szICON_PROP_NAME[]        = ICON_PROP_NAME;
CONST WCHAR szSHELLHOOK[]             = L"SHELLHOOK";
CONST WCHAR szACTIVATESHELLWINDOW[]   = L"ACTIVATESHELLWINDOW";
CONST WCHAR szOTHERWINDOWCREATED[]    = L"OTHERWINDOWCREATED";
CONST WCHAR szOTHERWINDOWDESTROYED[]  = L"OTHERWINDOWDESTROYED";
CONST WCHAR szOLEMAINTHREADWNDCLASS[] = L"OleMainThreadWndClass";
CONST WCHAR szFLASHWSTATE[]           = L"FlashWState";

#ifdef HUNGAPP_GHOSTING
CONST WCHAR szGHOST[]                 = L"Ghost";
#endif

CONST WCHAR szSHADOW[]                = L"SysShadow";

/***************************************************************************\
* Message Tables
*
*   DefDlgProc
*   MenuWndProc
*   ScrollBarWndProc
*   StaticWndProc
*   ButtonWndProc
*   ListboxWndProc
*   ComboWndProc
*   EditWndProc
*   DefWindowMsgs
*   DefWindowSpecMsgs
*
* These are used in InitMessageTables() to initialize gSharedInfo.awmControl[]
* using the INITMSGTABLE() macro.
*
* 25-Aug-1995 ChrisWil  Created comment block.
\***************************************************************************/

CONST WORD gawDefDlgProc[] = {
    WM_COMPAREITEM,
    WM_VKEYTOITEM,
    WM_CHARTOITEM,
    WM_INITDIALOG,
    WM_QUERYDRAGICON,
    WM_CTLCOLOR,
    WM_CTLCOLORMSGBOX,
    WM_CTLCOLOREDIT,
    WM_CTLCOLORLISTBOX,
    WM_CTLCOLORBTN,
    WM_CTLCOLORDLG,
    WM_CTLCOLORSCROLLBAR,
    WM_CTLCOLORSTATIC,
    WM_ERASEBKGND,
    WM_SHOWWINDOW,
    WM_SYSCOMMAND,
    WM_SYSKEYDOWN,
    WM_ACTIVATE,
    WM_SETFOCUS,
    WM_CLOSE,
    WM_NCDESTROY,
    WM_FINALDESTROY,
    DM_REPOSITION,
    DM_SETDEFID,
    DM_GETDEFID,
    WM_NEXTDLGCTL,
    WM_ENTERMENULOOP,
    WM_LBUTTONDOWN,
    WM_NCLBUTTONDOWN,
    WM_GETFONT,
    WM_NOTIFYFORMAT,
    WM_INPUTLANGCHANGEREQUEST,
    0
};

CONST WORD gawMenuWndProc[] = {
    WM_NCCREATE,
    WM_FINALDESTROY,
    WM_PAINT,
    WM_NCCALCSIZE,
    WM_CHAR,
    WM_SYSCHAR,
    WM_KEYDOWN,
    WM_SYSKEYDOWN,
    WM_TIMER,
    MN_SETHMENU,
    MN_SIZEWINDOW,
    MN_OPENHIERARCHY,
    MN_CLOSEHIERARCHY,
    MN_SELECTITEM,
    MN_SELECTFIRSTVALIDITEM,
    MN_CANCELMENUS,
    MN_FINDMENUWINDOWFROMPOINT,
    MN_SHOWPOPUPWINDOW,
    MN_BUTTONDOWN,
    MN_MOUSEMOVE,
    MN_BUTTONUP,
    MN_SETTIMERTOOPENHIERARCHY,
    WM_ACTIVATE,
    MN_GETHMENU,
    MN_DBLCLK,
    MN_ACTIVATEPOPUP,
    MN_ENDMENU,
    MN_DODRAGDROP,
    WM_ACTIVATEAPP,
    WM_MOUSELEAVE,
    WM_SIZE,
    WM_MOVE,
    WM_NCHITTEST,
    WM_NCPAINT,
    WM_PRINT,
    WM_PRINTCLIENT,
    WM_ERASEBKGND,
    WM_WINDOWPOSCHANGING,
    WM_WINDOWPOSCHANGED,
    0
};

CONST WORD gawDesktopWndProc[] = {
    WM_PAINT,
    WM_ERASEBKGND,
    0
};

CONST WORD gawScrollBarWndProc[] = {
    WM_CREATE,
    WM_SETFOCUS,
    WM_KILLFOCUS,
    WM_ERASEBKGND,
    WM_PAINT,
    WM_LBUTTONDBLCLK,
    WM_LBUTTONDOWN,
    WM_KEYUP,
    WM_KEYDOWN,
    WM_ENABLE,
    SBM_ENABLE_ARROWS,
    SBM_SETPOS,
    SBM_SETRANGEREDRAW,
    SBM_SETRANGE,
    SBM_SETSCROLLINFO,
    SBM_GETSCROLLINFO,
    SBM_GETSCROLLBARINFO,
    WM_PRINTCLIENT,
    WM_MOUSEMOVE,
    WM_MOUSELEAVE,
    0
};

CONST WORD gawStaticWndProc[] = {
    STM_GETICON,
    STM_GETIMAGE,
    STM_SETICON,
    STM_SETIMAGE,
    WM_ERASEBKGND,
    WM_PAINT,
    WM_PRINTCLIENT,
    WM_CREATE,
    WM_DESTROY,
    WM_NCCREATE,
    WM_NCDESTROY,
    WM_FINALDESTROY,
    WM_NCHITTEST,
    WM_LBUTTONDOWN,
    WM_NCLBUTTONDOWN,
    WM_LBUTTONDBLCLK,
    WM_NCLBUTTONDBLCLK,
    WM_SETTEXT,
    WM_ENABLE,
    WM_GETDLGCODE,
    WM_SETFONT,
    WM_GETFONT,
    WM_GETTEXT,
    WM_TIMER,
    WM_INPUTLANGCHANGEREQUEST,
    WM_UPDATEUISTATE,
    0
};

CONST WORD gawButtonWndProc[] = {
    WM_NCHITTEST,
    WM_ERASEBKGND,
    WM_PRINTCLIENT,
    WM_PAINT,
    WM_SETFOCUS,
    WM_GETDLGCODE,
    WM_CAPTURECHANGED,
    WM_KILLFOCUS,
    WM_LBUTTONDBLCLK,
    WM_LBUTTONUP,
    WM_MOUSEMOVE,
    WM_LBUTTONDOWN,
    WM_CHAR,
    BM_CLICK,
    WM_KEYDOWN,
    WM_KEYUP,
    WM_SYSKEYUP,
    BM_GETSTATE,
    BM_SETSTATE,
    BM_GETCHECK,
    BM_SETCHECK,
    BM_SETSTYLE,
    WM_SETTEXT,
    WM_ENABLE,
    WM_SETFONT,
    WM_GETFONT,
    BM_GETIMAGE,
    BM_SETIMAGE,
    WM_NCDESTROY,
    WM_FINALDESTROY,
    WM_NCCREATE,
    WM_INPUTLANGCHANGEREQUEST,
    WM_UPDATEUISTATE,
    0
};

CONST WORD gawListboxWndProc[] = {
    LB_GETTOPINDEX,
    LB_SETTOPINDEX,
    WM_SIZE,
    WM_ERASEBKGND,
    LB_RESETCONTENT,
    WM_TIMER,
    WM_MOUSEMOVE,
    WM_MBUTTONDOWN,
    WM_LBUTTONDOWN,
    WM_LBUTTONUP,
    WM_LBUTTONDBLCLK,
    WM_CAPTURECHANGED,
    LBCB_STARTTRACK,
    LBCB_ENDTRACK,
    WM_PRINTCLIENT,
    WM_PAINT,
    WM_NCDESTROY,
    WM_FINALDESTROY,
    WM_SETFOCUS,
    WM_KILLFOCUS,
    WM_VSCROLL,
    WM_HSCROLL,
    WM_GETDLGCODE,
    WM_CREATE,
    WM_SETREDRAW,
    WM_ENABLE,
    WM_SETFONT,
    WM_GETFONT,
    WM_DRAGSELECT,
    WM_DRAGLOOP,
    WM_DRAGMOVE,
    WM_DROPFILES,
    WM_QUERYDROPOBJECT,
    WM_DROPOBJECT,
    LB_GETITEMRECT,
    LB_GETITEMDATA,
    LB_SETITEMDATA,
    LB_ADDSTRINGUPPER,
    LB_ADDSTRINGLOWER,
    LB_ADDSTRING,
    LB_INSERTSTRINGUPPER,
    LB_INSERTSTRINGLOWER,
    LB_INSERTSTRING,
    LB_INITSTORAGE,
    LB_DELETESTRING,
    LB_DIR,
    LB_ADDFILE,
    LB_SETSEL,
    LB_SETCURSEL,
    LB_GETSEL,
    LB_GETCURSEL,
    LB_SELITEMRANGE,
    LB_SELITEMRANGEEX,
    LB_GETTEXTLEN,
    LB_GETTEXT,
    LB_GETCOUNT,
    LB_SETCOUNT,
    LB_SELECTSTRING,
    LB_FINDSTRING,
    LB_GETLOCALE,
    LB_SETLOCALE,
    WM_KEYDOWN,
    WM_CHAR,
    LB_GETSELITEMS,
    LB_GETSELCOUNT,
    LB_SETTABSTOPS,
    LB_GETHORIZONTALEXTENT,
    LB_SETHORIZONTALEXTENT,
    LB_SETCOLUMNWIDTH,
    LB_SETANCHORINDEX,
    LB_GETANCHORINDEX,
    LB_SETCARETINDEX,
    LB_GETCARETINDEX,
    LB_SETITEMHEIGHT,
    LB_GETITEMHEIGHT,
    LB_FINDSTRINGEXACT,
    LB_ITEMFROMPOINT,
    LB_SETLOCALE,
    LB_GETLOCALE,
    LBCB_CARETON,
    LBCB_CARETOFF,
    LB_GETLISTBOXINFO,
    WM_NCCREATE,
    WM_WINDOWPOSCHANGED,
    WM_MOUSEWHEEL,
    WM_STYLECHANGED,
    WM_STYLECHANGING,
    0
};

CONST WORD gawComboWndProc[] = {
    CBEC_KILLCOMBOFOCUS,
    WM_COMMAND,
    WM_CTLCOLORMSGBOX,
    WM_CTLCOLOREDIT,
    WM_CTLCOLORLISTBOX,
    WM_CTLCOLORBTN,
    WM_CTLCOLORDLG,
    WM_CTLCOLORSCROLLBAR,
    WM_CTLCOLORSTATIC,
    WM_CTLCOLOR,
    WM_GETTEXT,
    WM_GETTEXTLENGTH,
    WM_CLEAR,
    WM_CUT,
    WM_PASTE,
    WM_COPY,
    WM_SETTEXT,
    WM_CREATE,
    WM_ERASEBKGND,
    WM_GETFONT,
    WM_PRINT,
    WM_PRINTCLIENT,
    WM_PAINT,
    WM_GETDLGCODE,
    WM_SETFONT,
    WM_SYSKEYDOWN,
    WM_KEYDOWN,
    WM_CHAR,
    WM_LBUTTONDBLCLK,
    WM_LBUTTONDOWN,
    WM_CAPTURECHANGED,
    WM_LBUTTONUP,
    WM_MOUSEMOVE,
    WM_NCDESTROY,
    WM_FINALDESTROY,
    WM_SETFOCUS,
    WM_KILLFOCUS,
    WM_SETREDRAW,
    WM_ENABLE,
    WM_SIZE,
    CB_GETDROPPEDSTATE,
    CB_GETDROPPEDCONTROLRECT,
    CB_SETDROPPEDWIDTH,
    CB_GETDROPPEDWIDTH,
    CB_DIR,
    CB_SETEXTENDEDUI,
    CB_GETEXTENDEDUI,
    CB_GETEDITSEL,
    CB_LIMITTEXT,
    CB_SETEDITSEL,
    CB_ADDSTRING,
    CB_DELETESTRING,
    CB_INITSTORAGE,
    CB_SETTOPINDEX,
    CB_GETTOPINDEX,
    CB_GETCOUNT,
    CB_GETCURSEL,
    CB_GETLBTEXT,
    CB_GETLBTEXTLEN,
    CB_INSERTSTRING,
    CB_RESETCONTENT,
    CB_GETHORIZONTALEXTENT,
    CB_SETHORIZONTALEXTENT,
    CB_FINDSTRING,
    CB_FINDSTRINGEXACT,
    CB_SELECTSTRING,
    CB_SETCURSEL,
    CB_GETITEMDATA,
    CB_SETITEMDATA,
    CB_SETITEMHEIGHT,
    CB_GETITEMHEIGHT,
    CB_SHOWDROPDOWN,
    CB_SETLOCALE,
    CB_GETLOCALE,
    CB_GETCOMBOBOXINFO,
    WM_MEASUREITEM,
    WM_DELETEITEM,
    WM_DRAWITEM,
    WM_COMPAREITEM,
    WM_NCCREATE,
    WM_HELP,
    WM_MOUSEWHEEL,
    WM_MOUSELEAVE,
    WM_STYLECHANGED,
    WM_STYLECHANGING,
    WM_UPDATEUISTATE,
    0
};

CONST WORD gawEditWndProc[] = {
    EM_CANUNDO,
    EM_CHARFROMPOS,
    EM_EMPTYUNDOBUFFER,
    EM_FMTLINES,
    EM_GETFIRSTVISIBLELINE,
    EM_GETFIRSTVISIBLELINE,
    EM_GETHANDLE,
    EM_GETLIMITTEXT,
    EM_GETLINE,
    EM_GETLINECOUNT,
    EM_GETMARGINS,
    EM_GETMODIFY,
    EM_GETPASSWORDCHAR,
    EM_GETRECT,
    EM_GETSEL,
    EM_GETWORDBREAKPROC,
    EM_SETIMESTATUS,
    EM_GETIMESTATUS,
    EM_LINEFROMCHAR,
    EM_LINEINDEX,
    EM_LINELENGTH,
    EM_LINESCROLL,
    EM_POSFROMCHAR,
    EM_REPLACESEL,
    EM_SCROLL,
    EM_SCROLLCARET,
    EM_SETHANDLE,
    EM_SETLIMITTEXT,
    EM_SETMARGINS,
    EM_SETMODIFY,
    EM_SETPASSWORDCHAR,
    EM_SETREADONLY,
    EM_SETRECT,
    EM_SETRECTNP,
    EM_SETSEL,
    EM_SETTABSTOPS,
    EM_SETWORDBREAKPROC,
    EM_UNDO,
    WM_CAPTURECHANGED,
    WM_CHAR,
    WM_CLEAR,
    WM_CONTEXTMENU,
    WM_COPY,
    WM_CREATE,
    WM_CUT,
    WM_ENABLE,
    WM_ERASEBKGND,
    WM_GETDLGCODE,
    WM_GETFONT,
    WM_GETTEXT,
    WM_GETTEXTLENGTH,
    WM_HSCROLL,
    WM_IME_STARTCOMPOSITION,
    WM_IME_ENDCOMPOSITION,
    WM_IME_COMPOSITION,
    WM_IME_SETCONTEXT,
    WM_IME_NOTIFY,
    WM_IME_COMPOSITIONFULL,
    WM_IME_SELECT,
    WM_IME_CHAR,
    WM_IME_REQUEST,
    WM_INPUTLANGCHANGE,
    WM_KEYUP,
    WM_KEYDOWN,
    WM_KILLFOCUS,
    WM_MBUTTONDOWN,
    WM_LBUTTONDBLCLK,
    WM_LBUTTONDOWN,
    WM_LBUTTONUP,
    WM_MOUSEMOVE,
    WM_NCCREATE,
    WM_NCDESTROY,
    WM_RBUTTONDOWN,
    WM_RBUTTONUP,
    WM_FINALDESTROY,
    WM_PAINT,
    WM_PASTE,
    WM_PRINTCLIENT,
    WM_SETFOCUS,
    WM_SETFONT,
    WM_SETREDRAW,
    WM_SETTEXT,
    WM_SIZE,
    WM_STYLECHANGED,
    WM_STYLECHANGING,
    WM_SYSCHAR,
    WM_SYSKEYDOWN,
    WM_SYSTIMER,
    WM_UNDO,
    WM_VSCROLL,
    WM_MOUSEWHEEL,
    0
};

CONST WORD gawImeWndProc[] = {
    WM_ERASEBKGND,
    WM_PAINT,
    WM_DESTROY,
    WM_NCDESTROY,
    WM_FINALDESTROY,
    WM_CREATE,
    WM_IME_SYSTEM,
    WM_IME_SELECT,
    WM_IME_CONTROL,
    WM_IME_SETCONTEXT,
    WM_IME_NOTIFY,
    WM_IME_COMPOSITION,
    WM_IME_STARTCOMPOSITION,
    WM_IME_ENDCOMPOSITION,
    WM_IME_REQUEST,
    WM_COPYDATA,
    0
};

/*
 * This array is for all the messages that need to be passed straight
 * across to the server for handling.
 */
CONST WORD gawDefWindowMsgs[] = {
    WM_GETHOTKEY,
    WM_SETHOTKEY,
    WM_SETREDRAW,
    WM_SETTEXT,
    WM_PAINT,
    WM_CLOSE,
    WM_ERASEBKGND,
    WM_CANCELMODE,
    WM_SETCURSOR,
    WM_PAINTICON,
    WM_ICONERASEBKGND,
    WM_DRAWITEM,
    WM_KEYF1,
    WM_ISACTIVEICON,
    WM_NCCREATE,
    WM_SETICON,
    WM_NCCALCSIZE,
    WM_NCPAINT,
    WM_NCACTIVATE,
    WM_NCMOUSEMOVE,
    WM_NCRBUTTONUP,
    WM_NCRBUTTONDOWN,
#ifndef LAME_BUTTON
    WM_NCLBUTTONDOWN,
#endif
    WM_NCLBUTTONUP,
    WM_NCLBUTTONDBLCLK,
    WM_KEYUP,
    WM_SYSKEYUP,
#ifndef LAME_BUTTON
    WM_SYSCHAR,
    WM_SYSCOMMAND,
#endif
    WM_QUERYDROPOBJECT,
    WM_CLIENTSHUTDOWN,
    WM_SYNCPAINT,
    WM_PRINT,
    WM_GETICON,
    WM_CONTEXTMENU,
    WM_SYSMENU,
    WM_INPUTLANGCHANGEREQUEST,
    WM_INPUTLANGCHANGE,
    WM_UPDATEUISTATE,
    WM_NCUAHDRAWCAPTION,
    WM_NCUAHDRAWFRAME,
    WM_UAHINIT,
    0
};

/*
 * This array is for all messages that can be handled with some special
 * code by the client. DefWindowProcWorker returns 0 for all messages
 * that aren't in this array or the one above.
 */
CONST WORD gawDefWindowSpecMsgs[] = {
    WM_ACTIVATE,
    WM_GETTEXT,
    WM_GETTEXTLENGTH,
    WM_RBUTTONUP,
    WM_QUERYENDSESSION,
    WM_QUERYOPEN,
    WM_SHOWWINDOW,
    WM_MOUSEACTIVATE,
    WM_HELP,
    WM_VKEYTOITEM,
    WM_CHARTOITEM,
    WM_KEYDOWN,
#ifdef LAME_BUTTON
    WM_SYSCOMMAND,
    WM_SYSCHAR,
#endif
    WM_SYSKEYDOWN,
    WM_UNICHAR,
    WM_DROPOBJECT,
    WM_WINDOWPOSCHANGING,
    WM_WINDOWPOSCHANGED,
    WM_KLUDGEMINRECT,
    WM_CTLCOLOR,
    WM_CTLCOLORMSGBOX,
    WM_CTLCOLOREDIT,
    WM_CTLCOLORLISTBOX,
    WM_CTLCOLORBTN,
    WM_CTLCOLORDLG,
    WM_CTLCOLORSCROLLBAR,
    WM_NCHITTEST,
#ifdef LAME_BUTTON
    WM_NCLBUTTONDOWN,
#endif
    WM_NCXBUTTONUP,
    WM_CTLCOLORSTATIC,
    WM_NOTIFYFORMAT,
    WM_DEVICECHANGE,
    WM_POWERBROADCAST,
    WM_MOUSEWHEEL,
    WM_XBUTTONUP,
    WM_IME_KEYDOWN,
    WM_IME_KEYUP,
    WM_IME_CHAR,
    WM_IME_COMPOSITION,
    WM_IME_STARTCOMPOSITION,
    WM_IME_ENDCOMPOSITION,
    WM_IME_COMPOSITIONFULL,
    WM_IME_SETCONTEXT,
    WM_IME_CONTROL,
    WM_IME_NOTIFY,
    WM_IME_SELECT,
    WM_IME_SYSTEM,
    WM_LPKDRAWSWITCHWND,
    WM_QUERYDRAGICON,
    WM_CHANGEUISTATE,
    WM_QUERYUISTATE,
    WM_APPCOMMAND,
    0
};

static CONST LPCWSTR lpszOLEFormats[] = {
    L"ObjectLink",
    L"OwnerLink",
    L"Native",
    L"Binary",
    L"FileName",
    L"FileNameW",
    L"NetworkName",
    L"DataObject",
    L"Embedded Object",
    L"Embed Source",
    L"Custom Link Source",
    L"Link Source",
    L"Object Descriptor",
    L"Link Source Descriptor",
    L"OleDraw",
    L"PBrush",
    L"MSDraw",
    L"Ole Private Data",
    L"Screen Picture",
    L"OleClipboardPersistOnFlush",
    L"MoreOlePrivateData"
};

static CONST LPCWSTR lpszControls[] = {
    L"Button",
    L"Edit",
    L"Static",
    L"ListBox",
    L"ScrollBar",
    L"ComboBox",
    L"MDIClient",
    L"ComboLBox",
    L"DDEMLEvent",
    L"DDEMLMom",
    L"DMGClass",
    L"DDEMLAnsiClient",
    L"DDEMLUnicodeClient",
    L"DDEMLAnsiServer",
    L"DDEMLUnicodeServer",
    L"IME",
};


#ifdef ALLOC_DATA_PRAGMA
#pragma data_seg()
#endif

/***************************************************************************\
* DispatchServerMessage
*
*
* 19-Aug-1992 MikeKe    Created
\***************************************************************************/

#define WRAPPFN(pfn, type)                                   \
LRESULT xxxWrap ## pfn(                                      \
    PWND  pwnd,                                              \
    UINT  message,                                           \
    WPARAM wParam,                                           \
    LPARAM lParam,                                           \
    ULONG_PTR xParam)                                        \
{                                                            \
    UNREFERENCED_PARAMETER(xParam);                          \
                                                             \
    return xxx ## pfn((type)pwnd, message, wParam, lParam);  \
}

WRAPPFN(SBWndProc, PSBWND)
WRAPPFN(MenuWindowProc, PWND)
WRAPPFN(DesktopWndProc, PWND);
WRAPPFN(RealDefWindowProc, PWND)
WRAPPFN(SwitchWndProc, PWND)

LRESULT xxxWrapSendMessageCallback(
    PWND pwnd,
    UINT message,
    WPARAM wParam,
    LPARAM lParam,
    ULONG_PTR xParam)
{
    SNDMSGCALLBACK smcb;
    TL tlpwnd;
    LRESULT lRet = 0;
    NTSTATUS Status = STATUS_SUCCESS;


    UserAssert (xParam != 0L);

    /*
     * Prevent apps from setting hi 16 bits so we can use them internally.
     */
    if (message & MSGFLAG_MASK) {
        RIPERR0(ERROR_INVALID_PARAMETER, RIP_WARNING, "Invalid message");
        return 0;
    }

    /*
     * Probe all read arguments
     */
    try {
        ProbeForWrite((PVOID)xParam, sizeof(smcb), sizeof(ULONG));
        smcb = *(PSNDMSGCALLBACK)xParam;
    } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
        Status = GetExceptionCode();
    }
    if ( !NT_SUCCESS(Status) ) {
        return 0;
    }

    if (pwnd != PWND_BROADCAST)
        ThreadLockAlways(pwnd, &tlpwnd);

    lRet = (LRESULT)xxxSendMessageCallback(pwnd,
                                           message,
                                           wParam,
                                           lParam,
                                           smcb.lpResultCallBack,
                                           smcb.dwData,
                                           TRUE);
    if (pwnd != PWND_BROADCAST)
        ThreadUnlock(&tlpwnd);

    return lRet;
}

LRESULT xxxWrapSendNotifyMessage(
    PWND  pwnd,
    UINT  message,
    WPARAM wParam,
    LPARAM lParam,
    ULONG_PTR xParam)
{
    TL tlpwnd;
    LRESULT lRet = 0;

    UNREFERENCED_PARAMETER(xParam);

    if (message & MSGFLAG_MASK) {
        RIPERR0(ERROR_INVALID_PARAMETER, RIP_WARNING, "Invalid message");
        return 0;
    }

    if (pwnd != PWND_BROADCAST) {
        ThreadLockAlways(pwnd, &tlpwnd);
    }

    lRet = (LRESULT)xxxSendNotifyMessage(pwnd,
                                         message,
                                         wParam,
                                         lParam);
    if (pwnd != PWND_BROADCAST) {
        ThreadUnlock(&tlpwnd);
    }

    return lRet;
}

LRESULT xxxWrapSendMessage(
    PWND  pwnd,
    UINT  message,
    WPARAM wParam,
    LPARAM lParam,
    ULONG_PTR xParam)
{
    UNREFERENCED_PARAMETER(xParam);

    return xxxSendMessageTimeout(pwnd,
                                 message,
                                 wParam,
                                 lParam,
                                 SMTO_NORMAL,
                                 0,
                                 NULL);
}

LRESULT xxxWrapSendMessageBSM(
    PWND  pwnd,
    UINT  message,
    WPARAM wParam,
    LPARAM lParam,
    ULONG_PTR xParam)
{
    BROADCASTSYSTEMMSGPARAMS bsmParams;
    LRESULT lRet;

    try {
        ProbeForWriteBroadcastSystemMsgParams((LPBROADCASTSYSTEMMSGPARAMS)xParam);
        RtlCopyMemory(&bsmParams, (LPBROADCASTSYSTEMMSGPARAMS)xParam, sizeof(BROADCASTSYSTEMMSGPARAMS));
    } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
        return 0;
    }

    /*
     * If this broadcast is going to all desktops, make sure the thread has
     * sufficient privileges. Do the check here, so we don't effect kernel
     * generated broadcasts (i.e power messages).
     */
    if (bsmParams.dwRecipients & (BSM_ALLDESKTOPS)) {
        if (!IsPrivileged(&psTcb)) {
            bsmParams.dwRecipients &= ~(BSM_ALLDESKTOPS);
            try {
                RtlCopyMemory((LPBROADCASTSYSTEMMSGPARAMS)xParam, &bsmParams, sizeof(BROADCASTSYSTEMMSGPARAMS));
            } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
                  /* empty */
            }
        }
    }

    lRet = xxxSendMessageBSM(pwnd,
                             message,
                             wParam,
                             lParam,
                             &bsmParams);
    /*
     * If our query was denied, return who denied the query.
     */
    if (lRet == 0 && (bsmParams.dwFlags & BSF_QUERY)) {
        try {
            RtlCopyMemory((LPBROADCASTSYSTEMMSGPARAMS)xParam, &bsmParams, sizeof(BROADCASTSYSTEMMSGPARAMS));
        } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
            return 0;
        }
    }

    return lRet;
}

/***************************************************************************\
* xxxUnusedFunctionId
*
* This function is catches attempts to access invalid entries in the server
* side function dispatch table.
*
\***************************************************************************/
LRESULT xxxUnusedFunctionId(
    PWND  pwnd,
    UINT  message,
    WPARAM wParam,
    LPARAM lParam,
    ULONG_PTR xParam)
{
    UNREFERENCED_PARAMETER(pwnd);
    UNREFERENCED_PARAMETER(message);
    UNREFERENCED_PARAMETER(wParam);
    UNREFERENCED_PARAMETER(lParam);
    UNREFERENCED_PARAMETER(xParam);

    UserAssert(FALSE);
    return 0;
}

/***************************************************************************\
* xxxWrapCallWindowProc
*
* Warning should only be called with valid CallProc Handles or the
* EditWndProc special handlers.
*
*
* 21-Apr-1993 JohnC     Created
\***************************************************************************/

LRESULT xxxWrapCallWindowProc(
    PWND  pwnd,
    UINT  message,
    WPARAM wParam,
    LPARAM lParam,
    ULONG_PTR xParam)
{
    PCALLPROCDATA pCPD;
    LRESULT       lRet = 0;

    if (pCPD = HMValidateHandleNoRip((PVOID)xParam, TYPE_CALLPROC)) {

        lRet = ScSendMessage(pwnd,
                              message,
                              wParam,
                              lParam,
                              pCPD->pfnClientPrevious,
                              gpsi->apfnClientW.pfnDispatchMessage,
                              (pCPD->wType & CPD_UNICODE_TO_ANSI) ?
                                      SCMS_FLAGS_ANSI : 0);

    } else {

        /*
         * If it is not a real call proc handle it must be a special
         * handler for editwndproc or regular EditWndProc
         */
        lRet = ScSendMessage(pwnd,
                              message,
                              wParam,
                              lParam,
                              xParam,
                              gpsi->apfnClientA.pfnDispatchMessage,
                              (xParam == (ULONG_PTR)gpsi->apfnClientA.pfnEditWndProc) ?
                                      SCMS_FLAGS_ANSI : 0);
    }

    return lRet;
}

#if DBG
VOID VerifySyncOnlyMessages(VOID)
{
    int i;

    TRACE_INIT(("UserInit: Verify Sync Only Messages\n"));

    /*
     * There are a couple of thunks that just pass parameters. There are other
     * thunks besides SfnDWORD that do a straight pass through because they
     * do other processing beside the wparam and lparam
     */

    /*
     * Allow posting of LB_DIR and CB_DIR because DlgDirList allows a DDL_POSTMSGS
     * flag that makes the API post the messages. This should be OK as long as we
     * don't handle these messages in the kernel. NT 3.51 allowed posting these.
     */
    for (i=0; i<WM_USER; i++) {
        if (    i != LB_DIR
                && i != CB_DIR
                && (gapfnScSendMessage[MessageTable[i].iFunction] != SfnDWORD)
                && (gapfnScSendMessage[MessageTable[i].iFunction] != SfnNCDESTROY)
                && (gapfnScSendMessage[MessageTable[i].iFunction] != SfnINWPARAMCHAR)
                && (gapfnScSendMessage[MessageTable[i].iFunction] != SfnINWPARAMDBCSCHAR)
                && (gapfnScSendMessage[MessageTable[i].iFunction] != SfnSENTDDEMSG)
                && (gapfnScSendMessage[MessageTable[i].iFunction] != SfnPOWERBROADCAST)
                && (gapfnScSendMessage[MessageTable[i].iFunction] != SfnLOGONNOTIFY)
                && (gapfnScSendMessage[MessageTable[i].iFunction] != SfnINDESTROYCLIPBRD)) {
            if (!(TESTSYNCONLYMESSAGE(i,0x8000)))
                RIPMSG1(RIP_ERROR, "InitSyncOnly: is this message sync-only 0x%lX", i);
        } else {
            if (TESTSYNCONLYMESSAGE(i,0))
                RIPMSG1(RIP_VERBOSE, "InitSyncOnly: is this message not sync-only 0x%lX", i);
        }

    }
}
#endif // DBG

/***************************************************************************\
* InitWindowMsgTables
*
* This function generates a bit-array lookup table from a list of messages.
* The lookup table is used to determine whether the message needs to be
* passed over to the server for handling or whether it can be handled
* directly on the client.
*
* LATER: Some memory (a couple hundred bytes per process) could be saved
*        by putting this in the shared read-only heap.
*
*
* 27-Mar-1992 DarrinM   Created.
* 06-Dec-1993 MikeKe    Added support for all of our window procs.
\***************************************************************************/
VOID InitWindowMsgTable(
    PBYTE      *ppbyte,
    PUINT      pmax,
    CONST WORD *pw)
{
    UINT i;
    WORD msg;
    UINT cbTable;

    *pmax = 0;
    for (i = 0; (msg = pw[i]) != 0; i++) {
        if (msg > *pmax)
            *pmax = msg;
    }

    cbTable = *pmax / 8 + 1;
    *ppbyte = SharedAlloc(cbTable);
    if (*ppbyte == NULL) {
        return;
    }

    for (i = 0; (msg = pw[i]) != 0; i++) {
        (*ppbyte)[msg / 8] |= (BYTE)(1 << (msg & 7));
    }
}

/***************************************************************************\
* InitFunctionTables
*
* Initialize the procedures and function tables.
*
*
* 25-Aug-1995 ChrisWil  Created comment block.
\***************************************************************************/
VOID InitFunctionTables(
    VOID)
{
    UINT i;

    TRACE_INIT(("UserInit: Initialize Function Tables\n"));

    UserAssert(sizeof(CLIENTINFO) <= sizeof(NtCurrentTeb()->Win32ClientInfo));

    /*
     * This table is used to convert from server procs to client procs.
     */
    STOCID(FNID_SCROLLBAR)              = (WNDPROC_PWND)xxxSBWndProc;
    STOCID(FNID_ICONTITLE)              = xxxDefWindowProc;
    STOCID(FNID_MENU)                   = xxxMenuWindowProc;
    STOCID(FNID_DESKTOP)                = xxxDesktopWndProc;
    STOCID(FNID_DEFWINDOWPROC)          = xxxDefWindowProc;
    STOCID(FNID_MESSAGEWND)             = xxxDefWindowProc;

    /*
     * This table is used to determine the number minimum number of reserved
     * windows words required for the server proc.
     */
    CBFNID(FNID_SCROLLBAR)              = sizeof(SBWND);
    CBFNID(FNID_ICONTITLE)              = sizeof(WND);
    CBFNID(FNID_MENU)                   = sizeof(MENUWND);

    /*
     * Initialize this data structure (api function table).
     */
    for (i = 0; i < FNID_ARRAY_SIZE; i++) {
        FNID((i + FNID_START)) = xxxUnusedFunctionId;
    }
    FNID(FNID_SCROLLBAR)                = xxxWrapSBWndProc;
    FNID(FNID_ICONTITLE)                = xxxWrapRealDefWindowProc;
    FNID(FNID_MENU)                     = xxxWrapMenuWindowProc;
    FNID(FNID_DESKTOP)                  = xxxWrapDesktopWndProc;
    FNID(FNID_DEFWINDOWPROC)            = xxxWrapRealDefWindowProc;
    FNID(FNID_MESSAGEWND)               = xxxWrapRealDefWindowProc;
    FNID(FNID_SENDMESSAGE)              = xxxWrapSendMessage;
    FNID(FNID_HKINLPCWPEXSTRUCT)        = fnHkINLPCWPEXSTRUCT;
    FNID(FNID_HKINLPCWPRETEXSTRUCT)     = fnHkINLPCWPRETEXSTRUCT;
    FNID(FNID_SENDMESSAGEFF)            = xxxSendMessageFF;
    FNID(FNID_SENDMESSAGEEX)            = xxxSendMessageEx;
    FNID(FNID_CALLWINDOWPROC)           = xxxWrapCallWindowProc;
    FNID(FNID_SENDMESSAGEBSM)           = xxxWrapSendMessageBSM;
    FNID(FNID_SWITCH)                   = xxxWrapSwitchWndProc;
    FNID(FNID_SENDNOTIFYMESSAGE)        = xxxWrapSendNotifyMessage;
    FNID(FNID_SENDMESSAGECALLBACK)      = xxxWrapSendMessageCallback;

#if DBG
    {
        PULONG_PTR pdw;

        /*
         * Make sure that everyone got initialized.
         */
        for (pdw = (PULONG_PTR)&STOCID(FNID_START);
                (ULONG_PTR)pdw<(ULONG_PTR)(&STOCID(FNID_WNDPROCEND)); pdw++) {
            UserAssert(*pdw);
        }

        for (pdw=(PULONG_PTR)&FNID(FNID_START);
                (ULONG_PTR)pdw<(ULONG_PTR)(&FNID(FNID_WNDPROCEND)); pdw++) {
            UserAssert(*pdw);
        }
    }
#endif
}

/***************************************************************************\
* InitMessageTables
*
* Initialize the message tables.
*
* 25-Aug-1995 ChrisWil      Created.
\***************************************************************************/
VOID InitMessageTables(
    VOID)
{
    TRACE_INIT(("UserInit: Initialize Message Tables\n"));

#define INITMSGTABLE(member, procname)                \
    InitWindowMsgTable(&(gSharedInfo.member.abMsgs),  \
                       &(gSharedInfo.member.maxMsgs), \
                       gaw ## procname);

    INITMSGTABLE(DefWindowMsgs, DefWindowMsgs);
    INITMSGTABLE(DefWindowSpecMsgs, DefWindowSpecMsgs);

    INITMSGTABLE(awmControl[FNID_DIALOG       - FNID_START], DefDlgProc);
    INITMSGTABLE(awmControl[FNID_SCROLLBAR    - FNID_START], ScrollBarWndProc);
    INITMSGTABLE(awmControl[FNID_MENU         - FNID_START], MenuWndProc);
    INITMSGTABLE(awmControl[FNID_DESKTOP      - FNID_START], DesktopWndProc);
    INITMSGTABLE(awmControl[FNID_STATIC       - FNID_START], StaticWndProc);
    INITMSGTABLE(awmControl[FNID_BUTTON       - FNID_START], ButtonWndProc);
    INITMSGTABLE(awmControl[FNID_LISTBOX      - FNID_START], ListboxWndProc);
    INITMSGTABLE(awmControl[FNID_COMBOBOX     - FNID_START], ComboWndProc);
    INITMSGTABLE(awmControl[FNID_COMBOLISTBOX - FNID_START], ListboxWndProc);
    INITMSGTABLE(awmControl[FNID_EDIT         - FNID_START], EditWndProc);
    INITMSGTABLE(awmControl[FNID_IME          - FNID_START], ImeWndProc);
}

/***************************************************************************\
* InitOLEFormats
*
* OLE performance hack. OLE was previously having to call the server 15
* times for clipboard formats and another 15 LPC calls for the global atoms.
* Now we preregister them. We also assert they are in order so OLE only has
* to query the first to know them all. We call AddAtom directly instead of
* RegisterClipboardFormat.
*
* 25-Aug-1995 ChrisWil      Created.
\***************************************************************************/
BOOL InitOLEFormats(
    VOID)
{
    UINT idx;
    ATOM a1;
    ATOM a2;
    BOOL fSuccess = TRUE;

    TRACE_INIT(("UserInit: Initialize OLE Formats\n"));

    a1 = UserAddAtom(lpszOLEFormats[0], TRUE);

    for (idx = 1; idx < ARRAY_SIZE(lpszOLEFormats); idx++) {
        a2 = UserAddAtom(lpszOLEFormats[idx], TRUE);
        fSuccess &= !!a2;

        UserAssert(((a1 + 1) == a2) && (a1 = a2));
    }

    if (!fSuccess) {
        RIPMSG0(RIP_ERROR, "InitOLEFormats: at least one atom not registered");
    }

    return fSuccess;
}

#if DBG
/***************************************************************************\
* InitGlobalRIPFlags (CHK only).
*
* This initializes the global RIP flags from the registry.
*
* 25-Aug-1995 ChrisWil      Created.
\***************************************************************************/
VOID InitGlobalRIPFlags(
    VOID)
{

    UINT  idx;
    UINT  nCount;
    DWORD dwFlag;

    static CONST struct {
        LPWSTR lpszKey;
        DWORD  dwDef;
        DWORD  dwFlag;
    } aRIPFlags[] = {
        {L"fPromptOnError"  , 1, RIPF_PROMPTONERROR},
        {L"fPromptOnWarning", 0, RIPF_PROMPTONWARNING},
        {L"fPromptOnVerbose", 0, RIPF_PROMPTONVERBOSE},
        {L"fPrintError"     , 1, RIPF_PRINTONERROR},
        {L"fPrintWarning"   , 1, RIPF_PRINTONWARNING},
        {L"fPrintVerbose"   , 0, RIPF_PRINTONVERBOSE},
        {L"fPrintFileLine"  , 0, RIPF_PRINTFILELINE},
    };

    TRACE_INIT(("UserInit: Initialize Global RIP Flags\n"));

    nCount = ARRAY_SIZE(aRIPFlags);

    /*
     * Turn off the rip-on-warning bit. This is necessary to prevent the
     * FastGetProfileDwordW() routine from breaking into the debugger if an
     * entry can't be found. Since we provide default values, there's no sense
     * to break.
     */
    CLEAR_FLAG(gpsi->dwRIPFlags, RIPF_PROMPTONWARNING);
    CLEAR_FLAG(gpsi->dwRIPFlags, RIPF_PRINTONWARNING);

    for (idx = 0; idx < nCount; idx++) {
        FastGetProfileDwordW(NULL,
                             PMAP_WINDOWSM,
                             aRIPFlags[idx].lpszKey,
                             aRIPFlags[idx].dwDef,
                             &dwFlag,
                             0);

        SET_OR_CLEAR_FLAG(gpsi->dwRIPFlags, aRIPFlags[idx].dwFlag, dwFlag);
    }
}
#else
#define InitGlobalRIPFlags()
#endif


/***************************************************************************\
* _GetTextMetricsW
* _TextOutW
*
* Server shared function thunks.
*
* History:
* 10-Nov-1993 MikeKe    Created
\***************************************************************************/
BOOL _GetTextMetricsW(
    HDC           hdc,
    LPTEXTMETRICW ptm)
{
    TMW_INTERNAL tmi;
    BOOL         fret;

    fret = GreGetTextMetricsW(hdc, &tmi);

    *ptm = tmi.tmw;

    return fret;
}

BOOL _TextOutW(
    HDC     hdc,
    int     x,
    int     y,
    LPCWSTR lp,
    UINT    cc)
{
    return GreExtTextOutW(hdc, x, y, 0, NULL, (LPWSTR)lp, cc, NULL);
}


#define ROUND_UP_TO_PAGES(SIZE) \
        (((ULONG)(SIZE) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))

/***************************************************************************\
* InitCreateSharedSection
*
* This creates the shared section.
*
*
* 25-Aug-1995 ChrisWil      Created comment block.
\***************************************************************************/
NTSTATUS InitCreateSharedSection(
    VOID)
{
    ULONG             ulHeapSize;
    ULONG             ulHandleTableSize;
    NTSTATUS          Status;
    LARGE_INTEGER     SectionSize;
    SIZE_T            ViewSize;
    PVOID             pHeapBase;

    TRACE_INIT(("UserInit: Create Shared Memory Section\n"));

    UserAssert(ghSectionShared == NULL);

    ulHeapSize        = ROUND_UP_TO_PAGES(USRINIT_SHAREDSECT_SIZE * 1024);
    ulHandleTableSize = ROUND_UP_TO_PAGES(0x10000 * sizeof(HANDLEENTRY));

    TRACE_INIT(("UserInit: Share: TableSize = %X; HeapSize = %X\n",
            ulHandleTableSize, ulHeapSize));

    SectionSize.LowPart  = ulHeapSize + ulHandleTableSize;
    SectionSize.HighPart = 0;

    Status = Win32CreateSection(&ghSectionShared,
                                SECTION_ALL_ACCESS,
                                NULL,
                                &SectionSize,
                                PAGE_EXECUTE_READWRITE,
                                SEC_RESERVE,
                                NULL,
                                NULL,
                                TAG_SECTION_SHARED);

    if (!NT_SUCCESS(Status)) {
        RIPMSG1(RIP_WARNING,
                "MmCreateSection failed in InitCreateSharedSection with Status %x",
                Status);
        return Status;
    }

    ViewSize = 0;
    gpvSharedBase = NULL;

    Status = Win32MapViewInSessionSpace(ghSectionShared, &gpvSharedBase, &ViewSize);
    if (!NT_SUCCESS(Status)) {
        RIPMSG1(RIP_WARNING,
                "Win32MapViewInSessionSpace failed with Status %x",
                Status);
        Win32DestroySection(ghSectionShared);
        ghSectionShared = NULL;
        return Status;
    }

    pHeapBase = ((PBYTE)gpvSharedBase + ulHandleTableSize);

    TRACE_INIT(("UserInit: Share: BaseAddr = %#p; Heap = %#p, ViewSize = %X\n",
            gpvSharedBase, pHeapBase, ViewSize));

    /*
     * Create shared heap.
     */
    if ((gpvSharedAlloc = UserCreateHeap(
            ghSectionShared,
            ulHandleTableSize,
            pHeapBase,
            ulHeapSize,
            UserCommitSharedMemory)) == NULL) {
        RIPERR0(ERROR_NOT_ENOUGH_MEMORY,
                RIP_WARNING,
                "Can't create shared memory heap.");

        Win32UnmapViewInSessionSpace(gpvSharedBase);

        Win32DestroySection(ghSectionShared);
        gpvSharedAlloc = NULL;
        gpvSharedBase = NULL;
        ghSectionShared = NULL;

        return STATUS_NO_MEMORY;
    }

    UserAssert(Win32HeapGetHandle(gpvSharedAlloc) == pHeapBase);

    return STATUS_SUCCESS;
}

/**************************************************************************\
* InitCreateUserCrit
*
* Create and initialize the user critical sections needed throughout the
* system.
*
* 23-Jan-1996 ChrisWil      Created.
\**************************************************************************/
BOOL InitCreateUserCrit(
    VOID)
{
    TRACE_INIT(("Win32UserInit: InitCreateUserCrit()\n"));

    /*
     * Initialize a critical section structure that will be used to protect
     * all of the User Server's critical sections (except a few special
     * cases like the RIT -- see below).
     */
    gpresUser = ExAllocatePoolWithTag(NonPagedPool,
                                      sizeof(ERESOURCE),
                                      TAG_ERESOURCE);
    if (!gpresUser) {
        goto InitCreateUserCritExit;
    }

    if (!NT_SUCCESS(ExInitializeResourceLite(gpresUser))) {
        goto InitCreateUserCritExit;
    }

    /*
     * Initialize a critical section to be used in [Un]QueueMouseEvent
     * to protect the queue of mouse input events that the desktop thread
     * uses to pass input on to the RIT, after having moved the cursor
     * without obtaining gpresUser itself.
     */
    gpresMouseEventQueue = ExAllocatePoolWithTag(NonPagedPool,
                                                 sizeof(ERESOURCE),
                                                 TAG_ERESOURCE);
    if (!gpresMouseEventQueue) {
        goto InitCreateUserCritExit;
    }
    if (!NT_SUCCESS(ExInitializeResourceLite(gpresMouseEventQueue))) {
        goto InitCreateUserCritExit;
    }

    /*
     * Initialize a critical section to protect the list of DEVICEINFO structs
     * kept under gpDeviceInfoList. This is used by the RIT when reading kbd
     * input, the desktop thread when reading mouse input, and the PnP callback
     * routines DeviceClassNotify() and DeviceNotify() when devices come and go.
     */
    gpresDeviceInfoList = ExAllocatePoolWithTag(NonPagedPool,
                                            sizeof(ERESOURCE),
                                            TAG_ERESOURCE);
    if (!gpresDeviceInfoList) {
        goto InitCreateUserCritExit;
    }
    if (!NT_SUCCESS(ExInitializeResourceLite(gpresDeviceInfoList))) {
        goto InitCreateUserCritExit;
    }

    /*
     * Create the handle flag mutex. We'll need this once we start creating
     * windowstations and desktops.
     */
    gpHandleFlagsMutex = ExAllocatePoolWithTag(NonPagedPool,
                                               sizeof(FAST_MUTEX),
                                               TAG_SYSTEM);
    if (gpHandleFlagsMutex == NULL) {
        goto InitCreateUserCritExit;
    }
    ExInitializeFastMutex(gpHandleFlagsMutex);

    TRACE_INIT(("Win32UserInit: gpHandleFlagsMutex = %#p\n", gpHandleFlagsMutex));
    TRACE_INIT(("Win32UserInit: gpresDeviceInfoList = %#p\n", gpresDeviceInfoList));
    TRACE_INIT(("Win32UserInit: gpresMouseEventQueue = %#p\n", gpresMouseEventQueue));
    TRACE_INIT(("Win32UserInit: gpresUser  = %#p\n", gpresUser));

    TRACE_INIT(("Win32UserInit: exit InitCreateUserCrit()\n"));
    return TRUE;

InitCreateUserCritExit:
    RIPERR0(ERROR_NOT_ENOUGH_MEMORY,
            RIP_WARNING,
            "Win32UserInit: InitCreateUserCrit failed");

    if (gpresUser) {
        ExFreePool(gpresUser);
    }

    if (gpresMouseEventQueue) {
        ExFreePool(gpresMouseEventQueue);
    }

    if (gpresDeviceInfoList) {
        ExFreePool(gpresDeviceInfoList);
    }

    return FALSE;
}

/**************************************************************************\
* InitCreateObjectDirectory
*
* Create and initialize the user critical sections needed throughout the
* system.
*
* 23-Jan-1996 ChrisWil      Created.
\**************************************************************************/
NTSTATUS InitCreateObjectDirectory(VOID)
{
    HANDLE            hDir;
    NTSTATUS          Status;
    OBJECT_ATTRIBUTES ObjectAttributes;
    UNICODE_STRING    UnicodeString;
    ULONG             attributes = OBJ_CASE_INSENSITIVE | OBJ_PERMANENT;

    TRACE_INIT(("UserInit: Create User Object-Directory\n"));

    RtlInitUnicodeString(&UnicodeString, szWindowStationDirectory);

    if (gbRemoteSession) {
       /*
        * Remote sessions don't use this flag
        */
       attributes &= ~OBJ_PERMANENT;
    }

    InitializeObjectAttributes(&ObjectAttributes,
                               &UnicodeString,
                               attributes,
                               NULL,
                               gpsdInitWinSta);

    Status = ZwCreateDirectoryObject(&hDir,
                                     DIRECTORY_CREATE_OBJECT,
                                     &ObjectAttributes);

    UserFreePool(gpsdInitWinSta);

    /*
     * Do not close this handle for remote session because
     * if we do close it then the directory will go away and
     * we don't want that to happen. When CSRSS will go away
     * this handle will be freed also.
     */
    if (!gbRemoteSession)
        ZwClose(hDir);

    gpsdInitWinSta = NULL;

    return Status;
}

/**************************************************************************\
* InitCreateUserSubsystem
*
* Create and initialize the user subsystem stuff.
* system.
*
* 23-Jan-1996 ChrisWil      Created.
\**************************************************************************/
BOOL
InitCreateUserSubsystem()
{
    LPWSTR         lpszSubSystem;
    LPWSTR         lpszT;
    UNICODE_STRING strSize;

    TRACE_INIT(("UserInit: Create User SubSystem\n"));

    /*
     * Initialize the subsystem section. This identifies the default
     * user-heap size.
     */
    lpszSubSystem = UserAllocPoolWithQuota(USRINIT_SHAREDSECT_BUFF_SIZE * sizeof(WCHAR),
                                           TAG_SYSTEM);

    if (lpszSubSystem == NULL) {
        return FALSE;
    }

    if (FastGetProfileStringW(NULL,
                              PMAP_SUBSYSTEMS,
                              L"Windows",
                              L"SharedSection=,3072",
                              lpszSubSystem,
                              USRINIT_SHAREDSECT_READ_SIZE,
                              0) == 0) {
        RIPMSG0(RIP_WARNING,
                "UserInit: Windows subsystem definition not found");
        UserFreePool(lpszSubSystem);
        return FALSE;
    }

    /*
     * Locate the SharedSection portion of the definition and extract
     * the second value.
     */
    gdwDesktopSectionSize = USER_WINDOWSECT_SIZE;
    gdwNOIOSectionSize    = USER_NOIOSECT_SIZE;

    if (lpszT = wcsstr(lpszSubSystem, L"SharedSection")) {

        *(lpszT + 32) = UNICODE_NULL;

        if (lpszT = wcschr(lpszT, L',')) {

            RtlInitUnicodeString(&strSize, ++lpszT);
            RtlUnicodeStringToInteger(&strSize, 0, &gdwDesktopSectionSize);

            /*
             * Assert this logic doesn't need to change.
             */
            UserAssert(gdwDesktopSectionSize >= USER_WINDOWSECT_SIZE);

            gdwDesktopSectionSize = max(USER_WINDOWSECT_SIZE, gdwDesktopSectionSize);
            gdwNOIOSectionSize    = gdwDesktopSectionSize;

            /*
             * Now see if the optional non-interactive desktop
             * heap size was specified.
             */
            if (lpszT = wcschr(lpszT, L',')) {

                RtlInitUnicodeString(&strSize, ++lpszT);
                RtlUnicodeStringToInteger(&strSize, 0, &gdwNOIOSectionSize);

                UserAssert(gdwNOIOSectionSize >= USER_NOIOSECT_SIZE);
                gdwNOIOSectionSize = max(USER_NOIOSECT_SIZE, gdwNOIOSectionSize);
            }
        }
    }

    UserFreePool(lpszSubSystem);

    return TRUE;
}

extern UNICODE_STRING *gpastrSetupExe;     // These are used in
extern int giSetupExe;                     // SetAppImeCompatFlags in
                                           // queue.c

WCHAR* glpSetupPrograms;

/**************************************************************************\
* CreateSetupNameArray
*
* Create and initialize the arrary of setup app names. We inherited this
* hack From Chicago. See queue.c for more details.
\**************************************************************************/
BOOL CreateSetupNameArray(
    VOID)
{
    DWORD  dwProgNames;
    int    iSetupProgramCount = 0;
    WCHAR* lpTemp;
    int    ic, icnt, icMax;

    dwProgNames = FastGetProfileValue(NULL,
                                      PMAP_SETUPPROGRAMNAMES,
                                      L"SetupProgramNames",
                                      NULL,
                                      NULL,
                                      0,
                                      0);

    /*
     * This key is a multi-string, so is best to read as a value.
     * First, get the length and create the buffer to hold all of
     * the strings.
     */
    if (dwProgNames == 0) {
        return FALSE;
    }

    glpSetupPrograms = UserAllocPoolWithQuota(dwProgNames,
                                       TAG_SYSTEM);

    if (glpSetupPrograms == NULL) {
        RIPMSG0(RIP_WARNING, "CreateSetupNameArray: Memory allocation failure");
        return FALSE;
    }

    FastGetProfileValue(NULL,
                        PMAP_SETUPPROGRAMNAMES,
                        L"SetupProgramNames",
                        NULL,
                        (PBYTE)glpSetupPrograms,
                        dwProgNames,
                        0);

    lpTemp = glpSetupPrograms;
    icMax = dwProgNames/2;
    ic = 0; icnt=0;
    /*
     * Now count the strings.
     */
    while (ic < icMax) {
        if (*(lpTemp+ic) == 0) {
            ic++;
            continue;
        }
        ic += wcslen(lpTemp+ic)+1;
        icnt++;
    }

    /*
     * gpastrSetupExe is a pointer to an array of UNICODE_STRING structures.
     * Each structure is the name of one setup program.
     */
    giSetupExe = icnt;
    gpastrSetupExe = UserAllocPoolWithQuota(giSetupExe * sizeof(UNICODE_STRING),
                                       TAG_SYSTEM);

    if (gpastrSetupExe == NULL) {
        RIPMSG0(RIP_WARNING, "CreateSetupNameArray: Memory allocation failure");
        giSetupExe = 0;
        UserFreePool(glpSetupPrograms);
        glpSetupPrograms = NULL;
        return FALSE;
    }

    ic = 0; icnt=0;
    while (ic < icMax) {
        if (*(lpTemp+ic) == 0) {
            ic++;
            continue;
        }
        gpastrSetupExe[icnt].Buffer = lpTemp+ic;
        gpastrSetupExe[icnt].Length = sizeof(WCHAR)*wcslen(lpTemp+ic);
        gpastrSetupExe[icnt].MaximumLength = gpastrSetupExe[icnt].Length + sizeof(WCHAR);
        ic += wcslen(lpTemp+ic)+1;
        icnt++;

    }

    return TRUE;
}

#define CALC_DELTA(element)                   \
        (PVOID)((PBYTE)pClientBase +          \
        ((PBYTE)gSharedInfo.element -         \
        (PBYTE)gpvSharedBase))

/***************************************************************************\
* InitMapSharedSection
*
* This maps the shared section.
*
*
* 25-Aug-1995 ChrisWil      Created comment block.
\***************************************************************************/

NTSTATUS InitMapSharedSection(
    PEPROCESS    Process,
    PUSERCONNECT pUserConnect)
{
    int           i;
    PVOID         pClientBase = NULL;
    ULONG_PTR      ulSharedDelta;
    PW32PROCESS   pw32p;

    TRACE_INIT(("UserInit: Map Shared Memory Section\n"));

    UserAssert(ghSectionShared != NULL);

    ValidateProcessSessionId(Process);

    /*
     * Check to see if we haven't already mapped the section
     * This might happen for multiple LoadLibrary()/FreeLibrary calls
     * in one process. MCostea #56946
     */
    pw32p = PsGetProcessWin32Process(Process);
    if (pw32p == NULL ||
        ((PPROCESSINFO)pw32p)->pClientBase == NULL) {

        SIZE_T        ViewSize;
        LARGE_INTEGER liOffset;
        NTSTATUS Status;

        ViewSize = 0;
        liOffset.QuadPart = 0;

        Status = MmMapViewOfSection(ghSectionShared,
                                Process,
                                &pClientBase,
                                0,
                                0,
                                &liOffset,
                                &ViewSize,
                                ViewUnmap,
                                SEC_NO_CHANGE,
                                PAGE_EXECUTE_READ);
        if (NT_SUCCESS(Status)) {
            TRACE_INIT(("UserInit: Map: Client SharedInfo Base = %#p\n", pClientBase));

            UserAssert(gpvSharedBase > pClientBase);
            pw32p = PsGetProcessWin32Process(Process);
            if (pw32p != NULL) {
                ((PPROCESSINFO)pw32p)->pClientBase = pClientBase;
            }
        } else {
            return Status;
        }

    } else {
        pClientBase = ((PPROCESSINFO)PsGetProcessWin32Process(Process))->pClientBase;
    }
    ulSharedDelta = (PBYTE)gpvSharedBase - (PBYTE)pClientBase;
    pUserConnect->siClient.ulSharedDelta = ulSharedDelta;

    pUserConnect->siClient.psi          = CALC_DELTA(psi);
    pUserConnect->siClient.aheList      = CALC_DELTA(aheList);
    pUserConnect->siClient.pDispInfo    = CALC_DELTA(pDispInfo);


    pUserConnect->siClient.DefWindowMsgs.maxMsgs     = gSharedInfo.DefWindowMsgs.maxMsgs;
    pUserConnect->siClient.DefWindowMsgs.abMsgs      = CALC_DELTA(DefWindowMsgs.abMsgs);
    pUserConnect->siClient.DefWindowSpecMsgs.maxMsgs = gSharedInfo.DefWindowSpecMsgs.maxMsgs;
    pUserConnect->siClient.DefWindowSpecMsgs.abMsgs  = CALC_DELTA(DefWindowSpecMsgs.abMsgs);

    for (i = 0; i < (FNID_END - FNID_START + 1); ++i) {

        pUserConnect->siClient.awmControl[i].maxMsgs = gSharedInfo.awmControl[i].maxMsgs;

        if (gSharedInfo.awmControl[i].abMsgs)
            pUserConnect->siClient.awmControl[i].abMsgs = CALC_DELTA(awmControl[i].abMsgs);
        else
            pUserConnect->siClient.awmControl[i].abMsgs = NULL;
    }
    return STATUS_SUCCESS;
}
/**************************************************************************\
* InitLoadResources
*
*
* 25-Aug-1995 ChrisWil      Created.
\**************************************************************************/

VOID InitLoadResources()
{
    PRECT   prc;

    DISPLAYRESOURCE dr = {
        17,     // Height of vertical thumb
        17,     // Width of horizontal thumb
        2,      // Icon horiz compression factor
        2,      // Icon vert compression factor
        2,      // Cursor horz compression factor
        2,      // Cursor vert compression factor
        0,      // Kanji window height
        1,      // cxBorder (thickness of vertical lines)
        1       // cyBorder (thickness of horizontal lines)
    };


    TRACE_INIT(("UserInit: Load Display Resources\n"));

    /*
     * For high-DPI systems, don't compress the cursor as much.
     * This really should be a more configurable method, such as a System
     * Parameter Info setting or something. But for now, we'll hard-code
     * 150dpi to be the threshhold at which we'll double the cursor size.
     */
    if (gpsi->dmLogPixels >= 150) {
        dr.xCompressCursor = 1;
        dr.yCompressCursor = 1;
    }

    if (dr.xCompressIcon > 10) {

        /*
         * If so, the actual dimensions of icons and cursors are
         * kept in OEMBIN.
         */
        SYSMET(CXICON)   = dr.xCompressIcon;
        SYSMET(CYICON)   = dr.yCompressIcon;
        SYSMET(CXCURSOR) = dr.xCompressCursor;
        SYSMET(CYCURSOR) = dr.yCompressCursor;

    } else {

        /*
         * Else, only the ratio of (64/icon dimensions) is kept there.
         */
        SYSMET(CXICON)   = (64 / dr.xCompressIcon);
        SYSMET(CYICON)   = (64 / dr.yCompressIcon);
        SYSMET(CXCURSOR) = (64 / dr.xCompressCursor);
        SYSMET(CYCURSOR) = (64 / dr.yCompressCursor);
    }

    SYSMET(CXSMICON) = SYSMET(CXICON) / 2;
    SYSMET(CYSMICON) = SYSMET(CYICON) / 2;

    SYSMET(CYKANJIWINDOW) = dr.yKanji;

    /*
     * Get border thicknesses.
     */
    SYSMET(CXBORDER) = dr.cxBorder;
    SYSMET(CYBORDER) = dr.cyBorder;

    /*
     * Edge is two borders.
     */
    SYSMET(CXEDGE) = 2 * SYSMET(CXBORDER);
    SYSMET(CYEDGE) = 2 * SYSMET(CYBORDER);

    /*
     * Fixed frame is outer edge + border.
     */
    SYSMET(CXDLGFRAME) = SYSMET(CXEDGE) + SYSMET(CXBORDER);
    SYSMET(CYDLGFRAME) = SYSMET(CYEDGE) + SYSMET(CYBORDER);

    if (gbRemoteSession) {
        return;
    }

    prc = &GetPrimaryMonitor()->rcMonitor;
    SYSMET(CXFULLSCREEN) = prc->right;
    SYSMET(CYFULLSCREEN) = prc->bottom - SYSMET(CYCAPTION);

    /*
     * Set the initial cursor position to the center of the primary screen.
     */
    gpsi->ptCursor.x = prc->right / 2;
    gpsi->ptCursor.y = prc->bottom / 2;
}

/***************************************************************************\
* GetCharDimensions
*
* This function loads the Textmetrics of the font currently selected into
* the hDC and returns the Average char width of the font; Pl Note that the
* AveCharWidth value returned by the Text metrics call is wrong for
* proportional fonts. So, we compute them On return, lpTextMetrics contains
* the text metrics of the currently selected font.
*
* History:
* 10-Nov-1993 mikeke   Created
\***************************************************************************/
int GetCharDimensions(
        HDC          hdc,
        TEXTMETRIC*  lptm,
        LPINT        lpcy
        )
{
    TEXTMETRIC tm;

    /*
     * Didn't find it in cache, store the font metrics info.
     */
    if (!_GetTextMetricsW(hdc, &tm)) {
        RIPMSG1(RIP_WARNING, "GetCharDimensions: _GetTextMetricsW failed. hdc %#lx", hdc);
        tm = gpsi->tmSysFont; // damage control

        if (tm.tmAveCharWidth == 0) {
            RIPMSG0(RIP_WARNING, "GetCharDimensions: _GetTextMetricsW first time failure");
            tm.tmAveCharWidth = 8;
        }
    }
    if (lptm != NULL)
        *lptm = tm;
    if (lpcy != NULL)
        *lpcy = tm.tmHeight;

    /*
     * If variable_width font
     */
    if (tm.tmPitchAndFamily & TMPF_FIXED_PITCH) {
        SIZE size;
        static CONST WCHAR wszAvgChars[] =
                L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

        /*
         * Change from tmAveCharWidth. We will calculate a true average
         * as opposed to the one returned by tmAveCharWidth. This works
         * better when dealing with proportional spaced fonts.
         */
        if (GreGetTextExtentW(
                hdc, (LPWSTR)wszAvgChars,
                (sizeof(wszAvgChars) / sizeof(WCHAR)) - 1,
                &size, GGTE_WIN3_EXTENT)) {

            UserAssert((((size.cx / 26) + 1) / 2) > 0);
            return ((size.cx / 26) + 1) / 2;    // round up
        } else {
            RIPMSG1(RIP_WARNING, "GetCharDimensions: GreGetTextExtentW failed. hdc %#lx", hdc);
        }
    }

    UserAssert(tm.tmAveCharWidth > 0);

    return tm.tmAveCharWidth;
}


/**************************************************************************\
* InitVideo
*
* Create pmdev.
*
* 03-March-1998 CLupu  Moved from UserInitialize code
\**************************************************************************/
PMDEV InitVideo(
    BOOL bReenumerationNeeded)
{
    PMDEV pmdev;
    LONG  ChangeStatus;

    /*
     * NOTE: Need to get a status return from this call.
     * DrvInitConsole doesn't return failure, if or when it does we
     * should check for it and return NULL. However there seem to be
     * no known problems related to this.
     */

    DrvInitConsole(bReenumerationNeeded);

    /*
     * BASEVIDEO may be on or off, whether we are in setup or not.
     */

    ChangeStatus = DrvChangeDisplaySettings(NULL,
                                            NULL,
                                            NULL,
                                            (PVOID)GW_DESKTOP_ID,
                                            KernelMode,
                                            FALSE,
                                            TRUE,
                                            NULL,
                                            &pmdev,
                                            GRE_DEFAULT,
                                            TRUE);


    if (ChangeStatus != GRE_DISP_CHANGE_SUCCESSFUL) {
        /*
         * If we fail, try BASEVIDEO temporarily.
         */
        DrvSetBaseVideo(TRUE);

        ChangeStatus = DrvChangeDisplaySettings(NULL,
                                                NULL,
                                                NULL,
                                                (PVOID)GW_DESKTOP_ID,
                                                KernelMode,
                                                FALSE,
                                                TRUE,
                                                NULL,
                                                &pmdev,
                                                GRE_DEFAULT,
                                                TRUE);

        DrvSetBaseVideo(FALSE);

        /*
         * Give it one last try, not in basevideo, to handle TGA
         * (non-vgacompatible) during GUI-mode setup (BASEVIDEO is on by
         * default)
         */

        if (ChangeStatus != GRE_DISP_CHANGE_SUCCESSFUL) {
            ChangeStatus = DrvChangeDisplaySettings(NULL,
                                                    NULL,
                                                    NULL,
                                                    (PVOID)GW_DESKTOP_ID,
                                                    KernelMode,
                                                    FALSE,
                                                    TRUE,
                                                    NULL,
                                                    &pmdev,
                                                    GRE_DEFAULT,
                                                    TRUE);

        }
    }

    if (ChangeStatus != GRE_DISP_CHANGE_SUCCESSFUL) {
        RIPMSG0(RIP_WARNING, "InitVideo: No working display driver found");
        return NULL;
    }

    /*
     * Remove the basevideo flag. We want it set only on the first modeset.
     */
    DrvSetBaseVideo(FALSE);

    gpDispInfo->hDev  = pmdev->hdevParent;
    gpDispInfo->pmdev = pmdev;

    GreUpdateSharedDevCaps(gpDispInfo->hDev);

    if (!InitUserScreen()) {
        RIPMSG0(RIP_WARNING, "InitUserScreen failed");
        return NULL;
    }

    HYDRA_HINT(HH_INITVIDEO);

    return pmdev;
}

VOID DrvDriverFailure(
    VOID)
{
    KeBugCheckEx(VIDEO_DRIVER_INIT_FAILURE,
                 0,
                 0,
                 0,
                 USERCURRENTVERSION);
}

/***************************************************************************\
* BeginBootPhase
*
* Let's kernel know of boot progress on the console.
*
* 10-Apr-2001 Cenke  Created.
\***************************************************************************/
NTSTATUS BeginBootPhase(
    PF_BOOT_PHASE_ID Phase)
{
    PREFETCHER_INFORMATION PrefetcherInformation;
    NTSTATUS Status;

    /*
     * We should be called only on the console.
     */

    UserAssert(gSessionId == 0);

    /*
     * Setup the structure that will be passed to system call.
     */

    PrefetcherInformation.Magic = PF_SYSINFO_MAGIC_NUMBER;
    PrefetcherInformation.Version = PF_CURRENT_VERSION;
    PrefetcherInformation.PrefetcherInformationClass = PrefetcherBootPhase;
    PrefetcherInformation.PrefetcherInformation = &Phase;
    PrefetcherInformation.PrefetcherInformationLength = sizeof(Phase);

    /*
     * Call system and return status to caller.
     */

    Status = ZwSetSystemInformation(SystemPrefetcherInformation,
                                    &PrefetcherInformation,
                                    sizeof(PrefetcherInformation));

    return Status;
}

/**************************************************************************\
* ReserveUserSessionViews
*
* Reserve user needed session views beforhand to prevert GDI from eating
* uo the session view space when hardware acceleration is turned off.
*
* 27-Nov-2001 MSadek  Created it
\**************************************************************************/
NTSTATUS
ReserveUserSessionViews(
    PHANDLE phSection,
    PPVOID  ppMapBase)
{
    LARGE_INTEGER SectionSize;
    ULONG         ulSectionSize;
    SIZE_T        ulViewSize;
    NTSTATUS      Status;

    /*
     * We need to reserve session view space for:
     * - Winlogon desktop heap.
     * - Disconnect desktop heap.
     * - Winlogon desktop heap.
     * - Services desktops heap (how many?, let's have a factor of safety here).
     */

    ulSectionSize = GetDesktopHeapSize(DHS_LOGON) +
                    GetDesktopHeapSize(DHS_DISCONNECT) +
                    GetDesktopHeapSize(0) +
                    GetDesktopHeapSize(DHS_NOIO) * NOIO_DESKTOP_NUMBER;

    SectionSize.QuadPart = ulSectionSize;

    Status = Win32CreateSection(phSection,
                                SECTION_ALL_ACCESS,
                                (POBJECT_ATTRIBUTES)NULL,
                                &SectionSize,
                                PAGE_EXECUTE_READWRITE,
                                SEC_RESERVE,
                                (HANDLE)NULL,
                                NULL,
                                TAG_SECTION_DESKTOP);

    if (!NT_SUCCESS( Status )) {
        RIPNTERR0(Status,
                  RIP_WARNING,
                  "Can't create section for reserved session views.");
        return Status;
    }

    ulViewSize = ulSectionSize;
    *ppMapBase = NULL;

    Status = Win32MapViewInSessionSpace(*phSection, ppMapBase, &ulViewSize);

    if (!NT_SUCCESS(Status)) {
        RIPNTERR0(Status,
                  RIP_WARNING,
                  "Can't map section for for reserved session views into session space.");
        Win32DestroySection(&phSection);
    }

    return Status;
}

/**************************************************************************\
* UserInitialize
*
* Worker routine for user initialization.
*
* 25-Aug-1995 ChrisWil  Created comment block/Multiple desktop support.
* 15-Dec-1995 BradG     Modified to return MediaChangeEvent Handle.
\**************************************************************************/
NTSTATUS
UserInitialize(
    VOID)
{
    NTSTATUS Status;
    DWORD    dwData;
    HANDLE   hSection;
    PVOID    pSectionBase;

#if DBG
    /*
     * Allow a trace of all the init stuff going on related to display
     * drivers. Useful to debug boot time problems related to graphics.
     */
    if (RtlGetNtGlobalFlags() & FLG_SHOW_LDR_SNAPS) {
        TraceInitialization = 1;
    }
#endif

    TRACE_INIT(("Entering UserInitialize\n"));

    EnterCrit();

    HYDRA_HINT(HH_USERINITIALIZE);

    if (ISTS() && gbRemoteSession) {
        swprintf(szWindowStationDirectory, L"%ws\\%ld%ws",
                 SESSION_ROOT, gSessionId, WINSTA_DIR);
    } else {
        wcscpy(szWindowStationDirectory, WINSTA_DIR);
    }

    /*
     * Create WindowStation object directory.
     */
    Status = InitCreateObjectDirectory();

    if (!NT_SUCCESS(Status)) {
        RIPMSG1(RIP_WARNING, "InitCreateObjectDirectory failed with Status %x",
                Status);

        goto Exit;
    }

    /*
     * Create the event for  PnP RequestDeviceChangeSyncronization
     */
    gpEventPnPWainting = CreateKernelEvent(SynchronizationEvent, TRUE);

    if (gpEventPnPWainting == NULL) {
        RIPMSG0(RIP_WARNING, "Failed to create gpEventPnPWainting");
        Status = STATUS_NO_MEMORY;
        goto Exit;
    }

    /*
     * Read the registry configuration for the multimon snapsot flag
     */

    if (FastGetProfileIntFromID(NULL,PMAP_TS, STR_SNAPSHOTMONITORS, 1, &dwData, 0)) {
        if (dwData != 0) {
            gbSnapShotWindowsAndMonitors = TRUE;
        } else {
            gbSnapShotWindowsAndMonitors = FALSE;
        }
    }  else{
        gbSnapShotWindowsAndMonitors = TRUE;
    }



    /*
     * Create the event for the diconnect desktop creation
     */
    gpEventDiconnectDesktop = CreateKernelEvent(SynchronizationEvent, FALSE);

    if (gpEventDiconnectDesktop == NULL) {
        RIPMSG0(RIP_WARNING, "Failed to create gpEventDiconnectDesktop");
        Status = STATUS_NO_MEMORY;
        goto Exit;
    }

    /*
     * WinStations get init'ed on the first connect.
     */
    if (!gbRemoteSession) {
        BeginBootPhase(PfVideoInitPhase);

        /* If hardware acceleration is turned off, GDI will map huge sections for the panning
         * surfaces. We might end up exceeding the session view size quota before mapping the heap
         * section for critical desktops.
         * Let's pre-reserve approximately what we need now.
         */
        Status = ReserveUserSessionViews(&hSection, &pSectionBase);
        if (!NT_SUCCESS(Status)) {
            goto Exit;
        }

        if (InitVideo(TRUE) == NULL) {
            DrvDriverFailure();
        }

        Status = Win32UnmapViewInSessionSpace(pSectionBase);
        Win32DestroySection(hSection);

        BeginBootPhase(PfPostVideoInitPhase);

        /*
         * Do this here so power callouts
         * have the pmdev in gpDispInfo set
         */
        gbVideoInitialized = TRUE;
    }

    gbUserInitialized = TRUE;

    /*
     * Now that the system is initialized, allocate a pti for this thread.
     */
    Status = xxxCreateThreadInfo(PsGetCurrentThread());
    if (!NT_SUCCESS(Status)) {
        RIPMSG1(RIP_WARNING,
                "xxxCreateThreadInfo failed during UserInitialize with Status 0x%x",
                Status);
        goto Exit;
    }

    /*
     * Initialize Global RIP flags (debug only).
     */
    InitGlobalRIPFlags();

    /*
     * WinStations get init'ed on the first connect.
     */
    if (!gbRemoteSession) {
        UserVerify(LW_BrushInit());
    }

    InitLoadResources();

Exit:
    LeaveCrit();

    TRACE_INIT(("Leaving UserInitialize\n"));

    return Status;
}

/**************************************************************************\
* IsDBCSEnabledSystem
*
* check if the system is configured as FE Enabled
*
* 07-Feb-1997 HiroYama  Created
\**************************************************************************/
__inline BOOL IsDBCSEnabledSystem(
    VOID)
{
    return !!NLS_MB_CODE_PAGE_TAG;
}


BOOL IsIMMEnabledSystem(
    VOID)
{
    DWORD dwRet = 0;

    /*
     * If the entire system is DBCS enabled, IMM/IME should be activated
     * anyway.
     */
    if (IsDBCSEnabledSystem()) {
        return TRUE;
    }

    FastGetProfileDwordW(NULL, PMAP_IMM, TEXT("LoadIMM"), 0, &dwRet, 0);
    return dwRet;
}

#ifdef CUAS_ENABLE

BOOL IsCTFIMEEnabledSystem(
    VOID)
{
    DWORD dwRet = 0;

    FastGetProfileDwordW(NULL, PMAP_IMM, TEXT("DontLoadCTFIME"), 0, &dwRet, 0);
    if (dwRet)
        return FALSE;

    return IsIMMEnabledSystem();
}

#endif

/*
 * Get ACP and check if the system is configured as ME Enabled.
 */
BOOL IsMidEastEnabledSystem(
    VOID)
{
    extern __declspec(dllimport) USHORT NlsAnsiCodePage;
    /*
     * 1255 == Hebrew and 1256 == Arabic.
     */
    if (NlsAnsiCodePage == 1255 || NlsAnsiCodePage == 1256) {
        return TRUE;
    }

    return FALSE;
}

/***************************************************************************\
* SetupClassAtoms
*
* 10/01/1998   clupu          moved from Win32UserInitialize
\***************************************************************************/
BOOL SetupClassAtoms(
    VOID)
{
    BOOL fSuccess = TRUE;
    int  ind;

    /*
     * Set up class atoms.
     *
     * HACK: Controls are registered on the client side so we can't fill in
     * their atomSysClass entry the same way we do for the other classes.
     */
    for (ind = ICLS_BUTTON; ind < ICLS_CTL_MAX; ind++) {
        gpsi->atomSysClass[ind] = UserAddAtom(lpszControls[ind], TRUE);
        fSuccess &= !!gpsi->atomSysClass[ind];
    }

    gpsi->atomSysClass[ICLS_DIALOG]    = PTR_TO_ID(DIALOGCLASS);
    gpsi->atomSysClass[ICLS_ICONTITLE] = PTR_TO_ID(ICONTITLECLASS);
    gpsi->atomSysClass[ICLS_TOOLTIP]   = PTR_TO_ID(TOOLTIPCLASS);
    gpsi->atomSysClass[ICLS_DESKTOP]   = PTR_TO_ID(DESKTOPCLASS);
    gpsi->atomSysClass[ICLS_SWITCH]    = PTR_TO_ID(SWITCHWNDCLASS);
    gpsi->atomSysClass[ICLS_MENU]      = PTR_TO_ID(MENUCLASS);

    gpsi->atomContextHelpIdProp = UserAddAtom(szCONTEXTHELPIDPROP, TRUE);
    fSuccess &= !!gpsi->atomContextHelpIdProp;

    gpsi->atomIconSmProp        = UserAddAtom(szICONSM_PROP_NAME, TRUE);
    fSuccess &= !!gpsi->atomIconSmProp;

    gpsi->atomIconProp          = UserAddAtom(szICON_PROP_NAME, TRUE);
    fSuccess &= !!gpsi->atomIconProp;

    gpsi->uiShellMsg            = UserAddAtom(szSHELLHOOK, TRUE);
    fSuccess &= !!gpsi->uiShellMsg;

    /*
     * Initialize the integer atoms for our magic window properties
     */
    atomCheckpointProp = UserAddAtom(szCHECKPOINT_PROP_NAME, TRUE);
    fSuccess &= !!atomCheckpointProp;

    atomDDETrack = UserAddAtom(szDDETRACK_PROP_NAME, TRUE);
    fSuccess &= !!atomDDETrack;

    atomQOS = UserAddAtom(szQOS_PROP_NAME, TRUE);
    fSuccess &= !!atomQOS;

    atomDDEImp = UserAddAtom(szDDEIMP_PROP_NAME, TRUE);
    fSuccess &= !!atomDDEImp;

    atomWndObj = UserAddAtom(szWNDOBJ_PROP_NAME, TRUE);
    fSuccess &= !!atomWndObj;

    atomImeLevel = UserAddAtom(szIMELEVEL_PROP_NAME, TRUE);
    fSuccess &= !!atomImeLevel;

    atomLayer = UserAddAtom(szLAYER_PROP_NAME, TRUE);
    fSuccess &= !!atomLayer;

    guiActivateShellWindow = UserAddAtom(szACTIVATESHELLWINDOW, TRUE);
    fSuccess &= !!guiActivateShellWindow;

    guiOtherWindowCreated = UserAddAtom(szOTHERWINDOWCREATED, TRUE);
    fSuccess &= !!guiOtherWindowCreated;

    guiOtherWindowDestroyed = UserAddAtom(szOTHERWINDOWDESTROYED, TRUE);
    fSuccess &= !!guiOtherWindowDestroyed;

    gatomMessage = UserAddAtom(szMESSAGE, TRUE);
    fSuccess &= !!gatomMessage;

#ifdef HUNGAPP_GHOSTING
    gatomGhost = UserAddAtom(szGHOST, TRUE);
    fSuccess &= !!gatomGhost;
#endif

    gatomShadow = UserAddAtom(szSHADOW, TRUE);
    fSuccess &= !!gatomShadow;

    gaOleMainThreadWndClass = UserAddAtom(szOLEMAINTHREADWNDCLASS, TRUE);
    fSuccess &= !!gaOleMainThreadWndClass;

    gaFlashWState = UserAddAtom(szFLASHWSTATE, TRUE);
    fSuccess &= !!gaFlashWState;

    gatomLastPinned = gaOleMainThreadWndClass;

    return fSuccess;
}

/**************************************************************************\
* IsDesktopHeapLoggingOn
*
* Checks the registry to determine if we should write a message to the
* event log on desktop heap allocation failures.
*
* 05-19-2002    JasonSch    Created.
\**************************************************************************/
BOOL IsDesktopHeapLoggingOn(
    VOID)
{
    DWORD dwDesktopHeapLogging;

    FastGetProfileDwordW(NULL,
                         PMAP_WINDOWSM,
                         L"DesktopHeapLogging",
                         0,
                         &dwDesktopHeapLogging,
                         0);

    return (dwDesktopHeapLogging != 0);
}

/**************************************************************************\
* Win32UserInitialize
*
* Worker routine for user initialization called from Win32k's DriverEntry.
\**************************************************************************/
NTSTATUS Win32UserInitialize(
    VOID)
{
    NTSTATUS Status;
    POBJECT_TYPE_INITIALIZER pTypeInfo;
    LONG lTemp;

    TRACE_INIT(("Entering Win32UserInitialize\n"));

    /*
     * Create the shared section.
     */
    Status = InitCreateSharedSection();
    if (!NT_SUCCESS(Status)) {
        RIPMSG1(RIP_WARNING,
                "InitCreateSharedSection failed with Status 0x%x",
                Status);
        return Status;
    }

    EnterCrit();

    /*
     * Initialize security stuff.
     */
    if (!InitSecurity()) {
        RIPMSG0(RIP_WARNING, "InitSecurity failed");
        goto ExitWin32UserInitialize;
    }

    /*
     * Fill in windowstation and desktop object types
     */
    pTypeInfo = &(*ExWindowStationObjectType)->TypeInfo;
    pTypeInfo->DefaultNonPagedPoolCharge = sizeof(WINDOWSTATION) + sizeof(KEVENT);
    pTypeInfo->DefaultPagedPoolCharge    = 0;
    pTypeInfo->MaintainHandleCount       = TRUE;
    pTypeInfo->ValidAccessMask           = WinStaMapping.GenericAll;
    pTypeInfo->GenericMapping            = WinStaMapping;

    pTypeInfo = &(*ExDesktopObjectType)->TypeInfo;
    pTypeInfo->DefaultNonPagedPoolCharge = sizeof(DESKTOP);
    pTypeInfo->DefaultPagedPoolCharge    = 0;
    pTypeInfo->MaintainHandleCount       = TRUE;
    pTypeInfo->ValidAccessMask           = DesktopMapping.GenericAll;
    pTypeInfo->GenericMapping            = DesktopMapping;

    /*
     * Get this process so we can use the profiles.
     */
    gpepInit = PsGetCurrentProcess();

    Status  = InitQEntryLookaside();
    Status |= InitSMSLookaside();
    Status |= UserRtlCreateAtomTable(USRINIT_ATOMBUCKET_SIZE);

    if (!NT_SUCCESS(Status)) {
        RIPMSG1(RIP_WARNING,
                "Initialization failure (Status = 0x%x)",
                Status);
        goto ExitWin32UserInitialize;
    }

    atomUSER32 = UserAddAtom(szUSER32, TRUE);

    gatomFirstPinned = atomUSER32;

    if (gatomFirstPinned == 0) {
        RIPMSG0(RIP_WARNING, "Could not create atomUSER32");
        goto ExitWin32UserInitialize;
    }

    /*
     * Initialize the user subsystem information.
     */
    if (!InitCreateUserSubsystem()) {
        RIPMSG0(RIP_WARNING, "InitCreateUserSubsystem failed");
        goto ExitWin32UserInitialize;
    }

    /*
     * Don't bail out if CreateSetupNameArray fails.
     */
    CreateSetupNameArray();

    /*
     * Allocated shared SERVERINFO structure.
     */
    if ((gpsi = (PSERVERINFO)SharedAlloc(sizeof(SERVERINFO))) == NULL) {
        RIPMSG0(RIP_WARNING, "Could not allocate SERVERINFO");
        goto ExitWin32UserInitialize;
    }

    /*
     * Set the default rip-flags to rip on just about everything. We'll
     * truly set this in the InitGlobalRIPFlags() routine. These are needed
     * so that we can do appropriate ripping during the rest of the init
     * calls.
     */
#if DBG
    SET_FLAG(gpsi->dwRIPFlags, RIPF_DEFAULT);
#endif

    /*
     * Make sure we will not get a division by zero if the initialization
     * will not complete correctly. Set these to their normal values.
     */
    gpsi->cxMsgFontChar = 6;
    gpsi->cyMsgFontChar = 13;
    gpsi->cxSysFontChar = 8;
    gpsi->cySysFontChar = 16;

    /*
     * Initialize the last time we updated system-wide LastSystemRITEventTickCount
     * on system shared page.
     */

    gpsi->dwLastSystemRITEventTickCountUpdate = 0;

    /*
     * Initialize the DISPLAYINFO structure.
     */
    gpDispInfo = SharedAlloc(sizeof(*gpDispInfo));
    if (!gpDispInfo) {
        RIPMSG0(RIP_WARNING, "Could not allocate gpDispInfo");
        goto ExitWin32UserInitialize;
    }

    InitDbgTags();

    SET_OR_CLEAR_SRVIF(SRVIF_LOGDESKTOPHEAPFAILURE, IsDesktopHeapLoggingOn());
    SET_OR_CLEAR_SRVIF(SRVIF_DBCS, IsDBCSEnabledSystem());
    SET_OR_CLEAR_SRVIF(SRVIF_IME, IsIMMEnabledSystem());
#ifdef CUAS_ENABLE
    SET_OR_CLEAR_SRVIF(SRVIF_CTFIME_ENABLED, IsCTFIMEEnabledSystem());
#endif // CUAS_ENABLE

    SET_OR_CLEAR_SRVIF(SRVIF_MIDEAST, IsMidEastEnabledSystem());

#if DBG
    SET_SRVIF(SRVIF_CHECKED);

#if !defined(CUAS_ENABLE)
    RIPMSG3(RIP_WARNING, "*** win32k: DBCS:[%d] IME:[%d] MiddleEast:[%d]",
            IS_DBCS_ENABLED(),
            IS_IME_ENABLED(),
            IS_MIDEAST_ENABLED());
#else
    RIPMSG4(RIP_WARNING, "*** win32k: DBCS:[%d] IME:[%d] MiddleEast:[%d] CTFIME:[%d]",
            IS_DBCS_ENABLED(),
            IS_IME_ENABLED(),
            IS_MIDEAST_ENABLED(),
            IS_CICERO_ENABLED());
#endif
#endif

    gpsi->dwDefaultHeapSize = gdwDesktopSectionSize * 1024;

    /*
     * Initialize procedures and message tables.
     * Initialize the class structures for Get/SetClassWord/Long.
     * Initialize message-box strings.
     * Initialize OLE-Formats (performance-hack).
     */
    InitFunctionTables();
    InitMessageTables();
#if DBG
    VerifySyncOnlyMessages();
#endif
    if (!InitOLEFormats()) {
        RIPMSG0(RIP_WARNING, "InitOLEFormats failed");
        goto ExitWin32UserInitialize;
    }

    /*
     * Set up class atoms
     */
    if (!SetupClassAtoms()) {
        RIPMSG0(RIP_WARNING, "SetupClassAtoms failed to register atoms");
        goto ExitWin32UserInitialize;
    }

    /*
     * Initialize the handle manager.
     */
    if (!HMInitHandleTable(gpvSharedBase)) {
        RIPMSG0(RIP_WARNING, "HMInitHandleTable failed");
        goto ExitWin32UserInitialize;
    }

    /*
     * Setup shared info block.
     */
    gSharedInfo.psi = gpsi;
    gSharedInfo.pDispInfo = gpDispInfo;

    /*
     * Determine if we have unsigned drivers installed
     * Use      431013F760B53515A754C5D1DBE1E8A20DD00D9E as current key to check.
     */
    if (NT_SUCCESS(RtlCheckRegistryKey(RTL_REGISTRY_ABSOLUTE, L"\\Registry\\Machine\\SOFTWARE\\Policies\\Microsoft\\SystemCertificates\\Root\\Certificates\\431013F760B53515A754C5D1DBE1E8A20DD00D9E")) ||
        NT_SUCCESS(RtlCheckRegistryKey(RTL_REGISTRY_ABSOLUTE, L"\\Registry\\Machine\\SOFTWARE\\Microsoft\\SystemCertificates\\Root\\Certificates\\431013F760B53515A754C5D1DBE1E8A20DD00D9E")) ||
        NT_SUCCESS(RtlCheckRegistryKey(RTL_REGISTRY_USER, L"\\SOFTWARE\\Microsoft\\SystemCertificates\\Root\\Certificates\\431013F760B53515A754C5D1DBE1E8A20DD00D9E"))) {
        gfUnsignedDrivers = TRUE;
    }

    FastGetProfileDwordW(NULL,
                         PMAP_WINDOWSM,
                         L"USERProcessHandleQuota",
                         DEFAULT_USER_HANDLE_QUOTA,
                         &lTemp,
                         0);
    gUserProcessHandleQuota = lTemp;

    /*
     * The maximum number of posted message for a thread.
     */
    FastGetProfileDwordW(NULL,
                         PMAP_WINDOWSM,
                         L"USERPostMessageLimit",
                         DEFAULT_POSTMESSAGE_LIMIT,
                         &lTemp,
                         0);
    if (lTemp == 0) {
        /*
         * 0 means (virtually) no limit.
         */
        gUserPostMessageLimit = ~0;
    } else {
        gUserPostMessageLimit = lTemp;
    }

    if (!gDrawVersionAlways) {
        FastGetProfileDwordW(NULL,
                             PMAP_WINDOWSM,
                             L"DisplayVersion",
                             0,
                             &gDrawVersionAlways,
                             0);
    }

    FastGetProfileDwordW(NULL,
                         PMAP_TS_EXCLUDE_DESKTOP_VERSION,
                         L"TSExcludeDesktopVersion",
                         0,
                         &gdwTSExcludeDesktopVersion,
                         0);

    FastGetProfileDwordW(NULL,
                         PMAP_TABLETPC,
                         L"Installed",
                         0,
                         &SYSMET(TABLETPC),
                         0);

    FastGetProfileDwordW(NULL,
                         PMAP_MEDIACENTER,
                         L"Installed",
                         0,
                         &SYSMET(MEDIACENTER),
                         0);

    /*
     * Initialize SMWP structure.
     */
    if (!AllocateCvr(&gSMWP, 4)) {
        RIPMSG0(RIP_WARNING, "AllocateCvr failed");
        goto ExitWin32UserInitialize;
    }
    LeaveCrit();

    UserAssert(NT_SUCCESS(Status));
    return Status;

ExitWin32UserInitialize:
    LeaveCrit();

    if (NT_SUCCESS(Status)) {
        Status = STATUS_NO_MEMORY;
    }

    RIPMSG1(RIP_WARNING, "UserInitialize failed with Status = 0x%x", Status);
    return Status;
}


/**************************************************************************\
* UserGetDesktopDC
*
* 09-Jan-1992 mikeke created
*    Dec-1993 andreva changed to support desktops.
\**************************************************************************/

HDC UserGetDesktopDC(
    ULONG type,
    BOOL  bAltType,
    BOOL  bValidate)
{
    PETHREAD    Thread;
    HDC         hdc;
    PTHREADINFO pti = PtiCurrentShared();  // This is called from outside the crit sec
    HDEV        hdev  = gpDispInfo->hDev;

    if (bValidate && type != DCTYPE_INFO &&
        IS_THREAD_RESTRICTED(pti, JOB_OBJECT_UILIMIT_HANDLES)) {

        UserAssert(pti->rpdesk != NULL);

        if (!ValidateHwnd(PtoH(pti->rpdesk->pDeskInfo->spwnd))) {
            RIPMSG0(RIP_WARNING,
                    "UserGetDesktopDC fails desktop window validation");
            return NULL;
        }
    }

    /*
     * NOTE:
     * This is a real nasty trick to get both DCs created on a desktop on
     * a different device to work (for the video applet) and to be able
     * to clip DCs that are actually on the same device ...
     */
    if (pti && pti->rpdesk)
        hdev = pti->rpdesk->pDispInfo->hDev;

    /*
     * We want to turn this call that was originally OpenDC("Display", ...)
     * into GetDC null call so this DC will be clipped to the current
     * desktop or else the DC can write to any desktop. Only do this
     * for client apps; let the server do whatever it wants.
     */
    Thread = PsGetCurrentThread();
    if ((type != DCTYPE_DIRECT)  ||
        (hdev != gpDispInfo->hDev) ||
        PsIsSystemThread(Thread) ||
        (PsGetThreadProcess(Thread) == gpepCSRSS)) {

        hdc = GreCreateDisplayDC(hdev, type, bAltType);

    } else {

        PDESKTOP pdesk;

        EnterCrit();

        if (pdesk = PtiCurrent()->rpdesk) {

            hdc = _GetDCEx(pdesk->pDeskInfo->spwnd,
                           NULL,
                           DCX_WINDOW | DCX_CACHE | DCX_CREATEDC);
        } else {
            hdc = NULL;
        }

        LeaveCrit();
    }

    return hdc;
}


/**************************************************************************\
* UserThreadCallout
*
*
* Called by the kernel when a thread starts or ends.
*
* Dec-1993 andreva created.
\**************************************************************************/

NTSTATUS UserThreadCallout(
    IN PETHREAD pEThread,
    IN PSW32THREADCALLOUTTYPE CalloutType)
{
    PTHREADINFO pti;
    NTSTATUS    Status = STATUS_SUCCESS;

    UserAssert(gpresUser != NULL);

    switch (CalloutType) {
        case PsW32ThreadCalloutInitialize:
            TRACE_INIT(("Entering UserThreadCallout PsW32ThreadCalloutInitialize\n"));

            if (gbNoMorePowerCallouts) {
                RIPMSG0(RIP_WARNING, "No more GUI threads allowed");
                return STATUS_UNSUCCESSFUL;
            }

            /*
             * Only create a thread info structure if we're initialized.
             */
            if (gbUserInitialized) {
                EnterCrit();
                UserAssert(gpepCSRSS != NULL);

                /*
                 * Initialize this thread
                 */
                Status = xxxCreateThreadInfo(pEThread);

                LeaveCrit();
            }
            break;

        case PsW32ThreadCalloutExit:

            TRACE_INIT(("Entering UserThreadCallout PsW32ThreadCalloutExit\n"));

            /*
             * If we aren't already inside the critical section, enter it.
             * Because this is the first pass, we remain in the critical
             * section when we return so that our try/finally handlers
             * are protected by the critical section.
             * EnterCrit here before GreUnlockDisplay() provides a pti which
             * may be required if unlocking the display may release some
             * deferred WinEvents, for which a pti is required.
             */
            EnterCrit();

            pti = (PTHREADINFO)PsGetThreadWin32Thread(pEThread);

            /*
             * Mark this thread as in the middle of cleanup. This is useful for
             * several problems in USER where we need to know this information.
             */
            pti->TIF_flags |= TIF_INCLEANUP;

            /*
             * If we died during a full screen switch make sure we cleanup
             * correctly
             */
            FullScreenCleanup();

            /*
             * Cleanup gpDispInfo->hdcScreen - if we crashed while using it,
             * it may have owned objects still selected into it. Cleaning
             * it this way will ensure that gdi doesn't try to delete these
             * objects while they are still selected into this public hdc.
             */

            /*
             * WinStations that haven't gone through the first connect do not
             * have any of the graphics setup.
             */
            if (!gbRemoteSession || gbVideoInitialized) {
                GreCleanDC(gpDispInfo->hdcScreen);
            }

            /*
             * This thread is exiting execution; xxxDestroyThreadInfo cleans
             *  up everything that can go now
             */
            UserAssert(pti == PtiCurrent());
            xxxDestroyThreadInfo();
            LeaveCrit();

            break;
    }

    TRACE_INIT(("Leaving UserThreadCallout\n"));

    return Status;
}

/**************************************************************************\
* NtUserInitialize
*
* 01-Dec-1993 andreva created.
* 01-Dec-1995 BradG   Modified to return handle to Media Change Event
\**************************************************************************/

BOOL TellGdiToGetReady();

NTSTATUS NtUserInitialize(
    IN DWORD   dwVersion,
    IN HANDLE  hPowerRequestEvent,
    IN HANDLE  hMediaRequestEvent)
{
    NTSTATUS Status;

    TRACE_INIT(("Entering NtUserInitialize\n"));

    /*
     * Make sure we're not trying to load this twice.
     */
    if (gpepCSRSS != NULL) {
        RIPMSG0(RIP_WARNING, "Can't initialize more than once");
        return STATUS_UNSUCCESSFUL;
    }

    /*
     * Check version number
     */
    if (dwVersion != USERCURRENTVERSION) {
        KeBugCheckEx(WIN32K_INIT_OR_RIT_FAILURE,
                     0,
                     0,
                     dwVersion,
                     USERCURRENTVERSION);
    }

    /*
     * Get the session ID from the EPROCESS structure
     */
    gSessionId = PsGetCurrentProcessSessionId();
    UserAssert(gSessionId == 0 || gbRemoteSession == TRUE);

    Status = InitializePowerRequestList(hPowerRequestEvent);
    if (!NT_SUCCESS(Status)) {
        return Status;
    }

    Status = InitializeMediaChange(hMediaRequestEvent);
    if (!NT_SUCCESS(Status)) {
        return Status;
    }

    /*
     * Save the system process structure.
     */
    gpepCSRSS = PsGetCurrentProcess();
    ObReferenceObject(gpepCSRSS);
    if (!TellGdiToGetReady()) {
        RIPMSG0(RIP_WARNING, "TellGdiToGetReady failed");
        Status = STATUS_UNSUCCESSFUL;
        return Status;
    }

    /*
     * Allow CSR to read the screen
     */
    ((PW32PROCESS)PsGetProcessWin32Process(gpepCSRSS))->W32PF_Flags |= (W32PF_READSCREENACCESSGRANTED|W32PF_IOWINSTA);


    Status = UserInitialize();

    TRACE_INIT(("Leaving NtUserInitialize\n"));
    return Status;
}

/**************************************************************************\
* NtUserProcessConnect
*
* 01-Dec-1993   Andreva     Created.
\**************************************************************************/

NTSTATUS NtUserProcessConnect(
    IN HANDLE    hProcess,
    IN OUT PVOID pConnectInfo,
    IN ULONG     cbConnectInfo)
{
    PEPROCESS    Process;
    PUSERCONNECT pucConnect = (PUSERCONNECT)pConnectInfo;
    USERCONNECT  ucLocal;
    NTSTATUS     Status = STATUS_SUCCESS;


    TRACE_INIT(("Entering NtUserProcessConnect\n"));

    if (!pucConnect || (cbConnectInfo != sizeof(USERCONNECT))) {
        return STATUS_UNSUCCESSFUL;
    }

    try {
        ProbeForWrite(pucConnect, cbConnectInfo, sizeof(DWORD));

        ucLocal = *pucConnect;
    } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
        return GetExceptionCode();
    }

    /*
     * Check client/server versions.
     */
    if (ucLocal.ulVersion != USERCURRENTVERSION) {

        RIPMSG2(RIP_ERROR,
            "Client version %lx > server version %lx\n",
            ucLocal.ulVersion, USERCURRENTVERSION);
        return STATUS_UNSUCCESSFUL;
    }



    if (ucLocal.dwDispatchCount != gDispatchTableValues) {
        RIPMSG2(RIP_ERROR,
            "!!!! Client Dispatch info %lX != Server %lX\n",
            ucLocal.dwDispatchCount, gDispatchTableValues);
    }


    /*
     * Reference the process.
     */
    Status = ObReferenceObjectByHandle(hProcess,
                                       PROCESS_VM_OPERATION,
                                       *PsProcessType,
                                       UserMode,
                                       &Process,
                                       NULL);
    if (!NT_SUCCESS(Status))
        return Status;
    /*
     * Return client's view of shared data.
     */
    EnterCrit();
    Status = InitMapSharedSection(Process, &ucLocal);
    LeaveCrit();

    if (!NT_SUCCESS(Status)) {
        RIPMSG2(RIP_WARNING,
              "Failed to map shared data into client %x, status = %x\n",
              PsGetCurrentProcessId(), Status);
    }

    ObDereferenceObject(Process);

    if (NT_SUCCESS(Status)) {

        try {
             *pucConnect = ucLocal;
        } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
            Status = GetExceptionCode();
        }
    }

    TRACE_INIT(("Leaving NtUserProcessConnect\n"));

    return Status;
}

/**************************************************************************\
* xxxUserProcessCallout
*
* 01-Dec-1993   andreva     Created.
\**************************************************************************/

NTSTATUS xxxUserProcessCallout(
    IN PW32PROCESS Process,
    IN BOOLEAN     Initialize)
{
    NTSTATUS     Status = STATUS_SUCCESS;

    if (Initialize) {

        TRACE_INIT(("Entering xxxUserProcessCallout Initialize\n"));

        UserAssert(gpresUser != NULL);
        EnterCrit();

        /*
         * Initialize the important process level stuff.
         */
        Status = xxxInitProcessInfo(Process);

        LeaveCrit();

        if (Status == STATUS_SUCCESS) {

            PEJOB Job = PsGetProcessJob(Process->Process);

            if (Job != NULL &&
                PsGetJobUIRestrictionsClass(Job) != 0) {

                WIN32_JOBCALLOUT_PARAMETERS Parms;

                /*
                 * aquire the job's lock and after that enter the user
                 * critical section.
                 */
                KeEnterCriticalRegion();
                ExAcquireResourceExclusiveLite(PsGetJobLock(Job), TRUE);

                Parms.Job = Job;
                Parms.CalloutType = PsW32JobCalloutAddProcess;
                Parms.Data = Process;

                UserAssert(PsGetJobSessionId(Job) == PsGetProcessSessionId(Process->Process));

                UserJobCallout(&Parms);

                ExReleaseResourceLite(PsGetJobLock(Job));
                KeLeaveCriticalRegion();
            }
        }
    } else {

        int  i;
        PHE  phe;
        PDCE *ppdce;
        PDCE pdce;

        TRACE_INIT(("Entering xxxUserProcessCallout Cleanup\n"));

        UserAssert(gpresUser != NULL);

        EnterCrit();

#if DBG
        if (Process->Process == gpepCSRSS) {

            /*
             * CSRSS should be the last to go ...
             */
            UserAssert(gppiList->ppiNextRunning == NULL);
        }
#endif // DBG

        if (Process->Process && PsGetProcessJob(Process->Process) != NULL) {
            RemoveProcessFromJob((PPROCESSINFO)Process);
        }

        /*
         * DestroyProcessInfo will return TRUE if any threads ever
         * connected. If nothing ever connected, we needn't do
         * this cleanup.
         */
        if (DestroyProcessInfo(Process)) {

            /*
             * See if we can compact the handle table.
             */
            i = giheLast;
            phe = &gSharedInfo.aheList[giheLast];
            while ((phe > &gSharedInfo.aheList[0]) && (phe->bType == TYPE_FREE)) {
                phe--;
                giheLast--;
            }

            /*
             * Scan the DC cache to find any DC's that need to be destroyed.
             */
            for (ppdce = &gpDispInfo->pdceFirst; *ppdce != NULL; ) {

                pdce = *ppdce;
                if (pdce->DCX_flags & DCX_DESTROYTHIS)
                    DestroyCacheDC(ppdce, pdce->hdc);

                /*
                 * Step to the next DC. If the DC was deleted, there
                 * is no need to calculate the address of the next entry.
                 */
                if (pdce == *ppdce)
                    ppdce = &pdce->pdceNext;
            }
        }

        UserAssert(gpresUser != NULL);

        LeaveCrit();
    }

    TRACE_INIT(("Leaving xxxUserProcessCallout\n"));

    return Status;
}

/**************************************************************************\
* UserGetHDEV
*
* Provided as a means for GDI to get a hold of USER's hDev.
*
* 01-Jan-1996   ChrisWil    Created.
\**************************************************************************/

HDEV UserGetHDEV(VOID)
{

    /*
     *  NOTE: This is busted.
     *        This need to return the device for the current desktop.
     *        The graphics device may not be the same for all desktops.
     *        -Andre
     */
    return gpDispInfo->hDev;
}

/**************************************************************************\
* _UserGetGlobalAtomTable
*
* This function is called by the kernel mode global atom manager to get the
* address of the current thread's global atom table.
*
* Pointer to the global atom table for the current thread or NULL if unable
* to access it.
\**************************************************************************/
PVOID UserGlobalAtomTableCallout(
    VOID)
{
    PETHREAD       Thread;
    PTHREADINFO    pti;
    PWINDOWSTATION pwinsta;
    PW32JOB        pW32Job;
    PEJOB          Job;
    PVOID          GlobalAtomTable = NULL;

    Thread = PsGetCurrentThread();
    pti = PtiFromThread(Thread);

    EnterCrit();

    /*
     * For restricted threads access the atom table off of the job object
     */
    if (pti != NULL && IS_THREAD_RESTRICTED(pti, JOB_OBJECT_UILIMIT_GLOBALATOMS)) {
        TAGMSG1(DBGTAG_Callout, "Retrieving global atom table for pti 0x%p", pti);

        pW32Job = pti->ppi->pW32Job;

        UserAssert(pW32Job->pAtomTable != NULL);
        GlobalAtomTable = pW32Job->pAtomTable;

        goto End;
    }

    Job = PsGetProcessJob(PsGetCurrentProcess());

    /*
     * Now handle the case where this is not a GUI thread/process
     * but it is assigned to a job that has JOB_OBJECT_UILIMIT_GLOBALATOMS
     * restrictions set. There is no easy way to convert this thread
     * to GUI.
     */
    if (pti == NULL && Job != NULL &&
        (PsGetJobUIRestrictionsClass(Job) & JOB_OBJECT_UILIMIT_GLOBALATOMS)) {

        /*
         * find the W32JOB in the global list
         */
        pW32Job = gpJobsList;

        while (pW32Job) {
            if (pW32Job->Job == Job) {
                break;
            }
            pW32Job = pW32Job->pNext;
        }

        UserAssert(pW32Job != NULL && pW32Job->pAtomTable != NULL);

        GlobalAtomTable = pW32Job->pAtomTable;

        goto End;
    }

#if DBG
    pwinsta = NULL;
#endif

    if (NT_SUCCESS(ReferenceWindowStation(Thread,
                                    PsGetCurrentProcessWin32WindowStation(),
                                    WINSTA_ACCESSGLOBALATOMS,
                                    &pwinsta,
                                    TRUE))) {
        UserAssert(pwinsta != NULL);

        GlobalAtomTable = pwinsta->pGlobalAtomTable;
    }

End:
    LeaveCrit();

#if DBG
    if (GlobalAtomTable == NULL) {
        RIPMSG1(RIP_WARNING,
                "_UserGetGlobalAtomTable: NULL Atom Table for pwinsta=%#p",
                pwinsta);
    }
#endif

    return GlobalAtomTable;
}
