//  --------------------------------------------------------------------------
//  Module Name: UIHost.cpp
//
//  Copyright (c) 1999-2000, Microsoft Corporation
//
//  Class to handle the UI host for the logon process. This handles the IPC
//  as well as the creation and monitoring of process death. The process is
//  a restricted SYSTEM context process.
//
//  History:    1999-09-14  vtan        created
//              2000-02-01  vtan        moved from Neptune to Whistler
//  --------------------------------------------------------------------------

#include "StandardHeader.h"
#include "UIHost.h"

#include "RegistryResources.h"
#include "StatusCode.h"
#include "SystemSettings.h"

//  --------------------------------------------------------------------------
//  CUIHost::CUIHost
//
//  Arguments:  <none>
//
//  Returns:    <none>
//
//  Purpose:    Constructor for CUIHost. Determine UI host process. If none
//              exists then indicate it.
//
//  History:    1999-09-14  vtan        created
//  --------------------------------------------------------------------------

CUIHost::CUIHost (const TCHAR *pszCommandLine) :
    CExternalProcess(),
    _hwndArray(sizeof(HWND)),
    _pBufferAddress(NULL)

{
    ExpandCommandLine(pszCommandLine);
    AdjustForDebugging();
}

//  --------------------------------------------------------------------------
//  CUIHost::~CUIHost
//
//  Arguments:  <none>
//
//  Returns:    <none>
//
//  Purpose:    Destructor for CUIHost.
//
//  History:    1999-09-14  vtan        created
//  --------------------------------------------------------------------------

CUIHost::~CUIHost (void)

{
    if (_pBufferAddress != NULL)
    {
        (BOOL)VirtualFreeEx(_hProcess, _pBufferAddress, 0, MEM_DECOMMIT);
        _pBufferAddress = NULL;
    }
}

//  --------------------------------------------------------------------------
//  CUIHost::WaitRequired
//
//  Arguments:  <none>
//
//  Returns:    bool
//
//  Purpose:    Returns whether a wait is required for the UI host. This is
//              important when communication with the UI host is required or
//              if the UI host is being debugged.
//
//  History:    2000-10-05  vtan        created
//  --------------------------------------------------------------------------

bool    CUIHost::WaitRequired (void)         const

{

#ifdef      DBG

    return(IsBeingDebugged());

#else   /*  DBG     */

    return(false);

#endif  /*  DBG     */

}

//  --------------------------------------------------------------------------
//  CUIHost::GetData
//
//  Arguments:  pUIHostProcessAddress   =   Address in the UI host.
//              pLogonProcessAddress    =   Address in the logon process.
//              iDataSize               =   Size of the data.
//
//  Returns:    NTSTATUS
//
//  Purpose:    Extracts the data from the UI host. This could be another
//              process that we started or it could be in process if we
//              failed to start the UI host. This function deals with it
//              either way.
//
//  History:    1999-08-24  vtan        created
//              1999-09-14  vtan        factored
//  --------------------------------------------------------------------------

NTSTATUS    CUIHost::GetData (const void *pUIHostProcessAddress, void *pLogonProcessAddress, int iDataSize)  const

{
    NTSTATUS    status;

    status = STATUS_SUCCESS;
    if (_hProcess == NULL)
    {
        CopyMemory(pLogonProcessAddress, pUIHostProcessAddress, iDataSize);
    }
    else
    {
        if (ReadProcessMemory(_hProcess, pUIHostProcessAddress, pLogonProcessAddress, iDataSize, NULL) == FALSE)
        {
            status = CStatusCode::StatusCodeOfLastError();
        }
    }
    return(status);
}

//  --------------------------------------------------------------------------
//  CUIHost::PutData
//
//  Arguments:  pUIHostProcessAddress   =   Address in the UI host.
//              pLogonProcessAddress    =   Address in the logon process.
//              iDataSize               =   Size of the data.
//
//  Returns:    NTSTATUS
//
//  Purpose:    Puts data into the UI host. This could be another process that
//              we started or it could be in process if we failed to start the
//              UI host. This function deals with it either way.
//
//  History:    1999-08-24  vtan        created
//              1999-09-14  vtan        factored
//  --------------------------------------------------------------------------

NTSTATUS    CUIHost::PutData (void *pUIHostProcessAddress, const void *pLogonProcessAddress, int iDataSize)  const

{
    NTSTATUS    status;

    status = STATUS_SUCCESS;
    if (_hProcess == NULL)
    {
        CopyMemory(pUIHostProcessAddress, pLogonProcessAddress, iDataSize);
    }
    else
    {
        if (WriteProcessMemory(_hProcess, pUIHostProcessAddress, const_cast<void*>(pLogonProcessAddress), iDataSize, NULL) == FALSE)
        {
            status = CStatusCode::StatusCodeOfLastError();
        }
    }
    return(status);
}

//  --------------------------------------------------------------------------
//  CUIHost::Show
//
//  Arguments:  <none>
//
//  Returns:    NTSTATUS
//
//  Purpose:    Iterate the top level windows on this desktop and for any that
//              correspond to the UI host - show them!
//
//  History:    2000-03-08  vtan        created
//  --------------------------------------------------------------------------

NTSTATUS    CUIHost::Show (void)

{
    int     i;

    i = _hwndArray.GetCount();
    if (i > 0)
    {
        for (--i; i >= 0; --i)
        {
            HWND    hwnd;

            if (NT_SUCCESS(_hwndArray.Get(&hwnd, i)) && (hwnd != NULL))
            {
                (BOOL)ShowWindow(hwnd, SW_SHOW);
            }
            TSTATUS(_hwndArray.Remove(i));
        }
    }
    return(STATUS_SUCCESS);
}

//  --------------------------------------------------------------------------
//  CUIHost::Hide
//
//  Arguments:  <none>
//
//  Returns:    NTSTATUS
//
//  Purpose:    Iterate the top level windows on this desktop and for any that
//              correspond to the UI host - hide them!
//
//  History:    2000-03-08  vtan        created
//  --------------------------------------------------------------------------

NTSTATUS    CUIHost::Hide (void)

{
    NTSTATUS    status;

    status = STATUS_SUCCESS;
    if (_hwndArray.GetCount() == 0)
    {
        if (EnumWindows(EnumWindowsProc, reinterpret_cast<LPARAM>(this)) == FALSE)
        {
            status = CStatusCode::StatusCodeOfLastError();
        }
    }
    return(status);
}

//  --------------------------------------------------------------------------
//  CUIHost::IsHidden
//
//  Arguments:  <none>
//
//  Returns:    bool
//
//  Purpose:    Returns whether the UI host is currently hidden or not.
//
//  History:    2000-07-05  vtan        created
//  --------------------------------------------------------------------------

bool    CUIHost::IsHidden (void)     const

{
    return(_hwndArray.GetCount() != 0);
}

//  --------------------------------------------------------------------------
//  CUIHost::GetDataAddress
//
//  Arguments:  <none>
//
//  Returns:    void*
//
//  Purpose:    Returns the address of the buffer valid in the UI host process
//              context.
//
//  History:    2000-05-05  vtan        created
//  --------------------------------------------------------------------------

void*   CUIHost::GetDataAddress (void)       const

{
    return(_pBufferAddress);
}

//  --------------------------------------------------------------------------
//  CUIHost::PutData
//
//  Arguments:  pvData      =   Pointer to data.
//              dwDataSize  =   Size of data (in bytes).
//
//  Returns:    NTSTATUS
//
//  Purpose:    Writes the data to the UI host process at an allocated
//              address. If the address has not been allocated then it's
//              allocated and cached. It's released when this object goes
//              out of scope.
//
//  History:    2000-05-05  vtan        created
//              2001-01-10  vtan        changed to generic data placement
//  --------------------------------------------------------------------------

NTSTATUS    CUIHost::PutData (const void *pvData, DWORD dwDataSize)

{
    NTSTATUS    status;

    if (_pBufferAddress == NULL)
    {
        _pBufferAddress = VirtualAllocEx(_hProcess,
                                         0,
                                         2048,
                                         MEM_COMMIT,
                                         PAGE_READWRITE);
    }
    if (_pBufferAddress != NULL)
    {
        ASSERTMSG(dwDataSize < 2048, "Impending kernel32!WriteProcessMemory failure in CUIHost::PutData");
        if (WriteProcessMemory(_hProcess,
                               _pBufferAddress,
                               const_cast<void*>(pvData),
                               dwDataSize,
                               NULL) != FALSE)
        {
            status = STATUS_SUCCESS;
        }
        else
        {
            status = CStatusCode::StatusCodeOfLastError();
        }
    }
    else
    {
        status = STATUS_NO_MEMORY;
    }
    return(status);
}

//  --------------------------------------------------------------------------
//  CUIHost::PutString
//
//  Arguments:  pszString   =   String to put into UI host process.
//
//  Returns:    NTSTATUS
//
//  Purpose:    Writes the string to the UI host process at an allocated
//              address. If the address has not been allocated then it's
//              allocated and cached. It's released when this object goes
//              out of scope.
//
//  History:    2000-05-05  vtan        created
//  --------------------------------------------------------------------------

NTSTATUS    CUIHost::PutString (const WCHAR *pszString)

{
    ASSERTMSG(lstrlenW(pszString) < 256, "Too many characters in string passed to CUIHost::PutString");
    return(PutData(pszString, (lstrlenW(pszString) + sizeof('\0')) * sizeof(WCHAR)));
}

//  --------------------------------------------------------------------------
//  CUIHost::NotifyNoProcess
//
//  Arguments:  <none>
//
//  Returns:    NTSTATUS
//
//  Purpose:    Clears the string address associated with the process that
//              has now died.
//
//  History:    2001-01-09  vtan        created
//  --------------------------------------------------------------------------

void    CUIHost::NotifyNoProcess (void)

{
    _pBufferAddress = NULL;
}

//  --------------------------------------------------------------------------
//  CUIHost::ExpandCommandLine
//
//  Arguments:  pszCommandLine  =   Command line of UI host
//
//  Returns:    <none>
//
//  Purpose:    Find out which UI host we should use for the logon UI. This
//              is specified in registry at the moment but should be a less
//              accessible place to prevent tampering. An error is returned
//              if no host is specified.
//
//  History:    1999-08-24  vtan        created
//              1999-09-14  vtan        factored
//  --------------------------------------------------------------------------

void    CUIHost::ExpandCommandLine (const TCHAR *pszCommandLine)

{
    if (ExpandEnvironmentStrings(pszCommandLine, _szCommandLine, ARRAYSIZE(_szCommandLine)) == 0)
    {
        lstrcpy(_szCommandLine, pszCommandLine);
    }
}

//  --------------------------------------------------------------------------
//  CUIHost::EnumWindowsProc
//
//  Arguments:  hwnd    =   HWND from user32
//              lParam  =   this object.
//
//  Returns:    BOOL
//
//  Purpose:    Determines if the given HWND in the iteration belongs to the
//              UI host process.
//
//  History:    2000-03-08  vtan        created
//  --------------------------------------------------------------------------

BOOL    CALLBACK    CUIHost::EnumWindowsProc (HWND hwnd, LPARAM lParam)

{
    DWORD       dwThreadID, dwProcessID;
    CUIHost     *pUIHost;

    pUIHost = reinterpret_cast<CUIHost*>(lParam);
    dwThreadID = GetWindowThreadProcessId(hwnd, &dwProcessID);
    if ((dwProcessID == pUIHost->_dwProcessID) && IsWindowVisible(hwnd))
    {
        (NTSTATUS)pUIHost->_hwndArray.Add(&hwnd);
        (BOOL)ShowWindow(hwnd, SW_HIDE);
    }
    return(TRUE);
}