//  --------------------------------------------------------------------------
//  Module Name: CWLogonStatus.cpp
//
//  Copyright (c) 2000, Microsoft Corporation
//
//  File that contains implementation for status UI hosting by an external
//  process.
//
//  History:    2000-05-11  vtan        created
//  --------------------------------------------------------------------------

#include "StandardHeader.h"
#include <msginaexports.h>

#include "Access.h"
#include "GinaIPC.h"
#include "LogonWait.h"
#include "SingleThreadedExecution.h"
#include "StatusCode.h"
#include "SystemSettings.h"
#include "UIHost.h"

//  --------------------------------------------------------------------------
//  CLogonStatus
//
//  Purpose:    C++ class to handle logon status external process for consumer
//              windows.
//
//  History:    2000-05-11  vtan        created
//  --------------------------------------------------------------------------

class   CLogonStatus : public ILogonExternalProcess
{
    private:
                                    CLogonStatus (void);
    public:
                                    CLogonStatus (const TCHAR *pszParameter);
                                    ~CLogonStatus (void);

                NTSTATUS            Start (bool fWait);
                CUIHost*            GetUIHost (void);
        static  bool                IsStatusWindow (HWND hwnd);

                bool                WaitForUIHost (void);
                void                ShowStatusMessage (const WCHAR *pszMessage);
                void                SetStateStatus (int iCode);
                void                SetStateLogon (int iCode);
                void                SetStateLoggedOn (void);
                void                SetStateHide (void);
                void                SetStateEnd (bool fSendMessage);
                void                NotifyWait (void);
                void                NotifyNoAnimations (void);
                void                SelectUser (const WCHAR *pszUsername, const WCHAR *pszDomain);
                void                InteractiveLogon (const WCHAR *pszUsername, const WCHAR *pszDomain, WCHAR *pszPassword);
                HANDLE              ResetReadyEvent (void);
                bool                IsSuspendAllowed (void)     const;
                void                ShowUIHost (void);
                void                HideUIHost (void);
                bool                IsUIHostHidden (void)   const;
    public:
        virtual bool                AllowTermination (DWORD dwExitCode);
        virtual NTSTATUS            SignalAbnormalTermination (void);
        virtual NTSTATUS            SignalRestart (void);
        virtual NTSTATUS            LogonRestart (void);
    private:
                bool                IsUIHostReady (void)    const;
                void                SendToUIHost (WPARAM wParam, LPARAM lParam);
                void                UIHostReadySignal (void);
        static  void    CALLBACK    CB_UIHostReadySignal (void *pV, BOOLEAN fTimerOrWaitFired);
        static  void    CALLBACK    CB_UIHostAbnormalTermination (ULONG_PTR dwParam);
    private:
                DWORD               _dwThreadID;
                bool                _fRegisteredWait;
                HANDLE              _hEvent;
                HANDLE              _hWait;
                int                 _iState,
                                    _iCode,
                                    _iStatePending;
                WPARAM              _waitWPARAM;
                LPARAM              _waitLPARAM;
                CUIHost*            _pUIHost;
                CLogonWait          _logonWait;
};

CCriticalSection*   g_pLogonStatusLock  =   NULL;
CLogonStatus*       g_pLogonStatus      =   NULL;

//  --------------------------------------------------------------------------
//  CLogonStatus::CLogonStatus
//
//  Arguments:  pszParameter    =   Parameter to pass to status UI host.
//
//  Returns:    <none>
//
//  Purpose:    Constructor for CLogonStatus. This gets the status UI host
//              from the registry and assigns the given parameter into the
//              object. Create a named event which SHGINA knows about and
//              will signal once the ILogonStatusHost class has been
//              instantiated.
//
//  History:    2000-05-11  vtan        created
//  --------------------------------------------------------------------------

CLogonStatus::CLogonStatus (const TCHAR *pszParameter) :
    _dwThreadID(0),
    _fRegisteredWait(false),
    _hEvent(NULL),
    _hWait(NULL),
    _iState(UI_STATE_STATUS),
    _iCode(0),
    _iStatePending(0),
    _waitWPARAM(0),
    _waitLPARAM(0),
    _pUIHost(NULL)

{
    TCHAR   szRawHostCommandLine[MAX_PATH];

    if (ERROR_SUCCESS == CSystemSettings::GetUIHost(szRawHostCommandLine))
    {
        _pUIHost = new CUIHost(szRawHostCommandLine);
        if (_pUIHost != NULL)
        {
            _pUIHost->SetInterface(this);
            _pUIHost->SetParameter(pszParameter);
        }
    }

    SECURITY_ATTRIBUTES     securityAttributes;

    //  Build a security descriptor for the event that allows:
    //      S-1-5-18        EVENT_ALL_ACCESS
    //      S-1-5-32-544    SYNCHRONIZE | READ_CONTROL | EVENT_QUERY_STATE

    static  SID_IDENTIFIER_AUTHORITY    s_SecurityNTAuthority       =   SECURITY_NT_AUTHORITY;

    static  const CSecurityDescriptor::ACCESS_CONTROL   s_AccessControl[]   =
    {
        {
            &s_SecurityNTAuthority,
            1,
            SECURITY_LOCAL_SYSTEM_RID,
            0, 0, 0, 0, 0, 0, 0,
            EVENT_ALL_ACCESS
        },
        {
            &s_SecurityNTAuthority,
            2,
            SECURITY_BUILTIN_DOMAIN_RID,
            DOMAIN_ALIAS_RID_ADMINS,
            0, 0, 0, 0, 0, 0,
            SYNCHRONIZE | READ_CONTROL | EVENT_QUERY_STATE
        }
    };

    securityAttributes.nLength = sizeof(securityAttributes);
    securityAttributes.lpSecurityDescriptor = CSecurityDescriptor::Create(ARRAYSIZE(s_AccessControl), s_AccessControl);
    securityAttributes.bInheritHandle = FALSE;
    _hEvent = CreateEvent(&securityAttributes, TRUE, FALSE, TEXT("msgina: StatusHostReadyEvent"));
    ReleaseMemory(securityAttributes.lpSecurityDescriptor);
}

//  --------------------------------------------------------------------------
//  CLogonStatus::~CLogonStatus
//
//  Arguments:  <none>
//
//  Returns:    <none>
//
//  Purpose:    Destructor for CLogonStatus. Releases references.
//
//  History:    2000-05-11  vtan        created
//  --------------------------------------------------------------------------

CLogonStatus::~CLogonStatus (void)

{
    ASSERTMSG(_hWait == NULL, "Resend wait object not released in CLogonStatus::~CLogonStatus");
    ReleaseHandle(_hEvent);
    ASSERTMSG(_iState == UI_STATE_END, "State must be UI_STATE_END in CLogonStatus::~CLogonStatus");
    if (_pUIHost != NULL)
    {
        _pUIHost->Release();
        _pUIHost = NULL;
    }
}

//  --------------------------------------------------------------------------
//  CLogonStatus::Start
//
//  Arguments:  fWait   =   Wait for status host to signal ready.
//
//  Returns:    NTSTATUS
//
//  Purpose:    Starts the status UI host. Don't wait for the UI host. There
//              is a mechanism that can queue a message if the UI host window
//              cannot be found.
//
//  History:    2000-05-11  vtan        created
//  --------------------------------------------------------------------------

NTSTATUS    CLogonStatus::Start (bool fWait)

{
    NTSTATUS    status;

    if (_pUIHost != NULL)
    {
        (HANDLE)ResetReadyEvent();
        status = _pUIHost->Start();
        if (NT_SUCCESS(status))
        {
            _dwThreadID = GetCurrentThreadId();
            if (fWait || _pUIHost->WaitRequired())
            {
                if (!WaitForUIHost())
                {
                    status = STATUS_UNSUCCESSFUL;
                }
            }
        }
    }
    else
    {
        status = STATUS_UNSUCCESSFUL;
    }
    return(status);
}

//  --------------------------------------------------------------------------
//  CLogonStatus::GetUIHost
//
//  Arguments:  <none>
//
//  Returns:    CUIHost*
//
//  Purpose:    Returns a reference to the UIHost object held internally.
//              The reference belongs to the caller and must be released.
//
//  History:    2000-05-11  vtan        created
//  --------------------------------------------------------------------------

CUIHost*    CLogonStatus::GetUIHost (void)

{
    if (_pUIHost != NULL)
    {
        _pUIHost->AddRef();
    }
    return(_pUIHost);
}

//  --------------------------------------------------------------------------
//  CLogonStatus::IsStatusWindow
//
//  Arguments:  hwnd    =   HWND to check.
//
//  Returns:    bool
//
//  Purpose:    Returns whether the given HWND is the status window.
//
//  History:    2000-06-26  vtan        created
//  --------------------------------------------------------------------------

bool    CLogonStatus::IsStatusWindow (HWND hwnd)

{
    TCHAR   szWindowClass[256];

    return((GetClassName(hwnd, szWindowClass, ARRAYSIZE(szWindowClass)) != 0) &&
           (lstrcmpi(STATUS_WINDOW_CLASS_NAME, szWindowClass) == 0));
}

//  --------------------------------------------------------------------------
//  CLogonStatus::WaitForUIHost
//
//  Arguments:  <none>
//
//  Returns:    bool
//
//  Purpose:    Waits on the named event that the UI host signals when it's
//              initialized. Typically this happens very quickly but we don't
//              wait on it when starting up the UI host.
//
//  History:    2000-09-10  vtan        created
//  --------------------------------------------------------------------------

bool    CLogonStatus::WaitForUIHost (void)

{
    bool    fResult;

    fResult = true;
    ASSERTMSG(_hEvent != NULL, "No UI host named event to wait on in CLogonStatus::WaitForUIHost");
    if (!IsUIHostReady())
    {
        DWORD   dwWaitResult;

#ifdef      DBG
        DWORD   dwWaitStart, dwWaitEnd;

        dwWaitStart = (WAIT_TIMEOUT == WaitForSingleObject(_hEvent, 0)) ? GetTickCount() : 0;
#endif  /*  DBG     */
        do
        {
            dwWaitResult = WaitForSingleObject(_hEvent, 0);
            if (dwWaitResult != WAIT_OBJECT_0)
            {
                dwWaitResult = MsgWaitForMultipleObjectsEx(1, &_hEvent, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE);
                if (dwWaitResult == WAIT_OBJECT_0 + 1)
                {
                    MSG     msg;

                    if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) != FALSE)
                    {
                        (BOOL)TranslateMessage(&msg);
                        (LRESULT)DispatchMessage(&msg);
                    }
                }
             }
        } while ((dwWaitResult == WAIT_OBJECT_0 + 1) && (dwWaitResult != WAIT_IO_COMPLETION));
#ifdef      DBG
        dwWaitEnd = GetTickCount();
        if ((dwWaitStart != 0) && ((dwWaitEnd - dwWaitStart) != 0))
        {
            char    szBuffer[256];

            wsprintfA(szBuffer, "waited %d ticks for UI host", dwWaitEnd - dwWaitStart);
            INFORMATIONMSG(szBuffer);
        }
#endif  /*  DBG     */
        fResult = (dwWaitResult != WAIT_IO_COMPLETION);
    }
    return(fResult);
}

//  --------------------------------------------------------------------------
//  CLogonStatus::ShowStatusMessage
//
//  Arguments:  pszMessage  =   Unicode string message to display.
//
//  Returns:    <none>
//
//  Purpose:    Tells the UI host to display the given string message. Puts
//              the string directly inside the status host process and tells
//              the process where in its address space to find the string.
//              The string is limited to 256 characters.
//
//  History:    2000-05-11  vtan        created
//  --------------------------------------------------------------------------

void    CLogonStatus::ShowStatusMessage (const WCHAR *pszMessage)

{
    if (NT_SUCCESS(_pUIHost->PutString(pszMessage)))
    {
        SendToUIHost(UI_DISPLAY_STATUS, reinterpret_cast<LONG_PTR>(_pUIHost->GetDataAddress()));
    }
}

//  --------------------------------------------------------------------------
//  CLogonStatus::SetStateStatus
//
//  Arguments:  iCode   =   Magic code number for lock.
//
//  Returns:    <none>
//
//  Purpose:    Tells the status UI host to go into status state.
//
//  History:    2000-05-11  vtan        created
//  --------------------------------------------------------------------------

void    CLogonStatus::SetStateStatus (int iCode)

{
    _iStatePending = UI_STATE_STATUS;
    if (WaitForUIHost() && (_iState != UI_STATE_STATUS))
    {
        SendToUIHost(UI_STATE_STATUS, iCode);
        _iState = UI_STATE_STATUS;
        _iCode = iCode;
    }
    _iStatePending = UI_STATE_NONE;
}

//  --------------------------------------------------------------------------
//  CLogonStatus::SetStateLogon
//
//  Arguments:  iCode   =   Magic code number for lock.
//
//  Returns:    <none>
//
//  Purpose:    Tells the status UI host to go into logon state.
//
//  History:    2000-05-11  vtan        created
//  --------------------------------------------------------------------------

void    CLogonStatus::SetStateLogon (int iCode)

{
    _iStatePending = UI_STATE_LOGON;
    if (WaitForUIHost() && (iCode == _iCode))
    {
        SendToUIHost(UI_STATE_LOGON, iCode);
        _iState = UI_STATE_LOGON;
        _iCode = 0;
    }
    _iStatePending = UI_STATE_NONE;
}

//  --------------------------------------------------------------------------
//  CLogonStatus::SetStateLoggedOn
//
//  Arguments:  <none>
//
//  Returns:    <none>
//
//  Purpose:    Tells the status UI host to go into logged on state.
//
//  History:    2000-05-24  vtan        created
//  --------------------------------------------------------------------------

void    CLogonStatus::SetStateLoggedOn (void)

{
    _iStatePending = UI_STATE_STATUS;
    if (WaitForUIHost())
    {
        SendToUIHost(UI_STATE_LOGGEDON, 0);
        _iState = UI_STATE_STATUS;
    }
    _iStatePending = UI_STATE_NONE;
}

//  --------------------------------------------------------------------------
//  CLogonStatus::SetStateHide
//
//  Arguments:  <none>
//
//  Returns:    <none>
//
//  Purpose:    Tells the status UI host to hide itself.
//
//  History:    2001-01-08  vtan        created
//  --------------------------------------------------------------------------

void    CLogonStatus::SetStateHide (void)

{
    _iStatePending = UI_STATE_HIDE;
    if (WaitForUIHost())
    {
        SendToUIHost(UI_STATE_HIDE, 0);
        _iState = UI_STATE_HIDE;
    }
    _iStatePending = UI_STATE_NONE;
}

//  --------------------------------------------------------------------------
//  CLogonStatus::SetStateEnd
//
//  Arguments:  fSendMessage    =   Send message to UI host or not.
//
//  Returns:    <none>
//
//  Purpose:    Tells the status UI host to end and terminate itself.
//
//  History:    2000-05-11  vtan        created
//  --------------------------------------------------------------------------

void    CLogonStatus::SetStateEnd (bool fSendMessage)

{
    bool    fHostAlive;
    HANDLE  hWait;

    _iStatePending = UI_STATE_END;

    //  When going into end mode if there's a wait registered then
    //  unregister it. This will release an outstanding reference.
    //  A re-register should never happen but this is set just in case.

    _fRegisteredWait = true;
    hWait = InterlockedExchangePointer(&_hWait, NULL);
    if (hWait != NULL)
    {
        if (UnregisterWait(hWait) != FALSE)
        {
            Release();
        }
    }
    if (fSendMessage)
    {
        fHostAlive = WaitForUIHost();
    }
    else
    {
        fHostAlive = true;
    }
    if (fHostAlive)
    {
        if (_pUIHost != NULL)
        {
            _pUIHost->SetInterface(NULL);
        }
        if (fSendMessage)
        {
            SendToUIHost(UI_STATE_END, 0);
        }
        _iState = UI_STATE_END;
    }
    _iStatePending = UI_STATE_NONE;
}

//  --------------------------------------------------------------------------
//  CLogonStatus::NotifyWait
//
//  Arguments:  <none>
//
//  Returns:    <none>
//
//  Purpose:    Tells the status UI host to display a title that the system
//              is shutting down.
//
//  History:    2000-07-14  vtan        created
//  --------------------------------------------------------------------------

void    CLogonStatus::NotifyWait (void)

{
    SendToUIHost(UI_NOTIFY_WAIT, 0);
}

//  --------------------------------------------------------------------------
//  CLogonStatus::NotifyNoAnimations
//
//  Arguments:  <none>
//
//  Returns:    <none>
//
//  Purpose:    Tells the status UI host to no longer perform animations.
//
//  History:    2001-03-21  vtan        created
//  --------------------------------------------------------------------------

void    CLogonStatus::NotifyNoAnimations (void)

{
    SendToUIHost(UI_SET_ANIMATIONS, 0);
}

//  --------------------------------------------------------------------------
//  CLogonStatus::SelectUser
//
//  Arguments:  pszUsername     =   User name to select.
//
//  Returns:    <none>
//
//  Purpose:    Tells the status UI host to select the user by the given
//              logon name.
//
//  History:    2001-01-10  vtan        created
//  --------------------------------------------------------------------------

void    CLogonStatus::SelectUser (const WCHAR *pszUsername, const WCHAR *pszDomain)

{
    LOGONIPC_USERID     logonIPC;

    (WCHAR*)lstrcpyW(logonIPC.wszUsername, pszUsername);
    (WCHAR*)lstrcpyW(logonIPC.wszDomain, pszDomain);
    if (NT_SUCCESS(_pUIHost->PutData(&logonIPC, sizeof(logonIPC))))
    {
        SendToUIHost(UI_SELECT_USER, reinterpret_cast<LONG_PTR>(_pUIHost->GetDataAddress()));
    }
}

//  --------------------------------------------------------------------------
//  CLogonStatus::InteractiveLogon
//
//  Arguments:  pszUsername     =   Username to logon.
//              pszDomain       =   Domain to logon.
//              pszPassword     =   Password to use.
//
//  Returns:    <none>
//
//  Purpose:    Tell the status host to log the specified user on.
//
//  History:    2001-01-12  vtan        created
//  --------------------------------------------------------------------------

void    CLogonStatus::InteractiveLogon (const WCHAR *pszUsername, const WCHAR *pszDomain, WCHAR *pszPassword)

{
    LOGONIPC_CREDENTIALS    logonIPCCredentials;

    (WCHAR*)lstrcpyW(logonIPCCredentials.userID.wszUsername, pszUsername);
    (WCHAR*)lstrcpyW(logonIPCCredentials.userID.wszDomain, pszDomain);
    (WCHAR*)lstrcpyW(logonIPCCredentials.wszPassword, pszPassword);
    ZeroMemory(pszPassword, (lstrlenW(pszPassword) + sizeof('\0'))* sizeof(WCHAR));
    if (NT_SUCCESS(_pUIHost->PutData(&logonIPCCredentials, sizeof(logonIPCCredentials))))
    {
        SendToUIHost(UI_INTERACTIVE_LOGON, reinterpret_cast<LONG_PTR>(_pUIHost->GetDataAddress()));
    }
}

//  --------------------------------------------------------------------------
//  CLogonStatus::ResetReadyEvent
//
//  Arguments:  <none>
//
//  Returns:    HANDLE
//
//  Purpose:    Reset the UI host ready event. A new instance will set this
//              event. Use this in UI host failure.
//
//  History:    2001-01-09  vtan        created
//  --------------------------------------------------------------------------

HANDLE  CLogonStatus::ResetReadyEvent (void)

{
    TBOOL(ResetEvent(_hEvent));
    return(_hEvent);
}

//  --------------------------------------------------------------------------
//  CLogonStatus::IsSuspendAllowed
//
//  Arguments:  <none>
//
//  Returns:    bool
//
//  Purpose:    Returns whether the UI host allows suspending of the computer.
//              This is true if in the logon state or in the status (locked)
//              state.
//
//  History:    2000-08-21  vtan        created
//  --------------------------------------------------------------------------

bool    CLogonStatus::IsSuspendAllowed (void)     const

{
    return(((_iState == UI_STATE_STATUS) && (_iCode != 0)) ||
           (_iStatePending == UI_STATE_LOGON) ||
           (_iState == UI_STATE_LOGON) ||
           (_iState == UI_STATE_HIDE));
}

//  --------------------------------------------------------------------------
//  CLogonStatus::ShowUIHost
//
//  Arguments:  <none>
//
//  Returns:    <none>
//
//  Purpose:    Shows the UI host.
//
//  History:    2001-03-05  vtan        created
//  --------------------------------------------------------------------------

void    CLogonStatus::ShowUIHost (void)

{
    _pUIHost->Show();
}

//  --------------------------------------------------------------------------
//  CLogonStatus::HideUIHost
//
//  Arguments:  <none>
//
//  Returns:    <none>
//
//  Purpose:    Hides the UI host.
//
//  History:    2001-03-05  vtan        created
//  --------------------------------------------------------------------------

void    CLogonStatus::HideUIHost (void)

{
    _pUIHost->Hide();
}

//  --------------------------------------------------------------------------
//  CLogonStatus::IsUIHostHidden
//
//  Arguments:  <none>
//
//  Returns:    <none>
//
//  Purpose:    Returns whether the UI host is hidden.
//
//  History:    2001-03-05  vtan        created
//  --------------------------------------------------------------------------

bool    CLogonStatus::IsUIHostHidden (void)   const

{
    return(_pUIHost->IsHidden());
}

//  --------------------------------------------------------------------------
//  CLogonStatus::AllowTermination
//
//  Arguments:  dwExitCode  =   Exit code of host process.
//
//  Returns:    bool
//
//  Purpose:    Returns whether the host process is allowed to terminate
//              given the exit code passed in.
//
//              Currently the host process is not allowed to terminate.
//
//  History:    2000-05-11  vtan        created
//  --------------------------------------------------------------------------

bool    CLogonStatus::AllowTermination (DWORD dwExitCode)

{
    UNREFERENCED_PARAMETER(dwExitCode);

    return(false);
}

//  --------------------------------------------------------------------------
//  CLogonStatus::SignalAbnormalTermination
//
//  Arguments:  <none>
//
//  Returns:    NTSTATUS
//
//  Purpose:    Handles abnormal termination of host process.
//
//  History:    2000-05-11  vtan        created
//  --------------------------------------------------------------------------

NTSTATUS    CLogonStatus::SignalAbnormalTermination (void)

{
    HANDLE  hThread;

    TSTATUS(_logonWait.Cancel());
    hThread = OpenThread(THREAD_SET_CONTEXT | THREAD_QUERY_INFORMATION, FALSE, _dwThreadID);
    if (hThread != NULL)
    {
        (BOOL)QueueUserAPC(CB_UIHostAbnormalTermination, hThread, reinterpret_cast<ULONG_PTR>(this));
        TBOOL(CloseHandle(hThread));
    }
    _Shell_LogonStatus_Destroy(HOST_END_FAILURE);
    return(STATUS_SUCCESS);
}

//  --------------------------------------------------------------------------
//  CLogonStatus::SignalRestart
//
//  Arguments:  <none>
//
//  Returns:    NTSTATUS
//
//  Purpose:    Function to reset the ready event and set the UI host into
//              status state. This is invoked when the UI host is restarted
//              after a failure.
//
//  History:    2001-01-09  vtan        created
//  --------------------------------------------------------------------------

NTSTATUS    CLogonStatus::SignalRestart (void)

{
    (HANDLE)ResetReadyEvent();
    return(_logonWait.Register(_hEvent, this));
}

//  --------------------------------------------------------------------------
//  CLogonStatus::LogonRestart
//
//  Arguments:  <none>
//
//  Returns:    NTSTATUS
//
//  Purpose:    
//
//  History:    2001-02-21  vtan        created
//  --------------------------------------------------------------------------

NTSTATUS    CLogonStatus::LogonRestart (void)

{
    SetStateStatus(0);
    return(STATUS_SUCCESS);
}

//  --------------------------------------------------------------------------
//  CLogonStatus::IsUIHostReady
//
//  Arguments:  <none>
//
//  Returns:    bool
//
//  Purpose:    Returns whether the UI host is ready.
//
//  History:    2000-09-11  vtan        created
//  --------------------------------------------------------------------------

bool    CLogonStatus::IsUIHostReady (void)    const

{
    ASSERTMSG(_hEvent != NULL, "No UI host named event to wait on in CLogonStatus::IsUIHostReady");
    return(WAIT_OBJECT_0 == WaitForSingleObject(_hEvent, 0));
}

//  --------------------------------------------------------------------------
//  CLogonStatus::SendToUIHost
//
//  Arguments:  wParam  =   WPARAM to send to UI host.
//              lParam  =   LPARAM to send to UI host.
//
//  Returns:    <none>
//
//  Purpose:    Finds the status window created by SHGINA and sends the
//              message to it. That window turns around and sends the message
//              to the UI host. This allows communication implemenation method
//              to change without forcing the UI host to be rebuilt.
//
//  History:    2000-05-11  vtan        created
//              2000-09-11  vtan        uses PostMessage not SendMessage
//  --------------------------------------------------------------------------

void    CLogonStatus::SendToUIHost (WPARAM wParam, LPARAM lParam)

{
    HWND hwnd;

    if (IsUIHostReady())
    {
        hwnd = FindWindow(STATUS_WINDOW_CLASS_NAME, NULL);
    }
    else
    {
        hwnd = NULL;
    }

    if (hwnd != NULL)
    {
        HANDLE  hWait;

        //  Don't allow any registrations if we've found it.

        _fRegisteredWait = true;
        hWait = InterlockedExchangePointer(&_hWait, NULL);
        if (hWait != NULL)
        {
            if (UnregisterWait(hWait) != FALSE)
            {

                //  If sucesssfully releasing the hWait we need to call release.

                Release();
            }
        }
        TBOOL(PostMessage(hwnd, WM_UISERVICEREQUEST, wParam, lParam));
    }
    else if (!_fRegisteredWait)
    {

        //  Cannot find the UI host window. It's probably still getting
        //  its act together. Queue this post message for a callback when
        //  the event is signaled if a register has not already been
        //  made. If one has then just change the parameters.
        //  Add a reference here. The callback will release it. If the
        //  register on the wait failed the release the reference.

        if (_hWait == NULL)
        {
            HANDLE  hWait;

            AddRef();
            if (RegisterWaitForSingleObject(&hWait,
                                            _hEvent,
                                            CB_UIHostReadySignal,
                                            this,
                                            INFINITE,
                                            WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE) != FALSE)
            {
                if (InterlockedCompareExchangePointer(&_hWait, hWait, NULL) == NULL)
                {
                    _fRegisteredWait = true;
                }
                else
                {

                    //  Someone else beat us to registering (should never happen)

                    (BOOL)UnregisterWait(hWait);
                    Release();
                }
            }
            else
            {
                Release();
            }
        }
        _waitWPARAM = wParam;
        _waitLPARAM = lParam;
    }
}

//  --------------------------------------------------------------------------
//  CLogonStatus::UIHostReadySignal
//
//  Arguments:  <none>
//
//  Returns:    <none>
//
//  Purpose:    Callback invoked when the UI host signals it's ready. This
//              function unregisters the wait and resend the status message
//              to the UI host.
//
//  History:    2000-09-10  vtan        created
//  --------------------------------------------------------------------------

void    CLogonStatus::UIHostReadySignal (void)

{
    HANDLE  hWait;

    hWait = InterlockedExchangePointer(&_hWait, NULL);
    if (hWait != NULL)
    {
        TBOOL(UnregisterWait(hWait));
    }
    SendToUIHost(_waitWPARAM, _waitLPARAM);
}

//  --------------------------------------------------------------------------
//  CLogonStatus::CB_UIHostReadySignal
//
//  Arguments:  See the platform SDK under WaitOrTimerCallback.
//
//  Returns:    <none>
//
//  Purpose:    Callback entry point for registered event wait.
//
//  History:    2000-09-10  vtan        created
//  --------------------------------------------------------------------------

void    CALLBACK    CLogonStatus::CB_UIHostReadySignal (void *pV, BOOLEAN fTimerOrWaitFired)

{
    UNREFERENCED_PARAMETER(fTimerOrWaitFired);

    CLogonStatus    *pThis;

    pThis = reinterpret_cast<CLogonStatus*>(pV);
    if (pThis != NULL)
    {
        pThis->UIHostReadySignal();
    }
    pThis->Release();
}

//  --------------------------------------------------------------------------
//  CLogonStatus::CB_UIHostAbnormalTermination
//
//  Arguments:  See the platform SDK under APCProc.
//
//  Returns:    <none>
//
//  Purpose:    Callback entry point for queued APC on abnormal termination.
//
//  History:    2001-02-19  vtan        created
//  --------------------------------------------------------------------------

void    CALLBACK    CLogonStatus::CB_UIHostAbnormalTermination (ULONG_PTR dwParam)

{
    UNREFERENCED_PARAMETER(dwParam);
}

//  --------------------------------------------------------------------------
//  ::_Shell_LogonStatus_StaticInitialize
//
//  Arguments:  <none>
//
//  Returns:    NTSTATUS
//
//  Purpose:    Initialize the critical section for g_pLogonStatus.
//
//  History:    2001-06-11  vtan        created
//  --------------------------------------------------------------------------

EXTERN_C    NTSTATUS    _Shell_LogonStatus_StaticInitialize (void)

{
    NTSTATUS    status;

    ASSERTMSG(g_pLogonStatusLock == NULL, "g_pLogonStatusLock already exists in _Shell_LogonStatus_StaticInitialize");
    g_pLogonStatusLock = new CCriticalSection;
    if (g_pLogonStatusLock != NULL)
    {
        status = g_pLogonStatusLock->Status();
        if (!NT_SUCCESS(status))
        {
            delete g_pLogonStatusLock;
            g_pLogonStatusLock = NULL;
        }
    }
    else
    {
        status = STATUS_NO_MEMORY;
    }
    return(status);
}

//  --------------------------------------------------------------------------
//  ::_Shell_LogonStatus_StaticTerminate
//
//  Arguments:  <none>
//
//  Returns:    NTSTATUS
//
//  Purpose:    Delete the critical section for g_pLogonStatus.
//
//  History:    2001-06-11  vtan        created
//  --------------------------------------------------------------------------

EXTERN_C    NTSTATUS    _Shell_LogonStatus_StaticTerminate (void)

{
    if (g_pLogonStatusLock != NULL)
    {
        delete g_pLogonStatusLock;
        g_pLogonStatusLock = NULL;
    }
    return(STATUS_SUCCESS);
}

//  --------------------------------------------------------------------------
//  ::_Shell_LogonStatus_Init
//
//  Arguments:  uiStartType     =   Start mode of status host.
//
//  Returns:    <none>
//
//  Purpose:    Creates the instance of CLogonStatus telling it to pass
//              "/status" as the parameter to the UI host. It then starts
//              the host if the object was created.
//
//              The object is held globally.
//
//  History:    2000-05-11  vtan        created
//              2000-07-13  vtan        add shutdown parameter.
//              2000-07-17  vtan        changed to start type parameter.
//  --------------------------------------------------------------------------

EXTERN_C    void    _Shell_LogonStatus_Init (UINT uiStartType)

{
    bool    fIsRemote, fIsSessionZero;

    fIsRemote = (GetSystemMetrics(SM_REMOTESESSION) != 0);
    fIsSessionZero = (NtCurrentPeb()->SessionId == 0);
    if ((!fIsRemote || fIsSessionZero || CSystemSettings::IsForceFriendlyUI()) && CSystemSettings::IsFriendlyUIActive())
    {
        bool    fWait;
        TCHAR   szParameter[256];

        if (g_pLogonStatusLock != NULL)
        {
            g_pLogonStatusLock->Acquire();
            if (g_pLogonStatus == NULL)
            {
                (TCHAR*)lstrcpy(szParameter, TEXT(" /status"));
                if (HOST_START_SHUTDOWN == uiStartType)
                {
                    (TCHAR*)lstrcat(szParameter, TEXT(" /shutdown"));
                    fWait = true;
                }
                else if (HOST_START_WAIT == uiStartType)
                {
                    (TCHAR*)lstrcat(szParameter, TEXT(" /wait"));
                    fWait = true;
                }
                else
                {
                    fWait = false;
                }
                g_pLogonStatus = new CLogonStatus(szParameter);
                if (g_pLogonStatus != NULL)
                {
                    NTSTATUS    status;

                    g_pLogonStatusLock->Release();
                    status = g_pLogonStatus->Start(fWait);
                    g_pLogonStatusLock->Acquire();
                    if (!NT_SUCCESS(status) && (g_pLogonStatus != NULL))
                    {
                        g_pLogonStatus->Release();
                        g_pLogonStatus = NULL;
                    }
                }
            }
            else
            {
                g_pLogonStatus->SetStateStatus(0);
                if ((HOST_START_SHUTDOWN == uiStartType) || (HOST_START_WAIT == uiStartType))
                {
                    g_pLogonStatus->NotifyWait();
                }
            }
            g_pLogonStatusLock->Release();
        }
    }
}

//  --------------------------------------------------------------------------
//  ::_Shell_LogonStatus_Destroy
//
//  Arguments:  uiEndType   =   End mode of status host.
//
//  Returns:    <none>
//
//  Purpose:    If the end type is hide then tell the status host to hide.
//              Otherwise check the end type is terminate. In that case tell
//              the status host to go away.
//
//  History:    2000-05-11  vtan        created
//              2001-01-09  vtan        add end parameter
//  --------------------------------------------------------------------------

EXTERN_C    void    _Shell_LogonStatus_Destroy (UINT uiEndType)

{
    if (g_pLogonStatusLock != NULL)
    {
        CSingleThreadedExecution    lock(*g_pLogonStatusLock);

        if (g_pLogonStatus != NULL)
        {
            switch (uiEndType)
            {
                case HOST_END_HIDE:

                    //  HOST_END_HIDE: Is the UI host static? If so then hide it.
                    //  Otherwise revert to start/stop mode (dynamic) and force the
                    //  UI host to terminate.

                    if (CSystemSettings::IsUIHostStatic())
                    {
                        g_pLogonStatus->SetStateHide();
                        break;
                    }
                    uiEndType = HOST_END_TERMINATE;

                    //  If the the host is dynamic then set the type to
                    //  HOST_END_TERMINATE and fall thru to this case so that
                    //  the host is told to end.

                case HOST_END_TERMINATE:

                    //  HOST_END_TERMINATE: Force the UI host to terminate. This is
                    //  used in circumstances where it must terminate such as we
                    //  are terminating or the machine is shutting down.

                    g_pLogonStatus->SetStateEnd(true);
                    break;
                case HOST_END_FAILURE:

                    //  HOST_END_FAILURE: This is sent when the UI host failed to
                    //  start and will not be restarted. This allows the interface
                    //  reference to be deleted so that object reference count
                    //  will reach zero and the memory will be released.

                    g_pLogonStatus->SetStateEnd(false);
                    uiEndType = HOST_END_TERMINATE;
                    break;
                default:
                    DISPLAYMSG("Unknown uiEndType passed to _Shell_LogonStatus_Destroy");
                    break;
            }
            if (HOST_END_TERMINATE == uiEndType)
            {
                g_pLogonStatus->Release();
                g_pLogonStatus = NULL;
            }
        }
    }
}

//  --------------------------------------------------------------------------
//  ::_Shell_LogonStatus_Exists
//
//  Arguments:  <none>
//
//  Returns:    BOOL
//
//  Purpose:    Returns whether there is status host created.
//
//  History:    2000-05-11  vtan        created
//  --------------------------------------------------------------------------

EXTERN_C    BOOL    _Shell_LogonStatus_Exists (void)

{
    return(g_pLogonStatus != NULL);
}

//  --------------------------------------------------------------------------
//  ::_Shell_LogonStatus_IsStatusWindow
//
//  Arguments:  hwnd    =   HWND to check.
//
//  Returns:    BOOL
//
//  Purpose:    Returns whether the given HWND is the status HWND.
//
//  History:    2000-06-26  vtan        created
//  --------------------------------------------------------------------------

EXTERN_C    BOOL    _Shell_LogonStatus_IsStatusWindow (HWND hwnd)

{
    return(CLogonStatus::IsStatusWindow(hwnd));
}

//  --------------------------------------------------------------------------
//  ::_Shell_LogonStatus_IsSuspendAllowed
//
//  Arguments:  <none>
//
//  Returns:    BOOL
//
//  Purpose:    Ask the status host (if present) if suspend is allowed.
//
//  History:    2000-08-18  vtan        created
//  --------------------------------------------------------------------------

EXTERN_C    BOOL    _Shell_LogonStatus_IsSuspendAllowed (void)

{
    return((g_pLogonStatus == NULL) || g_pLogonStatus->IsSuspendAllowed());
}

//  --------------------------------------------------------------------------
//  ::_Shell_LogonStatus_WaitforUIHost
//
//  Arguments:  <none>
//
//  Returns:    BOOL
//
//  Purpose:    External C entry point to force the current thread to wait
//              until the UI host signals it's ready. Returns whether the
//              wait was successful or abandoned. Success is true. Abandoned
//              or non-existant is false.
//
//  History:    2000-09-10  vtan        created
//              2001-02-19  vtan        added return result
//  --------------------------------------------------------------------------

EXTERN_C    BOOL    _Shell_LogonStatus_WaitForUIHost (void)

{
    return((g_pLogonStatus != NULL) && g_pLogonStatus->WaitForUIHost());
}

//  --------------------------------------------------------------------------
//  ::_Shell_LogonStatus_ShowStatusMessage
//
//  Arguments:  pszMessage  =   Unicode string to display.
//
//  Returns:    <none>
//
//  Purpose:    External C entry point to pass display string to status host.
//
//  History:    2000-05-11  vtan        created
//  --------------------------------------------------------------------------

EXTERN_C    void    _Shell_LogonStatus_ShowStatusMessage (const WCHAR *pszMessage)

{
    if (g_pLogonStatus != NULL)
    {
        g_pLogonStatus->ShowStatusMessage(pszMessage);
    }
}

//  --------------------------------------------------------------------------
//  ::_Shell_LogonStatus_SetStateStatus
//
//  Arguments:  <none>
//
//  Returns:    <none>
//
//  Purpose:    External C entry point to tell the status host to go to status
//              state.
//
//  History:    2000-05-11  vtan        created
//  --------------------------------------------------------------------------

EXTERN_C    void    _Shell_LogonStatus_SetStateStatus (int iCode)

{
    if (g_pLogonStatus != NULL)
    {
        g_pLogonStatus->SetStateStatus(iCode);
    }
}

//  --------------------------------------------------------------------------
//  ::_Shell_LogonStatus_SetStateLogon
//
//  Arguments:  <none>
//
//  Returns:    <none>
//
//  Purpose:    External C entry point to tell the status host to go to logon
//              state.
//
//  History:    2000-05-11  vtan        created
//  --------------------------------------------------------------------------

EXTERN_C    void    _Shell_LogonStatus_SetStateLogon (int iCode)

{
    if (g_pLogonStatus != NULL)
    {
        g_pLogonStatus->SetStateLogon(iCode);
    }
}

//  --------------------------------------------------------------------------
//  ::_Shell_LogonStatus_SetStateLoggedOn
//
//  Arguments:  <none>
//
//  Returns:    <none>
//
//  Purpose:    External C entry point to tell the status host to go to
//              logged on state.
//
//  History:    2000-05-24  vtan        created
//  --------------------------------------------------------------------------

EXTERN_C    void    _Shell_LogonStatus_SetStateLoggedOn (void)

{
    if (g_pLogonStatus != NULL)
    {
        g_pLogonStatus->SetStateLoggedOn();
    }
}

//  --------------------------------------------------------------------------
//  ::_Shell_LogonStatus_SetStateHide
//
//  Arguments:  <none>
//
//  Returns:    <none>
//
//  Purpose:    External C entry point to tell the status host to hide itself.
//
//  History:    2001-01-08  vtan        created
//  --------------------------------------------------------------------------

EXTERN_C    void    _Shell_LogonStatus_SetStateHide (void)

{
    if (g_pLogonStatus != NULL)
    {
        g_pLogonStatus->SetStateHide();
    }
}

//  --------------------------------------------------------------------------
//  ::_Shell_LogonStatus_SetStateEnd
//
//  Arguments:  <none>
//
//  Returns:    <none>
//
//  Purpose:    External C entry point to tell the status host to terminate.
//
//  History:    2000-05-11  vtan        created
//  --------------------------------------------------------------------------

EXTERN_C    void    _Shell_LogonStatus_SetStateEnd (void)

{
    if (g_pLogonStatus != NULL)
    {
        g_pLogonStatus->SetStateEnd(true);
    }
}

//  --------------------------------------------------------------------------
//  ::_Shell_LogonStatus_NotifyWait
//
//  Arguments:  <none>
//
//  Returns:    <none>
//
//  Purpose:    External C entry point to tell the status host to display a
//              title stating the system is preparing to shut down.
//
//  History:    2000-07-14  vtan        created
//  --------------------------------------------------------------------------

EXTERN_C    void    _Shell_LogonStatus_NotifyWait (void)

{
    if (g_pLogonStatus != NULL)
    {
        g_pLogonStatus->NotifyWait();
    }
}

//  --------------------------------------------------------------------------
//  ::_Shell_LogonStatus_NotifyNoAnimations
//
//  Arguments:  <none>
//
//  Returns:    <none>
//
//  Purpose:    External C entry point to tell the status host to no longer
//              perform animations.
//
//  History:    2001-03-21  vtan        created
//  --------------------------------------------------------------------------

EXTERN_C    void    _Shell_LogonStatus_NotifyNoAnimations (void)

{
    if (g_pLogonStatus != NULL)
    {
        g_pLogonStatus->NotifyNoAnimations();
    }
}

//  --------------------------------------------------------------------------
//  ::_Shell_LogonStatus_SelectUser
//
//  Arguments:  pszUsername     =   Username to select.
//
//  Returns:    <none>
//
//  Purpose:    External C entry point to tell the status host to select a
//              specific user as being logged on.
//
//  History:    2001-01-10  vtan        created
//  --------------------------------------------------------------------------

EXTERN_C    void    _Shell_LogonStatus_SelectUser (const WCHAR *pszUsername, const WCHAR *pszDomain)

{
    if (g_pLogonStatus != NULL)
    {
        g_pLogonStatus->SelectUser(pszUsername, pszDomain);
    }
}

//  --------------------------------------------------------------------------
//  ::_Shell_LogonStatus_InteractiveLogon
//
//  Arguments:  pszUsername     =   Username to logon.
//              pszDomain       =   Domain to logon.
//              pszPassword     =   Password to use.
//
//  Returns:    <none>
//
//  Purpose:    External C entry point to tell the status host to log the
//              specified user on.
//
//  History:    2001-01-12  vtan        created
//  --------------------------------------------------------------------------

EXTERN_C    void    _Shell_LogonStatus_InteractiveLogon (const WCHAR *pszUsername, const WCHAR *pszDomain, WCHAR *pszPassword)

{
    if (g_pLogonStatus != NULL)
    {
        g_pLogonStatus->InteractiveLogon(pszUsername, pszDomain, pszPassword);
    }
}

//  --------------------------------------------------------------------------
//  ::_Shell_LogonStatus_GetUIHost
//
//  Arguments:  <none>
//
//  Returns:    void*
//
//  Purpose:    External C entry point that returns a reference to the UI
//              host object. This is returned as a void* because C doesn't
//              understand C++ objects. The void* is cast to the appropriate
//              type for use in CWLogonDialog.cpp so that it doesn't go and
//              create a new instance of the object but increments the
//              reference to this already existing object.
//
//  History:    2000-05-11  vtan        created
//  --------------------------------------------------------------------------

EXTERN_C    void*   _Shell_LogonStatus_GetUIHost (void)

{
    void    *pResult;

    if (g_pLogonStatus != NULL)
    {
        pResult = g_pLogonStatus->GetUIHost();
    }
    else
    {
        pResult = NULL;
    }
    return(pResult);
}

//  --------------------------------------------------------------------------
//  ::_Shell_LogonStatus_ResetReadyEvent
//
//  Arguments:  <none>
//
//  Returns:    HANDLE
//
//  Purpose:    Resets the ready event in case of UI host failure.
//
//  History:    2001-01-09  vtan        created
//  --------------------------------------------------------------------------

EXTERN_C    HANDLE  _Shell_LogonStatus_ResetReadyEvent (void)

{
    HANDLE  hEvent;

    if (g_pLogonStatus != NULL)
    {
        hEvent = g_pLogonStatus->ResetReadyEvent();
    }
    else
    {
        hEvent = NULL;
    }
    return(hEvent);
}

//  --------------------------------------------------------------------------
//  ::_Shell_LogonStatus_Show
//
//  Arguments:  <none>
//
//  Returns:    <none>
//
//  Purpose:    Shows the UI host.
//
//  History:    2001-03-05  vtan        created
//  --------------------------------------------------------------------------

EXTERN_C    void        _Shell_LogonStatus_Show (void)

{
    if (g_pLogonStatus != NULL)
    {
        g_pLogonStatus->ShowUIHost();
    }
}

//  --------------------------------------------------------------------------
//  ::_Shell_LogonStatus_Hide
//
//  Arguments:  <none>
//
//  Returns:    <none>
//
//  Purpose:    Hides the UI host.
//
//  History:    2001-03-05  vtan        created
//  --------------------------------------------------------------------------

EXTERN_C    void        _Shell_LogonStatus_Hide (void)

{
    if (g_pLogonStatus != NULL)
    {
        g_pLogonStatus->HideUIHost();
    }
}

//  --------------------------------------------------------------------------
//  ::_Shell_LogonStatus_IsHidden
//
//  Arguments:  <none>
//
//  Returns:    <none>
//
//  Purpose:    Returns whether the UI host is hidden.
//
//  History:    2001-03-05  vtan        created
//  --------------------------------------------------------------------------

EXTERN_C    BOOL        _Shell_LogonStatus_IsHidden (void)

{
    return((g_pLogonStatus != NULL) && g_pLogonStatus->IsUIHostHidden());
}