//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1996 - 2000
//
//  File:       srequest.cxx
//
//  Contents:   Server side of catalog/query requests
//
//  Classes:    CPipeServer
//              CRequestServer
//              CRequestQueue
//
//  Functions:  StartCiSvcWork
//              StopCiSvcWork
//
//  History:    16-Sep-96   dlee       Created.
//
//--------------------------------------------------------------------------

#include <pch.cxx>
#pragma hdrstop

#include <cci.hxx>
#include <query.hxx>
#include <rstprop.hxx>
#include <pickle.hxx>
#include <worker.hxx>
#include <propspec.hxx>
#include <seqquery.hxx>
#include <rowseek.hxx>
#include <srequest.hxx>
#include <propvar.h>
#include <ciregkey.hxx>
#include <regacc.hxx>
#include <imprsnat.hxx>
#include <driveinf.hxx>

#include <qparse.hxx>
#include <dbprputl.hxx>
#include <cidbprop.hxx>
#include <ciframe.hxx>
#include <cisvcfrm.hxx>
#include <lang.hxx>

#include <fsciexps.hxx>
#include <pidcvt.hxx>
#include <cisvcex.hxx>

#include <fsciclnt.h>

#include "secutil.hxx"

extern CLocateDocStore g_svcDocStoreLocator;
extern CStaticMutexSem g_mtxStartStop;

CRequestQueue * g_pRequestQueue = 0;

const CRequestServer::ProxyMessageFunction CRequestServer::_aMsgFunctions[] =
{
    DoConnect,
    DoDisconnect,
    DoCreateQuery,
    DoFreeCursor,
    DoGetRows,
    DoRatioFinished,
    DoCompareBmk,
    DoGetApproximatePosition,
    DoSetBindings,
    DoGetNotify,
    DoSendNotify,
    DoSetWatchMode,
    DoGetWatchInfo,
    DoShrinkWatchRegion,
    DoRefresh,
    DoGetQueryStatus,
    DoObsolete,                  //WidToPath
    DoCiState,
    DoObsolete,                  //BeginCacheTransaction
    DoObsolete,                  //SetupCache
    DoObsolete,                  //EndCacheTransaction
    DoObsolete,                  //AddScope
    DoObsolete,                  //RemoveScope
    DoObsolete,                  //AddVirtualScope
    DoObsolete,                  //RemoveVirtualScope
    DoForceMerge,
    DoAbortMerge,
    DoObsolete,                  //SetPartition
    DoFetchValue,
    DoWorkIdToPath,
    DoUpdateDocuments,
    DoGetQueryStatusEx,
    DoRestartPosition,
    DoStopAsynch,
    DoStartWatching,
    DoStopWatching,
    DoSetCatState
};

const BOOL CRequestServer::_afImpersonate[] =
{
    TRUE,   //    DoConnect,
    FALSE,  //    DoDisconnect,
    TRUE,   //    DoCreateQuery,
    FALSE,  //    DoFreeCursor,
    TRUE,   //    DoGetRows,
    FALSE,  //    DoRatioFinished,
    FALSE,  //    DoCompareBmk,
    FALSE,  //    DoGetApproximatePosition,
    FALSE,  //    DoSetBindings,
    FALSE,  //    DoGetNotify,
    FALSE,  //    DoSendNotify,
    FALSE,  //    DoSetWatchMode,
    FALSE,  //    DoGetWatchInfo,
    FALSE,  //    DoShrinkWatchRegion,
    FALSE,  //    DoRefresh,
    FALSE,  //    DoGetQueryStatus,
    FALSE,  //    DoObsolete,                  //DoWidToPath,
    FALSE,  //    DoCiState,
    TRUE,   //    DoBeginCacheTransaction,
    TRUE,   //    DoSetupCache,
    TRUE,   //    DoEndCacheTransaction,
    FALSE,  //    DoObsolete,                  //AddScope
    FALSE,  //    DoObsolete,                  //RemoveScope
    FALSE,  //    DoObsolete,                  //AddVirtualScope
    FALSE,  //    DoObsolete,                  //RemoveVirtualScope
    TRUE,   //    DoForceMerge,
    TRUE,   //    DoAbortMerge,
    FALSE,  //    DoObsolete,                  //SetPartition
    TRUE,   //    DoFetchValue,
    TRUE,   //    DoWorkIdToPath,
    TRUE,   //    DoUpdateDocuments,
    FALSE,  //    DoGetQueryStatusEx,
    FALSE,  //    DoRestartPosition,
    FALSE,  //    DoStopAsynch,
    FALSE,  //    DoStartWatching,
    FALSE,  //    DoStopWatching,
    TRUE,   //    DoSetCatState
};

//+-------------------------------------------------------------------------
//
//  Member:     CPipeServer::CPipeServer, public
//
//  Synopsis:   Constructor for a server pipe.  The pipe is created such
//              that any client can connect.
//
//  Arguments:  [pwcName]                 - Name for the pipe
//              [cmsDefaultClientTimeout] - Default timeout for clients
//                                          trying to get a pipe instance
//              [pSecurityDescriptor]     - Security for the pipe
//
//  History:    16-Sep-96   dlee       Created.
//
//--------------------------------------------------------------------------

CPipeServer::CPipeServer(
    const WCHAR *         pwcName,
    ULONG                 cmsDefaultClientTimeout,
    SECURITY_DESCRIPTOR * pSecurityDescriptor ) :
    _eventWrite( (HANDLE) 0 ) // defer creation of the notification event
{
    RtlZeroMemory( &_overlapped, sizeof _overlapped );

    SECURITY_ATTRIBUTES saPipeSecurity;
    saPipeSecurity.nLength = sizeof SECURITY_ATTRIBUTES;
    saPipeSecurity.bInheritHandle = FALSE;
    saPipeSecurity.lpSecurityDescriptor = pSecurityDescriptor;

    // note: the read/write buffers are allocated from non-paged pool,
    //       and are grown if too small to reflect usage

    _hPipe = CreateNamedPipe( pwcName,
                              PIPE_ACCESS_DUPLEX |
                                  FILE_FLAG_OVERLAPPED,
                              PIPE_TYPE_MESSAGE |
                                  PIPE_READMODE_MESSAGE |
                                  PIPE_WAIT,
                              PIPE_UNLIMITED_INSTANCES,
                              1024,      // read buffer size
                              2048,      // write buffer size
                              cmsDefaultClientTimeout,
                              &saPipeSecurity );

    if ( INVALID_HANDLE_VALUE == _hPipe )
        THROW( CException() );

    prxDebugOut(( DEB_ITRACE, "created pipe 0x%x\n", _hPipe ));
} //CPipeServer

//+-------------------------------------------------------------------------
//
//  Member:     CPipeServer::Connect, public
//
//  Synopsis:   Enables a client to connect to the pipe.
//
//  Returns:    TRUE if connected, FALSE if GetEvent() will be signalled
//              on connection, or throws on error.
//
//  History:    16-Sep-96   dlee       Created.
//
//--------------------------------------------------------------------------

BOOL CPipeServer::Connect()
{
    prxDebugOut(( DEB_ITRACE, "connecting from pipe 0x%x\n", _hPipe ));

    _overlapped.hEvent = _event.GetHandle();
    BOOL fEventWait = ConnectNamedPipe( _hPipe, &_overlapped );

    if ( !fEventWait )
    {
        DWORD dwError = GetLastError();

        prxDebugOut(( DEB_ITRACE, "CPipeServer::Connect.. GetLastError() == %d\n", dwError ));

        if ( ERROR_PIPE_CONNECTED == dwError )
            return TRUE;
        else if ( ERROR_IO_PENDING != dwError )
            THROW( CException() );
    }

    return FALSE;
} //Connect

//+-------------------------------------------------------------------------
//
//  Member:     CPipeServer::ReadRemainingSync, public
//
//  Synopsis:   Reads the remainder of a message from the pipe (if any)
//
//  Arguments:  [pvSoFar]  - Part of the message read so far
//              [cbSoFar]  - in:  # of bytes read so far for the message
//                           out: # bytes total in message
//
//  Returns:    Pointer to memory containing entire message or 0 if
//              there really wasn't anything more to read
//
//  History:    16-Sep-96   dlee       Created.
//
//--------------------------------------------------------------------------

BYTE * CPipeServer::ReadRemainingSync(
    void *  pvSoFar,
    DWORD & cbSoFar )
{
    DWORD cbLeft;

    if ( ! PeekNamedPipe( _hPipe, 0, 0, 0, 0, &cbLeft ) )
        THROW( CException() );

    if ( 0 == cbLeft )
    {
        prxDebugOut(( DEB_WARN, "peek says no more to read\n" ));
        return 0;
    }

    XArray<BYTE> xMsg( cbSoFar + cbLeft );
    RtlCopyMemory( xMsg.Get(), pvSoFar, cbSoFar );

    CEventSem evt;
    OVERLAPPED o;
    RtlZeroMemory( &o, sizeof o );
    o.hEvent = evt.GetHandle();

    DWORD cbRead;
    if ( ! ReadFile( _hPipe,
                     xMsg.Get() + cbSoFar,
                     cbLeft,
                     &cbRead,
                     &o ) )
    {
        if ( ERROR_IO_PENDING == GetLastError() )
        {
            if ( !GetOverlappedResult( _hPipe,
                                       &o,
                                       &cbRead,
                                       TRUE ) )
                THROW( CException() );
        }
        else
            THROW( CException() );
    }

    Win4Assert( cbRead == cbLeft );
    cbSoFar += cbLeft;
    return xMsg.Acquire();
} //ReadRemainingSync

//+-------------------------------------------------------------------------
//
//  Member:     CPipeServer::WriteSync, public
//
//  Synopsis:   Does a synchronous write to a pipe.  There can be at most
//              one caller at a time to this method.
//
//  Arguments:  [pv] - Buffer to write
//              [cb] - # of bytes to write
//
//  History:    16-Sep-96   dlee       Created.
//
//--------------------------------------------------------------------------

void CPipeServer::WriteSync(
    void * pv,
    DWORD  cb )
{
    prxDebugOut(( DEB_ITRACE, "WriteSync on this 0x%x, pipe 0x%x\n",
                  this, _hPipe ));

    OVERLAPPED o;
    RtlZeroMemory( &o, sizeof o );

    // Create and keep the event, since if we do it once (for notifications)
    // we'll likely do it again soon.

    if ( 0 == _eventWrite.GetHandle() )
        _eventWrite.Create();

    o.hEvent = _eventWrite.GetHandle();

    if ( ! WriteFile( _hPipe, pv, cb, 0, &o ) )
    {
        if ( ERROR_IO_PENDING == GetLastError() )
        {
            DWORD cbWritten;
            if ( !GetOverlappedResult( _hPipe,
                                       &o,
                                       &cbWritten,
                                       TRUE ) )
                THROW( CException() );

            Win4Assert( cbWritten == cb );
        }
        else
            THROW( CException() );
    }
    prxDebugOut(( DEB_ITRACE, "WriteSync completed on this 0x%x, pipe 0x%x\n",
                  this, _hPipe ));
} //WriteSync

//+-------------------------------------------------------------------------
//
//  Member:     CRequestServer::CRequestServer, public
//
//  Synopsis:   Constructor for the request server
//
//  Arguments:  [pwcPipe]                 - Name of the pipe to use
//              [cmsDefaultClientTimeout] - Timeout for clients waiting for
//                                          and instance
//              [requestQueue]            - The 1 and only request queue
//              [workQueue]               - The work queue
//
//  History:    16-Sep-96   dlee       Created.
//
//--------------------------------------------------------------------------

CRequestServer::CRequestServer(
    const WCHAR *   pwcPipe,
    ULONG           cmsDefaultClientTimeout,
    CRequestQueue & requestQueue,
    CWorkQueue &    workQueue ) :
    CPipeServer( pwcPipe,
                 cmsDefaultClientTimeout,
                 requestQueue.GetSecurityDescriptor() ),
    PWorkItem( sigCRequestServer ),
    _state( pipeStateNone ),
    _cRefs( 1 ),
    _iClientVersion( 0 ),
    _pQuery( 0 ),
    _fClientIsRemote( FALSE ),
    _pWorkThread( 0 ),
    _hWorkThread( INVALID_HANDLE_VALUE ),
    _cbFetchedValueSoFar( 0 ),
    _cbPendingWrite( 0 ),
    _scPendingStatus( S_OK ),
    _requestQueue( requestQueue ),
    _workQueue( workQueue ),
    _pevtDone( 0 ),
    _dwLastTouched( GetTickCount() )
{
    requestQueue.AddToListNoThrow( this );
} //CRequestServer

//+-------------------------------------------------------------------------
//
//  Member:     CRequestServer::Release, public
//
//  Synopsis:   Releases and if approprate deletes the object
//
//  History:    16-Sep-96   dlee       Created.
//
//--------------------------------------------------------------------------

void CRequestServer::Release()
{
    Win4Assert( 0 != _cRefs );

    CLock lock( _requestQueue.GetTheMutex() );

    // Since the AddRef() is not done under lock (and we don't want to)
    // InterlockedDecrement is used even though we're under lock.

    if ( 0 == InterlockedDecrement( & _cRefs ) )
    {
        _requestQueue.RemoveFromListNoThrow( this );

        // If someone is waiting for this to go out of the list,
        // tell them now.

        if ( 0 != _pevtDone )
        {
            _pevtDone->Set();
            _pevtDone = 0;
        }

        delete this;
    }
} //Release

//+-------------------------------------------------------------------------
//
//  Member:     CRequestServer::DoIt, public
//
//  Synopsis:   Implements the work queue DoIt method by scheduling
//              the first read from the client of the pipe.  The work queue
//              is needed since apc completion routines must be run by
//              the thread that submitted the i/o.
//
//  Arguments:  [pThread] - Thread being used (for refcounting)
//
//  History:    16-Sep-96   dlee       Created.
//
//--------------------------------------------------------------------------

void CRequestServer::DoIt(
    CWorkThread *pThread )
{
    TRY
    {
        Win4Assert( pipeStateNone == _state );
        Win4Assert( 0 == _pWorkThread );
        Win4Assert( INVALID_HANDLE_VALUE == _hWorkThread );
        prxDebugOut(( DEB_ITRACE, "doit CRequestServer %p\n", this ));

        _pWorkThread = pThread;
        _hWorkThread = pThread->GetThreadHandle();
        _workQueue.AddRef( pThread );
        _state = pipeStateRead;

        Read( _Buffer(), _BufferSize(), APCRoutine );

        if ( IsBeingRemoved() )
            CancelIO();
    }
    CATCH( CException, ex )
    {
        prxDebugOut(( DEB_ITRACE,
                      "failed initial read error 0x%x, pipe %p\n",
                      ex.GetErrorCode(),
                      GetPipe() ));

        _state = pipeStateNone;
        _requestQueue.RecycleRequestServerNoThrow( this );
    }
    END_CATCH;
} //DoIt

//+-------------------------------------------------------------------------
//
//  Function:   TranslateOldPropsToNewProps
//
//  Synopsis:   Translates ver 5 client properties into version 6+ props
//
//  Arguments:  [oldProps]    - source of translated values
//              [newProps]    - destination of translated values
//
//  History:    31-May-97   dlee       Created.
//
//--------------------------------------------------------------------------

void TranslateOldPropsToNewProps(
    CDbProperties & oldProps,
    CDbProperties & newProps )
{
    const GUID guidFsClientPropset = DBPROPSET_FSCIFRMWRK_EXT;
    const GUID guidQueryCorePropset = DBPROPSET_CIFRMWRKCORE_EXT;

    // pluck out the catalog and scope information from the old set

    WCHAR *pwcCatalog = 0;
    BSTR bstrMachine = 0;
    CDynArrayInPlace<ULONG> aDepths(2);
    CDynArrayInPlace<WCHAR *> aScopes(2);
    LONG lQueryType = 0;

    for ( ULONG i = 0; i < oldProps.Count(); i++ )
    {
        CDbPropSet & propSet = oldProps.GetPropSet( i );
        DBPROPSET *pSet = propSet.CastToStruct();

        if ( guidQueryCorePropset == pSet->guidPropertySet )
        {
            ULONG cProp = pSet->cProperties;
            DBPROP * pProp = pSet->rgProperties;

            for ( ULONG p = 0; p < pSet->cProperties; p++, pProp++ )
            {
                PROPVARIANT &v = (PROPVARIANT &) pProp->vValue;
                switch ( pProp->dwPropertyID )
                {
                    case DBPROP_MACHINE :
                    {
                        Win4Assert( VT_BSTR == v.vt );
                        if ( VT_BSTR == v.vt )
                            bstrMachine = v.bstrVal;
                        break;
                    }
                }
            }
        }
        else if ( guidFsClientPropset == pSet->guidPropertySet )
        {
            ULONG cProp = pSet->cProperties;
            DBPROP * pProp = pSet->rgProperties;

            for ( ULONG p = 0; p < pSet->cProperties; p++, pProp++ )
            {
                PROPVARIANT &v = (PROPVARIANT &) pProp->vValue;
                switch ( pProp->dwPropertyID )
                {
                    case DBPROP_CI_INCLUDE_SCOPES :
                    {
                        Win4Assert( (VT_VECTOR|VT_LPWSTR) == v.vt );
                        if ( (VT_VECTOR|VT_LPWSTR) == v.vt )
                            for ( ULONG s = 0; s < v.calpwstr.cElems; s++ )
                                aScopes[s] = v.calpwstr.pElems[s];
                        break;
                    }
                    case DBPROP_CI_DEPTHS :
                    {
                        Win4Assert( (VT_VECTOR|VT_I4) == v.vt );
                        if ( (VT_VECTOR|VT_I4) == v.vt )
                            for ( ULONG s = 0; s < v.cal.cElems; s++ )
                                aDepths[s] = v.cal.pElems[s];
                        break;
                    }
                    case DBPROP_CI_CATALOG_NAME :
                    {
                        Win4Assert( VT_LPWSTR == v.vt );
                        if ( VT_LPWSTR == v.vt )
                            pwcCatalog = v.pwszVal;
                        break;
                    }
                    case DBPROP_CI_QUERY_TYPE :
                    {
                        Win4Assert( VT_I4 == v.vt );
                        lQueryType = v.lVal;
                        break;
                    }
                }
            }
        }
    }

    if ( 0 == pwcCatalog ||
         0 == bstrMachine )
        THROW( CException( STATUS_INVALID_PARAMETER_MIX ) );

    prxDebugOut(( DEB_ITRACE,
                  "Converting old props to new props, catalog '%ws'",
                  pwcCatalog ));

    prxDebugOut(( DEB_ITRACE,
                  "type %d, %d scopes, %d depths\n",
                  lQueryType,
                  aScopes.Count(),
                  aDepths.Count() ));

    // create a new set of properties based on the old info

    CDynArrayInPlace<BSTR> aBSTR( aScopes.Count() + 2 );

    DBPROPSET aNewSet[2];
    DBPROP aFSProps[4];
    RtlZeroMemory( aFSProps, sizeof aFSProps );
    aNewSet[0].cProperties = 0;
    aNewSet[0].guidPropertySet = guidFsClientPropset;
    aNewSet[0].rgProperties = aFSProps;

    aFSProps[0].dwPropertyID = DBPROP_CI_CATALOG_NAME;
    aFSProps[0].vValue.vt = VT_BSTR;
    aFSProps[0].vValue.bstrVal = SysAllocString( pwcCatalog );
    aBSTR[ aBSTR.Count() ] = aFSProps[0].vValue.bstrVal;
    aNewSet[0].cProperties++;

    aFSProps[1].dwPropertyID = DBPROP_CI_QUERY_TYPE;
    aFSProps[1].vValue.vt = VT_I4;
    aFSProps[1].vValue.lVal = lQueryType;
    aNewSet[0].cProperties++;

    DBPROP & propDepth = aFSProps[ aNewSet[0].cProperties ];

    propDepth.dwPropertyID = DBPROP_CI_DEPTHS;

    SAFEARRAY saDepth = { 1,                      // Dimension
                          FADF_AUTO,              // Flags: on stack
                          sizeof(LONG),           // Size of an element
                          1,                      // Lock count.  1 for safety.
                          (void *)aDepths.GetPointer(), // The data
                          { aDepths.Count(), 0 } };       // Bounds (element count, low bound)

    if ( 1 == aDepths.Count() )
    {
        propDepth.vValue.vt = VT_I4;
        propDepth.vValue.lVal = aDepths[0];
        aNewSet[0].cProperties++;
    }
    else if ( aDepths.Count() > 1 )
    {
        propDepth.vValue.vt = ( VT_ARRAY | VT_I4 );
        propDepth.vValue.parray = & saDepth;
        aNewSet[0].cProperties++;
    }

    DBPROP & propScope = aFSProps[ aNewSet[0].cProperties ];

    propScope.dwPropertyID = DBPROP_CI_INCLUDE_SCOPES;

    SAFEARRAY saScope = { 1,                      // Dimension
                          FADF_AUTO | FADF_BSTR,  // Flags: on stack, contains BSTRs
                          sizeof(BSTR),           // Size of an element
                          1,                      // Lock count.  1 for safety.
                          (void *)0, // The data
                          { aScopes.Count(), 0 } };       // Bounds (element count, low bound)

    if ( 1 == aScopes.Count() )
    {
        propScope.vValue.vt = VT_BSTR;
        BSTR bstr = SysAllocString( aScopes[ 0 ] );
        aBSTR[ aBSTR.Count() ] = bstr;
        propScope.vValue.bstrVal = bstr;
        aNewSet[0].cProperties++;
    }
    else if ( aScopes.Count() > 1 )
    {
        propScope.vValue.vt = ( VT_ARRAY | VT_BSTR );
        propScope.vValue.parray = & saScope;

        BSTR * pBSTR = (BSTR *) aBSTR.GetPointer();
        pBSTR += aBSTR.Count();

        for ( ULONG x = 0; x < aScopes.Count(); x++ )
            aBSTR[ aBSTR.Count() ] = SysAllocString( aScopes[ x ] );

        saScope.pvData = (void *) pBSTR;
        aNewSet[0].cProperties++;
    }

    DBPROP aQueryProps[1];
    RtlZeroMemory( aQueryProps, sizeof aQueryProps );
    aNewSet[1].cProperties = 1;
    aNewSet[1].guidPropertySet = guidQueryCorePropset;
    aNewSet[1].rgProperties = aQueryProps;

    aQueryProps[0].dwPropertyID = DBPROP_MACHINE;
    aQueryProps[0].vValue.vt = VT_BSTR;
    aQueryProps[0].vValue.bstrVal = bstrMachine;

    // Verify all the bstrs were allocated

    for ( ULONG iBstr = 0; iBstr < aBSTR.Count(); iBstr++ )
    {
        if ( 0 == aBSTR[ iBstr ] )
        {
            for ( ULONG i = 0; i < aBSTR.Count(); i++ )
            {
                if ( 0 != aBSTR[ i ] )
                    SysFreeString( aBSTR[ i ] );
            }

            THROW( CException( E_OUTOFMEMORY ) );
        }
    }

    SCODE sc = newProps.SetProperties( 2, aNewSet );

    for ( ULONG ibstr = 0; ibstr < aBSTR.Count(); ibstr++ )
        SysFreeString( aBSTR[ ibstr ] );

    if ( FAILED( sc ) )
        THROW( CException( sc ) );
} //TranslateOldPropsToNewProps

//+-------------------------------------------------------------------------
//
//  Member:     CRequestServer::DoConnect, private
//
//  Synopsis:   Handles a connect message.
//
//  Arguments:  [cbRequest] - Size of the request in _Buffer()
//              [cbToWrite] - On output, set to the # of bytes to write if
//                            other than sizeof CProxyMessage.
//
//  History:    16-Sep-96   dlee       Created.
//
//--------------------------------------------------------------------------

RequestState CRequestServer::DoConnect(
    DWORD   cbRequest,
    DWORD & cbToWrite )
{
    CPMConnectIn &request = * (CPMConnectIn *) _ActiveBuffer();
    XPtrST<BYTE> xTemp( _xTempBuffer.Acquire() );

    Win4Assert( pmConnect == request.GetMessage() );

    // guard against attack

    Win4Assert( 0 == _pQuery );
    Win4Assert( _xDocStore.IsNull() );
    if ( !_xDocStore.IsNull() )
        THROW( CException( STATUS_INVALID_PARAMETER ) );

    // save the client version, isRemote, machine name, and user name

    _iClientVersion = request.GetClientVersion();

    // don't support old clients

    if ( GetClientVersion() < 5 )
        THROW( CException( STATUS_INVALID_PARAMETER_MIX ) );

    request.ValidateCheckSum( GetClientVersion(), cbRequest );

    _fClientIsRemote = request.IsClientRemote();
    WCHAR *pwcMachine = request.GetClientMachineName();
    ULONG cwcMachine = 1 + wcslen( pwcMachine );

    // Check if the machine name looks mal-formed

    if ( ( cwcMachine * sizeof WCHAR ) >= __min( 1024, cbRequest ) )
        THROW( CException( STATUS_INVALID_PARAMETER ) );

    WCHAR *pwcUser = request.GetClientUserName();
    ULONG cwcUser = 1 + wcslen( pwcUser );

    // Check if the user name looks mal-formed

    if ( ( ( cwcMachine + cwcUser ) * sizeof WCHAR ) >= __min( 1024, cbRequest ) )
        THROW( CException( STATUS_INVALID_PARAMETER ) );

    _xClientMachine.Init( cwcMachine );
    RtlCopyMemory( _xClientMachine.Get(),
                   pwcMachine,
                   _xClientMachine.SizeOf() );

    _xClientUser.Init( cwcUser );
    RtlCopyMemory( _xClientUser.Get(),
                   pwcUser,
                   _xClientUser.SizeOf() );

    //
    // Retreive the db properties.
    //

    if ( 5 == GetClientVersion() )
    {
        // unmarshall the old-style properties, and translate into
        // the new-style properties

        if ( request.GetBlobSize() > 0 )
        {
            if ( request.GetBlobSize() >= cbRequest )
                THROW( CException( STATUS_INVALID_PARAMETER ) );

            CMemDeSerStream stmDeser( request.GetBlobStartAddr(),
                                      request.GetBlobSize() );

            XInterface<CDbProperties> xOldDbP( new CDbProperties );
            if ( xOldDbP.IsNull() || ! xOldDbP->UnMarshall( stmDeser ) )
                THROW( CException( E_OUTOFMEMORY ) );

            XInterface<CDbProperties> xNewDbP( new CDbProperties );
            if ( xNewDbP.IsNull() )
                THROW( CException( E_OUTOFMEMORY ) );
            TranslateOldPropsToNewProps( xOldDbP.GetReference(),
                                         xNewDbP.GetReference() );
            _xDbProperties.Set( xNewDbP.Acquire() );
        }
    }
    else
    {
        Win4Assert( GetClientVersion() >= 6 );

        if ( GetClientVersion() < 5 )
            THROW( CException( STATUS_INVALID_PARAMETER ) );

        if ( request.GetBlob2Size() > 0 )
        {
            if ( ( 0 == request.GetBlob2StartAddr() ) ||
                 ( ( request.GetBlobSize() + request.GetBlob2Size() ) >= cbRequest ) )
                THROW( CException( STATUS_INVALID_PARAMETER ) );

            CMemDeSerStream stmDeser( request.GetBlob2StartAddr(),
                                      request.GetBlob2Size() );

            XInterface<CDbProperties> xDbP( new CDbProperties );
            if ( xDbP.IsNull() || !xDbP->UnMarshall( stmDeser ) )
                THROW( CException( E_OUTOFMEMORY ) );

            _xDbProperties.Set( xDbP.Acquire() );
        }
    }

    prxDebugOut(( DEB_ITRACE|DEB_PRX_LOG,
                  "connect clientver %d, remote %d, machine '%ws', user '%ws'\n",
                  _iClientVersion,
                  _fClientIsRemote,
                  pwcMachine,
                  pwcUser ));

    XInterface<ICiCDocStoreLocator> xLocator( _requestQueue.DocStoreLocator() );
    Win4Assert( !xLocator.IsNull() );

    CPMConnectOut & reply = *(CPMConnectOut *) _Buffer();
    cbToWrite = sizeof reply;
    reply.ServerVersion() = pmServerVersion;

    //
    // Never fault in the docstore since docstores have been opened by now.
    //

    Win4Assert( _requestQueue.AreDocStoresOpen() );

    ICiCDocStore * pDocStore = 0;
    SCODE sc = xLocator->LookUpDocStore( _xDbProperties.GetPointer(),
                                         &pDocStore,
                                         TRUE );

    if ( SUCCEEDED(sc) )
    {
        // sc will be CI_S_NO_DOCSTORE if this is the CIADMIN catalog

        if ( 0 != pDocStore )
        {
            _xDocStore.Set( pDocStore );

            // This QI is just about guaranteed to work.

            XInterface<ICiCAdviseStatus> xAdviseStatus;
            sc = pDocStore->QueryInterface( IID_ICiCAdviseStatus,
                                            xAdviseStatus.GetQIPointer() );
            if ( S_OK != sc )
                THROW( CException(sc) );

            xAdviseStatus->IncrementPerfCounterValue( CI_PERF_RUNNING_QUERIES );
        }
    }
    else
        reply.SetStatus( CI_E_NO_CATALOG );

    return stateContinue;
} //DoConnect

//+-------------------------------------------------------------------------
//
//  Member:     CRequestServer::DoDisconnect, private
//
//  Synopsis:   Handles a disconnect message.
//
//  Arguments:  [cbRequest] - Size of the request in _Buffer()
//              [cbToWrite] - On output, set to the # of bytes to write if
//                            other than sizeof CProxyMessage.
//
//  Returns:    stateDisconnect -- the connection will be closed
//
//  History:    16-Sep-96   dlee       Created.
//
//--------------------------------------------------------------------------

RequestState CRequestServer::DoDisconnect(
    DWORD   cbRequest,
    DWORD & cbToWrite )
{
    CProxyMessage &msg = * (CProxyMessage *) _Buffer();
    Win4Assert( pmDisconnect == msg.GetMessage() );

    return stateDisconnect;
} //DoDisconnect

//+-------------------------------------------------------------------------
//
//  Member:     CRequestServer::DoCreateQuery, private
//
//  Synopsis:   Handles a create query message.
//
//  Arguments:  [cbRequest] - Size of the request in _Buffer()
//              [cbToWrite] - On output, set to the # of bytes to write if
//                            other than sizeof CProxyMessage.
//
//  History:    16-Sep-96   dlee       Created.
//
//--------------------------------------------------------------------------

RequestState CRequestServer::DoCreateQuery(
    DWORD   cbRequest,
    DWORD & cbToWrite )
{
    // if this hits, there is probably a cursor refcounting bug

    Win4Assert( 0 == _pQuery );

    if ( _xDocStore.IsNull() ||
         ( 0 != _pQuery ) )
        THROW( CException( STATUS_INVALID_PARAMETER ) );

    XInterface<ICiCDocStoreEx>  xDocStoreEx;

    SCODE sc = _xDocStore->QueryInterface( IID_ICiCDocStoreEx,
                                           xDocStoreEx.GetQIPointer() );

    if ( SUCCEEDED(sc) )
    {
        BOOL fNoQuery = FALSE;

        sc = xDocStoreEx->IsNoQuery( &fNoQuery );

        if ( S_OK == sc )
        {
            if ( fNoQuery )
                THROW( CException( QUERY_S_NO_QUERY ) );
        }
        else
            THROW ( CException( sc ) );

    }
    else
        THROW ( CException( sc ) );

    RequestState state = stateContinue;

    // The request is either in the standard buffer or the temp buffer.
    // Take ownership of the temp buffer.

    CPMCreateQueryIn &request = * (CPMCreateQueryIn *) _ActiveBuffer();
    XPtrST<BYTE> xTemp( _xTempBuffer.Acquire() );

    // marshalling / unmarshalling is inconsistent if alignment is different

    Win4Assert( isQWordAligned( &request ) );
    Win4Assert( isQWordAligned( request.Data() ) );

    request.ValidateCheckSum( GetClientVersion(), cbRequest );

    // unpickle the query

    XColumnSet         cols;
    XRestriction       rst;
    XSortSet           sort;
    XCategorizationSet categ;
    CRowsetProperties  Props;
    XPidMapper         pidmap;

    UnPickle( GetClientVersion(),
              cols,
              rst,
              sort,
              categ,
              Props,
              pidmap,
              (BYTE *) request.Data(),
              cbRequest - sizeof CProxyMessage );

    if ( Props.GetMaxResults() > 0 && Props.GetFirstRows() > 0 )
        THROW( CException( E_INVALIDARG ) );
 
    // Compute # of cursors to create and where to put them

    unsigned cCursors = 1;
    if ( 0 != categ.GetPointer() )
        cCursors += categ->Size();

    // verify the output buffer is large enough

    if ( _BufferSize() <
         ( cCursors * sizeof ULONG + sizeof CPMCreateQueryOut ) )
        THROW( CException( STATUS_INVALID_PARAMETER ) );

    // get reference to CLangList to pass to CQParse objects...

    XInterface<ICiManager> xICiManager;

    sc = _xDocStore->GetContentIndex( xICiManager.GetPPointer() );
    if ( FAILED(sc) )
        THROW( CException(sc) );

    XInterface<ICiFrameworkQuery> xICiFrameworkQuery;
    sc = xICiManager->QueryInterface( IID_ICiFrameworkQuery,
                                      xICiFrameworkQuery.GetQIPointer() );
    if ( FAILED(sc) )
        THROW( CException(sc) );

    CLangList * pLangList = 0;
    sc = xICiFrameworkQuery->GetLangList((void**)&pLangList);
    if ( FAILED(sc) )
        THROW( CException(sc) );

    //
    // Set up a property mapper.  Used for restriction parsing and sort/output
    // column translation.
    //

    XInterface<IPropertyMapper> xPropMapper;
    sc = _xDocStore->GetPropertyMapper( xPropMapper.GetPPointer() );

    if ( FAILED( sc ) )
    {
         vqDebugOut(( DEB_ERROR, "CRequestServer::DoCreateQuery, GetPropertyMapper failed\n" ));
         THROW( CException( sc ) );
    }

    //
    // Adjust pidmap to translate properties.
    //

    CPidConverter PidConverter( xPropMapper.GetPointer() );
    pidmap->SetPidConverter( &PidConverter );

    // parse the query -- expand phrases, etc.

    XRestriction rstParsed;
    DWORD dwQueryStatus = 0;
    if ( !rst.IsNull() )
    {
        CQParse qparse( pidmap.GetReference(), *pLangList );
        rstParsed.Set( qparse.Parse( rst.GetPointer() ) );

        DWORD dwParseStatus = qparse.GetStatus();

        if ( ( 0 != ( dwParseStatus & CI_NOISE_PHRASE ) ) &&
             ( rst->Type() != RTVector ) &&
             ( rst->Type() != RTOr ) )
        {
            vqDebugOut(( DEB_WARN, "Query contains phrase composed "
                                   "entirely of noise words.\n" ));
            THROW( CException( QUERY_E_ALLNOISE ) );
        }

        const DWORD dwCiNoise = CI_NOISE_IN_PHRASE | CI_NOISE_PHRASE;
        if ( 0 != ( dwCiNoise & dwParseStatus ) )
            dwQueryStatus |= STAT_NOISE_WORDS;
    }

    //
    // Re-map property ids.
    //

    //
    // TODO: Get rid of this whole pid remap thing.  We should
    //       really be able to do it earlier now that the pidmap
    //       can be set up to convert fake to real pids.
    //

    XInterface<CPidRemapper> pidremap( new CPidRemapper( pidmap.GetReference(),
                                                         xPropMapper,
                                                         0, // rstParsed.GetPointer(),
                                                         cols.GetPointer(),
                                                         sort.GetPointer() ) );

    //
    // WorkID may be added to the columns requested in SetBindings.
    // Be sure it's in the pidremap from the beginning.
    //

    CFullPropSpec psWorkId( guidQuery, DISPID_QUERY_WORKID );
    pidremap->NameToReal( &psWorkId );

    XInterface<ICiQueryPropertyMapper> xQueryPropMapper;
    sc = pidremap->QueryInterface( IID_ICiQueryPropertyMapper,
                                   xQueryPropMapper.GetQIPointer() );
    if ( FAILED(sc) )
    {
        vqDebugOut(( DEB_ERROR, "DoCreateQuery - QI for property mapper failed 0x%x\n", sc ));
        THROW ( CException( sc ) ) ;
    }

    XInterface<ICiCQuerySession> xQuerySession;
    sc = _xDocStore->GetQuerySession( xQuerySession.GetPPointer() );
    if ( FAILED(sc) )
    {
        vqDebugOut(( DEB_ERROR, "DoCreateQuery - GetQuerySession failed 0x%x\n", sc ));
        THROW ( CException( sc ) ) ;
    }

    //
    // Initialize the query session
    //
    sc = xQuerySession->Init( 0,
                              0,
                              _xDbProperties.GetPointer(),
                              xQueryPropMapper.GetPointer() );
    if ( FAILED( sc ) )
        THROW( CException( sc ) );

    //
    // Optimize the query
    //

    XQueryOptimizer xqopt( new CQueryOptimizer( xQuerySession,
                                                _xDocStore.GetPointer(),
                                                rstParsed,
                                                cols.GetReference(),
                                                sort.GetPointer(),
                                                pidremap.GetReference(),
                                                Props,
                                                dwQueryStatus ) );

    prxDebugOut(( DEB_ITRACE, "Query has %s1 component%s\n",
                  xqopt->IsMultiCursor() ? "> " : "",
                  xqopt->IsMultiCursor() ? "s" : "" ));
    prxDebugOut(( DEB_ITRACE, "Current component of query %s fully sorted\n",
                  xqopt->IsFullySorted() ? "is" : "is not" ));
    prxDebugOut(( DEB_ITRACE, "Current component of query %s positionable\n",
                  xqopt->IsPositionable() ? "is" : "is not" ));
    prxDebugOut(( DEB_ITRACE, "Rowset props flags: 0x%x\n",
                  Props.GetPropertyFlags() ));
    prxDebugOut(( DEB_ITRACE, "Rowset props maxresults: %d\n",
                  Props.GetMaxResults() ));
    prxDebugOut(( DEB_ITRACE, "Rowset props cmdtimeout: %d\n",
                  Props.GetCommandTimeout() ));

    CPMCreateQueryOut &reply = * (CPMCreateQueryOut *) _Buffer();
    ULONG *aCursors = reply.GetCursors();

    reply.IsWorkIdUnique() = xqopt->IsWorkidUnique();

    reply.SetServerCookie( (ULONG_PTR) this );

    // create either a true sequential or bigtable query

    if ( ( (Props.GetPropertyFlags() & eLocatable) == 0) &&
         ( !xqopt->IsMultiCursor() )             &&
         ( xqopt->IsFullySorted() )              &&
         ( 0 == categ.GetPointer() ) )
    {
        _pQuery = new CSeqQuery( xqopt,
                                 cols,
                                 aCursors,
                                 pidremap,
                                 _xDocStore.GetPointer() );
        reply.IsTrueSequential() = TRUE;
    }
    else
    {
        reply.IsTrueSequential() = FALSE;

        // If we've been instructed to create a synchronous cursor,
        // put the request into a pending state

        BOOL fSync = ( 0 == ( Props.GetPropertyFlags() & eAsynchronous ) );

        XInterface<CRequestServer> xMe;

        if ( fSync )
        {
            // Refcount ourselves in case the async write fails and
            // the request server is recycled before this thread returns.

            AddRef();
            xMe.Set( this );
            state = statePending;
            _state = pipeStatePending;
            _scPendingStatus = S_OK;
            _cbPendingWrite = sizeof CPMCreateQueryOut +
                              ( sizeof ULONG * cCursors );
        }

        TRY
        {
            CRequestQueue & requestQueue = _requestQueue;

            PQuery *pQ = new CAsyncQuery( xqopt,
                                          cols,
                                          sort,
                                          categ,
                                          cCursors,
                                          aCursors,
                                          pidremap,
                                          (Props.GetPropertyFlags() & eWatchable) != 0,
                                          _xDocStore.GetPointer(),
                                          fSync ? this : 0 );

            // If synchronous, the query may be done and freed
            // by the client app by now.  the CRequestServer is still
            // valid due to the AddRef above.

            if ( fSync )
                requestQueue.IncrementPendingItems();
            else
                _pQuery = pQ;
        }
        CATCH( CException, e )
        {
            _pQuery = 0;
            _state = pipeStateRead;
            RETHROW();
        }
        END_CATCH
    }

    cbToWrite = sizeof CPMCreateQueryOut + ( sizeof ULONG * cCursors );

    return state;
} //DoCreateQuery

//+-------------------------------------------------------------------------
//
//  Member:     CRequestServer::DoFreeCursor, private
//
//  Synopsis:   Handles a free cursor message.
//
//  Arguments:  [cbRequest] - Size of the request in _Buffer()
//              [cbToWrite] - On output, set to the # of bytes to write if
//                            other than sizeof CProxyMessage.
//
//  History:    16-Sep-96   dlee       Created.
//
//--------------------------------------------------------------------------

RequestState CRequestServer::DoFreeCursor(
    DWORD   cbRequest,
    DWORD & cbToWrite )
{
    if ( 0 == _pQuery )
        THROW( CException( STATUS_INVALID_PARAMETER ) );

    CPMFreeCursorIn &request = * (CPMFreeCursorIn *) _Buffer();
    CPMFreeCursorOut &reply = * (CPMFreeCursorOut *) _Buffer();

    reply.CursorsRemaining() = _pQuery->FreeCursor( request.GetCursor() );

    // The client is allowed to serially do multiple queries on 1 connection.
    // The way a query is released is by freeing all of its cursors.
    // If this is the last cursor reference to the query, delete the query.

    if ( 0 == reply.CursorsRemaining() )
        FreeQuery();

    cbToWrite = sizeof reply;

    return stateContinue;
} //DoFreeCursor

//+-------------------------------------------------------------------------
//
//  Member:     CRequestServer::DoGetRows, private
//
//  Synopsis:   Handles a get rows message.
//
//  Arguments:  [cbRequest] - Size of the request in _Buffer()
//              [cbToWrite] - On output, set to the # of bytes to write if
//                            other than sizeof CProxyMessage.
//
//  History:    16-Sep-96   dlee       Created.
//              12-Nov-99   KLam       Adjust results for Win64 server to
//                                     Win32 client.
//
//--------------------------------------------------------------------------

RequestState CRequestServer::DoGetRows(
    DWORD   cbRequest,
    DWORD & cbToWrite )
{
    if ( 0 == _pQuery )
        THROW( CException( STATUS_INVALID_PARAMETER ) );

    CPMGetRowsIn & request = * (CPMGetRowsIn *) _Buffer();
    request.ValidateCheckSum( GetClientVersion(), cbRequest );

    //  The input buffer must be as large as the parameter buffer
    //  plus the size of the serialized seek description.

    Win4Assert( _BufferSize() >= sizeof CPMGetRowsOut + request.GetSeekSize() );

    if ( _BufferSize() < sizeof CPMGetRowsOut + request.GetSeekSize() )
        THROW( CException( STATUS_INVALID_PARAMETER ) );

    unsigned cbReserved = request.GetReservedSize();
    unsigned cbRowWidth = request.GetRowWidth();
    unsigned cbClientRead = request.GetReadBufferSize();
    unsigned cRowsToTransfer = request.GetRowsToTransfer();
    BOOL fFwdFetch = request.GetFwdFetch();
    ULONG_PTR ulClientBase = request.GetClientBase();
    ULONG hCursor = request.GetCursor();

    // validate params agains attack

    if ( 0 == cbRowWidth ||
         cbRowWidth > cbClientRead ||
         cbReserved >= cbClientRead )
    {
        Win4Assert( 0 != cbRowWidth );
        Win4Assert( cbRowWidth <= cbClientRead );
        Win4Assert( cbReserved < cbClientRead );
        THROW( CException( STATUS_INVALID_PARAMETER ) );
    }

    // Deserialize the row seek description

    Win4Assert( isQWordAligned( (void*) UIntToPtr( cbReserved ) ) );
    Win4Assert( isQWordAligned( request.GetDesc() ) );
    CMemDeSerStream stmDeser( request.GetDesc(), request.GetSeekSize() );
    XPtr<CRowSeekDescription> pRowSeek;
    UnmarshallRowSeekDescription( stmDeser, GetClientVersion(), pRowSeek, FALSE );

    CPMGetRowsOut *pReply;

    // use a temporary buffer if the client is doing a large read

    if ( cbClientRead <= _BufferSize() )
    {
        pReply = (CPMGetRowsOut *) _Buffer();
    }
    else
    {
        _xTempBuffer.Init( cbClientRead );
        pReply = new( _xTempBuffer.Get() ) CPMGetRowsOut;
    }

    #if CIDBG == 1
        RtlFillMemory( ( (BYTE *) pReply ) + sizeof CPMGetRowsOut,
                         cbClientRead - sizeof CPMGetRowsOut,
                         0xca );
    #endif // CIDBG == 1

    // cbReserved includes room for the the CPMGetRowsOut and the marshalled
    // seek description.  All other space in the buffer is for row data.

    unsigned cbRealRowWidth = cbRowWidth;

#ifdef _WIN64
    if ( !IsClient64() ) // replying to a 32 bit client
        cbRealRowWidth = _cbRowWidth64;
#endif

    CFixedVarBufferAllocator Alloc( pReply,
                                    ulClientBase,
                                    cbClientRead,
                                    cbRealRowWidth,
                                    cbReserved );

    CGetRowsParams Fetch( cRowsToTransfer,
                          fFwdFetch,
                          cbRealRowWidth,
                          Alloc );
    XPtr<CRowSeekDescription> xRowSeekOut;

    SCODE scRet = _pQuery->GetRows( hCursor,
                                    pRowSeek.GetReference(),
                                    Fetch,
                                    xRowSeekOut );

    if ( ( SUCCEEDED( scRet ) ) ||
         ( ( STATUS_BUFFER_TOO_SMALL == scRet ) && ( Fetch.RowsTransferred() > 0 ) ) )
    {
        if ( STATUS_BUFFER_TOO_SMALL == scRet )
            pReply->SetStatus( DB_S_BLOCKLIMITEDROWS );
        else
            pReply->SetStatus( scRet );

        pReply->RowsReturned() = Fetch.RowsTransferred();

#ifdef _WIN64
        if ( !IsClient64() ) // replying to a 32 bit client
        {
            // Reply + CPMGetRowsOut + seek info => table info
            BYTE *pbResults = ((BYTE *)pReply) + cbReserved;

            unsigned cbResults = cbClientRead - cbReserved;
            unsigned cRows = Fetch.RowsTransferred();
            
            //
            // Adjust the sizes of the results to fit for a Win32 client
            //
            FixRows ( (BYTE *)pReply, pbResults, cbResults,
                      ulClientBase, cRows, cbRowWidth );
        }
#endif

        //
        // We can't say everything is ok and fetch 0 rows.  This assert was
        // added because we hit this situation on the client side during
        // stress.
        //

        Win4Assert( ! ( ( S_OK == scRet ) &&
                        ( 0 == Fetch.RowsTransferred() ) ) );

        if ( xRowSeekOut.IsNull() )
        {
            ULONG *pul = (ULONG *) pReply->GetSeekDesc();
            *pul = 0;
        }
        else
        {
            Win4Assert( ( xRowSeekOut->MarshalledSize() +
                          sizeof CPMGetRowsOut ) <= cbReserved );
            CMemSerStream stmMem( pReply->GetSeekDesc(),
                                  xRowSeekOut->MarshalledSize() );
            xRowSeekOut->Marshall( stmMem );
        }

        // Have to transfer the entire buffer since variable data is
        // written from the top of the buffer down, and fixed data from
        // the bottom of the buffer up.

        cbToWrite = cbClientRead;
    }
    else
    {
        // This exception is too commmon in testing...

        QUIETTHROW( CException( scRet ) );
    }

    prxDebugOut(( DEB_ITRACE, "status at end of getrows: 0x%x\n",
                  pReply->GetStatus() ));

    return stateContinue;
} //DoGetRows

#ifdef _WIN64

//+-------------------------------------------------------------------------
//
//  Member:     CRequestServer::FixRows, private
//
//  Synopsis:   Adjusts Win64 rows into Win32 rows.
//
//  Arguments:  [pbReply]       - Reply buffer
//              [pbResults]     - Buffer containting result data
//              [cbResults]     - Number of bytes in results buffer
//              [ulpClientBase] - Offset of buffer on the client
//              [cRows]         - Count of rows
//              [cbRowWidth]    - Bytes in a row
//
//  History:    12-Nov-99   KLam     created
//
//--------------------------------------------------------------------------
void CRequestServer::FixRows ( BYTE * pbReply,
                               BYTE * pbResults,
                               ULONG cbResults,
                               ULONG_PTR ulpClientBase,
                               ULONG cRows,
                               ULONG cbRowWidth )
{
    prxDebugOut(( DEB_ITRACE,
                  "64bit server replying to 32bit client, fixing rows!\n" ));

    Win4Assert( !IsClient64() );

    unsigned cCols = _xCols64->Count();
    
    // Save the result buffer
    BYTE *pbCurrentResults = new BYTE[cbResults];
    XPtrST<BYTE> xbCurrentResults( pbCurrentResults );

    prxDebugOut(( DEB_ITRACE, 
                  "\tCopying %d bytes from 0x%I64x to 0x%I64x\n",
                  cbResults, pbResults, pbCurrentResults ));

    memcpy( pbCurrentResults, pbResults, cbResults );

#if CIDBG == 1
    RtlFillMemory( pbResults,
                   cRows * _cbRowWidth64,
                   0xba );
#endif  // CIDBG == 1

    prxDebugOut(( DEB_ITRACE, 
                  "\tReturning %d rows and %d cols each row being %d bytes (32bit: %d) for a total of %d in a %d bytes buffer\n",
                  cRows, cCols, _cbRowWidth64, cbRowWidth, cRows * _cbRowWidth64, cbResults ));

    prxDebugOut(( DEB_ITRACE, 
                  "\t64bit results are at: 0x%I64x 32bit results will be at: 0x%I64x\n",
                  pbCurrentResults, pbResults ));

    // Fix the results
    for ( unsigned iRow = 0; iRow < cRows; iRow++ )
    {
        prxDebugOut(( DEB_ITRACE, "\n\tRow: %d   Reply Buffer: 0x%I64x  Data Buffer: 0x%I64x\n",
                      iRow, pbCurrentResults, pbResults ));
        for ( unsigned iCol = 0; iCol < cCols; iCol++ )
        {
            prxDebugOut(( DEB_ITRACE, "\n\t\tColumn: %d\n", iCol ));
            prxDebugOut(( DEB_ITRACE, "\t\t_xCols64 0x%I64x\t_xCols32 0x%I64x\n", 
                          _xCols64.GetPointer(), _xCols32.GetPointer() ));

            CTableColumn *pCol64 = _xCols64->Get(iCol);
            CTableColumn *pCol32 = _xCols32->Get(iCol);

            if ( pCol64->IsValueStored() )
            {
                ULONG cbSize32 = 0;
                if ( pCol64->GetStoredType() == VT_VARIANT )
                {
                    PROPVARIANT *pVar64 = (PROPVARIANT *) (pbCurrentResults + pCol64->GetValueOffset());
                    PROPVARIANT32 *pVar32 = (PROPVARIANT32 *) (pbResults + pCol32->GetValueOffset());

                    prxDebugOut(( DEB_ITRACE,
                                  "\t\t Col64 Data: 0x%I64x Offset: %d Col32 Data: 0x%I64x Offset: %d\n", 
                                  pVar64, pCol64->GetValueOffset(), pVar32, pCol32->GetValueOffset() ));

                    cbSize32 = sizeof( PROPVARIANT32 ) + 
                               ((CTableVariant *)pVar64)->VarDataSize32( pbReply, ulpClientBase );
                    pVar32->wReserved2 = (WORD) cbSize32;

                    FixVariantPointers ( pVar32, pVar64, pbReply, ulpClientBase );
                }
                else if ( pCol64->GetStoredType() == VT_I4 )
                {
                    ULONG *pul64 = (ULONG *) (pbCurrentResults + pCol64->GetValueOffset());
                    ULONG *pul32 = (ULONG *) (pbResults + pCol32->GetValueOffset());

                    prxDebugOut(( DEB_ITRACE,
                                  "\t\t VT_I4: %d Col64 Data: 0x%I64x Offset: %d Col32 Data: 0x%I64x Offset: %d\n", 
                                  *pul64, pul64, pCol64->GetValueOffset(), pul32, pCol32->GetValueOffset() ));

                    cbSize32 = 4;
                    *pul32 = *pul64;
                }
                else
                {
                    // Unhandled type
                    Win4Assert ( pCol64->GetStoredType() == VT_I4 ||
                                 pCol64->GetStoredType() == VT_VARIANT );

                }

                if ( pCol64->IsStatusStored() )
                {
                    BYTE *pbStatus64 = (BYTE *) (pbCurrentResults + pCol64->GetStatusOffset());
                    BYTE *pbStatus32 = (BYTE *) (pbResults + pCol32->GetStatusOffset());
                    *pbStatus32 = *pbStatus64;
                    prxDebugOut (( DEB_ITRACE, "\t\tStatus = 0x%x\n", *pbStatus32 ));
                }

                if ( pCol64->IsLengthStored() )
                {
                    LONG *plLength64 = (LONG *) (pbCurrentResults + pCol64->GetLengthOffset());
                    LONG *plLength32 = (LONG *) (pbResults + pCol32->GetLengthOffset());
                    prxDebugOut (( DEB_ITRACE, "\t\tSize32 = %d Size64 = %d\n", cbSize32, *plLength64  ));
                    *plLength32 = cbSize32;
                    prxDebugOut (( DEB_ITRACE, "\t\tLength = %d\n", *plLength32 ));
                }
            }
        }
        
        // Move to the next row
        pbResults += cbRowWidth;
        pbCurrentResults += _cbRowWidth64;
    }
}  // FixRows

//+-------------------------------------------------------------------------
//
//  Member:     CRequestServer::FixVariantPointers, private
//
//  Synopsis:   Adjusts Win64 variants to fit into Win32 variants.
//
//  Arguments:  [pVar32]        - Pointer to 32 bit variant
//              [pVar64]        - Pointer to 64 bit variant
//              [pbResults]     - Buffer containing variant data
//              [ulClientBase]  - Offset of buffer on the client
//
//  History:    21-Oct-99   KLam     created
//              13-Feb-2000 KLam     assert needed to check element not pointer
//              15-Feb-2000 DLee     DECIMAL is special case (16 bits)
//
//--------------------------------------------------------------------------

void CRequestServer::FixVariantPointers ( PROPVARIANT32 *pVar32,
                                          PROPVARIANT *pVar64,
                                          BYTE *pbResults,
                                          ULONG_PTR ulClientBase )
{
    pVar32->vt = pVar64->vt;

    // reference or a vector or an array
    if ( CTableVariant::IsByRef ( pVar64->vt ) || 0 != (pVar64->vt & (VT_VECTOR | VT_ARRAY)) )
    {
        // blob used here as a generic holder for a size and pointer
        pVar32->blob.cbSize = pVar64->blob.cbSize;
        pVar32->blob.pBlob = (PTR32)(UINT_PTR) (pVar64->blob.pBlobData);

        prxDebugOut(( DEB_ITRACE,
                      "\t\t Fixing reference. ClientBase: 0x%I64x Buffer: 0x%I64x at 0x%x\n", 
                      ulClientBase, pbResults, pVar32->blob.pBlob ));

        if ( VT_LPWSTR == pVar64->vt ||
             VT_LPSTR == pVar64->vt ||
             VT_BSTR == pVar64->vt )
            prxDebugOut(( DEB_ITRACE, "\t\tvt: %d pStr: 0x%I64x\n",
                          pVar64->vt,
                          ((UINT_PTR)pVar64->calpwstr.pElems - ulClientBase) + pbResults ));
        else
            prxDebugOut(( DEB_ITRACE, "\t\tvt: %d size: 0x%I64x pBlob: 0x%I64x\n",
                          pVar64->vt, pVar64->blob.cbSize, pVar64->blob.pBlobData ));

        // Vector of strings
        if ( (VT_VECTOR|VT_LPWSTR) == pVar64->vt ||
             (VT_VECTOR|VT_LPSTR) == pVar64->vt ||
             (VT_VECTOR|VT_BSTR) == pVar64->vt )
        {
            // Get a pointer to the vector in the variable section of the return buffer
            void ** ppvVector = (void **)( ((UINT_PTR)pVar64->calpwstr.pElems - ulClientBase ) + pbResults );
            ULONG * pulVector = (ULONG *) ppvVector;

            prxDebugOut(( DEB_ITRACE, "\t\t Fixing vector 0x%I64x\n", ppvVector ));

            for ( unsigned iElement = 0; iElement < pVar64->calpwstr.cElems; ++iElement )
                pulVector[iElement] = (ULONG) (UINT_PTR) (ppvVector[iElement]);
        }
        // Vector of variants
        else if ( (VT_VARIANT|VT_VECTOR) == pVar64->vt ) // not currently supported
        {
            PROPVARIANT * pVarVector64 = (PROPVARIANT *)( ((UINT_PTR)pVar64->capropvar.pElems - ulClientBase ) + pbResults );
            PROPVARIANT32 * pVarVector32 = (PROPVARIANT32 *) pVarVector64;

            // Recursively fix each variant in the vector
            for ( unsigned iVarElement = 0; iVarElement < pVar64->capropvar.cElems; ++iVarElement )
                FixVariantPointers ( &pVarVector32[iVarElement],
                                     &pVarVector64[iVarElement],
                                     pbResults,
                                     ulClientBase );
        }
        // Arrays (Safearrays)
        else if ( ( 0 != (VT_ARRAY & pVar64->vt) ) || 
                  ( VT_SAFEARRAY == (pVar64->vt & VT_TYPEMASK) ) )
        {
            // Get a pointer to the vector in the variable section of the return buffer
            SAFEARRAY * psa = (SAFEARRAY *)(( (UINT_PTR)pVar64->pparray - ulClientBase ) + pbResults );
            SAFEARRAY32 * psa32 = (SAFEARRAY32 *) psa;
            psa32->pvData = (PTR32) (UINT_PTR) psa->pvData;
            // memmove is smart enough to move the overlapping bytes first
            memmove ( psa32->rgsabound, 
                      psa->rgsabound, 
                      psa32->cDims * sizeof (SAFEARRAYBOUND) );

            if ( VT_BSTR == (pVar64->vt & VT_TYPEMASK) )
            {
                // Pointing to an array of 32bit pointers so adjust the size
                psa32->cbElements = sizeof ( PTR32 ); 

                // Get the number of elements in the safe array
                unsigned cBstrElements = psa32->rgsabound[0].cElements;
                for ( unsigned j = 1; j < psa32->cDims; j++ )
                    cBstrElements *= psa32->rgsabound[j].cElements;

                ULONG *pulBstr = (ULONG *) ((psa32->pvData - ulClientBase) + pbResults );
                void **ppvBstr = (void **) pulBstr;

                for ( j = 0; j < cBstrElements; j++ )
                {
                    // Make sure Win64 isn't passing to big of a pointer
                    if ( 0 != (((ULONG_PTR)ppvBstr[j]) & 0xFFFFFFFF00000000) )
                    {
                        prxDebugOut(( DEB_ERROR, "Non 32 bit pointer 0x%I64x !!!\n",
                                      (ULONG_PTR)ppvBstr[j] ));
                        Win4Assert( 0 == (((ULONG_PTR)ppvBstr[j]) & 0xFFFFFFFF00000000) );
                    }

                    pulBstr[j] = (ULONG) (ULONG_PTR) ppvBstr[j];
                }
            }
            else if ( VT_VARIANT == (pVar64->vt & VT_TYPEMASK) )
            {
                // Pointing to an array of 32bit variants so adjust the size
                psa32->cbElements = sizeof ( PROPVARIANT32 ); 

                // Get the number of elements in the safe array
                unsigned cVariants = psa32->rgsabound[0].cElements;
                for ( unsigned j = 1; j < psa32->cDims; j++ )
                    cVariants *= psa32->rgsabound[j].cElements;

                PROPVARIANT * avar64 = (PROPVARIANT *) ((psa32->pvData - ulClientBase) + pbResults );
                PROPVARIANT32 * avar32 = (PROPVARIANT32 *) avar64;

                prxDebugOut(( DEB_ITRACE, "\t\t Found %d Variants at 0x%I64x\n", cVariants, avar64 ));

                // Recursively fix each variant in the array
                for ( unsigned v = 0; v < cVariants; v++ )
                    FixVariantPointers ( &avar32[v],
                                         &avar64[v],
                                         pbResults,
                                         ulClientBase );
            }
        }
    }
    else if ( VT_DECIMAL == pVar64->vt )
    {
        RtlCopyMemory ( pVar32, pVar64, sizeof PROPVARIANT32 );
    }
    else
    {
        pVar32->uhVal = (ULONGLONG)(UINT_PTR) pVar64->pulVal;

        prxDebugOut(( DEB_ITRACE, "\t\tvt: %d value: 0x%I64x\n\n",
                      pVar64->vt, pVar64->uhVal ));
    }
} //FixVariantPointers
#endif // _WIN64

//+-------------------------------------------------------------------------
//
//  Member:     CRequestServer::DoRestartPosition, private
//
//  Synopsis:   Handles a restart position message.
//
//  Arguments:  [cbRequest] - Size of the request in _Buffer()
//              [cbToWrite] - On output, set to the # of bytes to write if
//                            other than sizeof CProxyMessage.
//
//  History:    17-Apr-97   emilyb     created
//
//--------------------------------------------------------------------------

RequestState CRequestServer::DoRestartPosition(
    DWORD   cbRequest,
    DWORD & cbToWrite )
{
    if ( 0 == _pQuery )
        THROW( CException( STATUS_INVALID_PARAMETER ) );

    CPMRestartPositionIn & request = * (CPMRestartPositionIn *) _Buffer();

    _pQuery->RestartPosition( request.GetCursor(),
                              request.GetChapter() );

    return stateContinue;
} //DoRestartPosition

//+-------------------------------------------------------------------------
//
//  Member:     CRequestServer::DoStopAsynch, private
//
//  Synopsis:   Handles a stop processing of async rowset message.
//
//  Arguments:  [cbRequest] - Size of the request in _Buffer()
//              [cbToWrite] - On output, set to the # of bytes to write if
//                            other than sizeof CProxyMessage.
//
//  History:    17-Apr-97   emilyb     created
//
//--------------------------------------------------------------------------

RequestState CRequestServer::DoStopAsynch(
    DWORD   cbRequest,
    DWORD & cbToWrite )
{
    if ( 0 == _pQuery )
        THROW( CException( STATUS_INVALID_PARAMETER ) );

    CPMStopAsynchIn & request = * (CPMStopAsynchIn *) _Buffer();

    _pQuery->StopAsynch( request.GetCursor() );

    return stateContinue;
} //DoStopAsynch

//+-------------------------------------------------------------------------
//
//  Member:     CRequestServer::DoStartWatching, private
//
//  Synopsis:   Handles a start watch all behavior for rowset message.
//
//  Arguments:  [cbRequest] - Size of the request in _Buffer()
//              [cbToWrite] - On output, set to the # of bytes to write if
//                            other than sizeof CProxyMessage.
//
//  History:    17-Apr-97   emilyb     created
//
//--------------------------------------------------------------------------

RequestState CRequestServer::DoStartWatching(
    DWORD   cbRequest,
    DWORD & cbToWrite )
{
    if ( 0 == _pQuery )
        THROW( CException( STATUS_INVALID_PARAMETER ) );

    CPMStartWatchingIn & request = * (CPMStartWatchingIn *) _Buffer();

    _pQuery->StartWatching( request.GetCursor() );

    return stateContinue;
} //DoStartWatching

//+-------------------------------------------------------------------------
//
//  Member:     CRequestServer::DoStopWatching, private
//
//  Synopsis:   Handles a stop watch all behavior for rowset message.
//
//  Arguments:  [cbRequest] - Size of the request in _Buffer()
//              [cbToWrite] - On output, set to the # of bytes to write if
//                            other than sizeof CProxyMessage.
//
//  History:    17-Apr-97   emilyb     created
//
//--------------------------------------------------------------------------

RequestState CRequestServer::DoStopWatching(
    DWORD   cbRequest,
    DWORD & cbToWrite )
{
    if ( 0 == _pQuery )
        THROW( CException( STATUS_INVALID_PARAMETER ) );

    CPMStopWatchingIn & request = * (CPMStopWatchingIn *) _Buffer();

    _pQuery->StopWatching( request.GetCursor() );

    return stateContinue;
} //DoStopWatching

//+-------------------------------------------------------------------------
//
//  Member:     CRequestServer::DoRatioFinished, private
//
//  Synopsis:   Handles a ratio finished message.
//
//  Arguments:  [cbRequest] - Size of the request in _Buffer()
//              [cbToWrite] - On output, set to the # of bytes to write if
//                            other than sizeof CProxyMessage.
//
//  History:    16-Sep-96   dlee       Created.
//
//--------------------------------------------------------------------------

RequestState CRequestServer::DoRatioFinished(
    DWORD   cbRequest,
    DWORD & cbToWrite )
{
    if ( 0 == _pQuery )
        THROW( CException( STATUS_INVALID_PARAMETER ) );

    CPMRatioFinishedIn &request = * (CPMRatioFinishedIn *) _Buffer();
    CPMRatioFinishedOut &reply = * (CPMRatioFinishedOut *) _Buffer();

    DBCOUNTITEM den, num, rows;

    _pQuery->RatioFinished( request.GetCursor(),
                            den,
                            num,
                            rows,
                            reply.NewRows() );

    reply.Denominator() = (ULONG) den;
    reply.Numerator() = (ULONG) num;
    reply.RowCount() = (ULONG) rows;

    cbToWrite = sizeof reply;
    return stateContinue;
} //DoRatioFinished

//+-------------------------------------------------------------------------
//
//  Member:     CRequestServer::DoCompareBmk, private
//
//  Synopsis:   Handles a compare bookmark message.
//
//  Arguments:  [cbRequest] - Size of the request in _Buffer()
//              [cbToWrite] - On output, set to the # of bytes to write if
//                            other than sizeof CProxyMessage.
//
//  History:    16-Sep-96   dlee       Created.
//
//--------------------------------------------------------------------------

RequestState CRequestServer::DoCompareBmk(
    DWORD   cbRequest,
    DWORD & cbToWrite )
{
    if ( 0 == _pQuery )
        THROW( CException( STATUS_INVALID_PARAMETER ) );

    CPMCompareBmkIn &request = * (CPMCompareBmkIn *) _Buffer();
    CPMCompareBmkOut &reply = * (CPMCompareBmkOut *) _Buffer();

    _pQuery->Compare( request.GetCursor(),
                      request.GetChapter(),
                      request.GetBmkFirst(),
                      request.GetBmkSecond(),
                      reply.Comparison() );

    cbToWrite = sizeof reply;
    return stateContinue;
} //DoCompareBmk

//+-------------------------------------------------------------------------
//
//  Member:     CRequestServer::DoGetApproximatePosition, private
//
//  Synopsis:   Handles a get approximate position message.
//
//  Arguments:  [cbRequest] - Size of the request in _Buffer()
//              [cbToWrite] - On output, set to the # of bytes to write if
//                            other than sizeof CProxyMessage.
//
//  History:    16-Sep-96   dlee       Created.
//
//--------------------------------------------------------------------------

RequestState CRequestServer::DoGetApproximatePosition(
    DWORD   cbRequest,
    DWORD & cbToWrite )
{
    if ( 0 == _pQuery )
        THROW( CException( STATUS_INVALID_PARAMETER ) );

    CPMGetApproximatePositionIn &request = * (CPMGetApproximatePositionIn *)_Buffer();
    CPMGetApproximatePositionOut &reply = * (CPMGetApproximatePositionOut *)_Buffer();

    DBCOUNTITEM cNum,cDen;

    _pQuery->GetApproximatePosition( request.GetCursor(),
                                     request.GetChapter(),
                                     request.GetBmk(),
                                     & cNum,
                                     & cDen );

    reply.Numerator() = (ULONG) cNum;
    reply.Denominator() = (ULONG) cDen;

    cbToWrite = sizeof reply;
    return stateContinue;
} //DoGetApproximatePosition

//+-------------------------------------------------------------------------
//
//  Member:     CRequestServer::DoSetBindings, private
//
//  Synopsis:   Handles a set bindings message.
//
//  Arguments:  [cbRequest] - Size of the request in _Buffer()
//              [cbToWrite] - On output, set to the # of bytes to write if
//                            other than sizeof CProxyMessage.
//
//  History:    16-Sep-96   dlee       Created.
//              05-Oct-99   KLam       Handle Win32 client to Win64 server
//
//--------------------------------------------------------------------------

RequestState CRequestServer::DoSetBindings(
    DWORD   cbRequest,
    DWORD & cbToWrite )
{
    Win4Assert( 0 != _pQuery || IsBeingRemoved() );

    if ( 0 == _pQuery )
        THROW( CException( STATUS_INVALID_PARAMETER ) );

    // The request is either in the standard buffer or the temp buffer.
    // Take ownership of the temp buffer.

    CPMSetBindingsIn &request = * (CPMSetBindingsIn *) _ActiveBuffer();
    XPtrST<BYTE> xTemp( _xTempBuffer.Acquire() );

    Win4Assert( isQWordAligned( request.GetDescription() ) );

    request.ValidateCheckSum( GetClientVersion(), cbRequest );

    CMemDeSerStream stmDeser( request.GetDescription(),
                              request.GetBindingDescLength() );

    XPtr<CPidMapper> Pidmap( new CPidMapper() );

    XPtr<CTableColumnSet> Cols( new CTableColumnSet ( stmDeser,
                                                      Pidmap.GetReference() ) );

#ifdef _WIN64    

    // Create a new TableColumnSet with 64-bit sizes
    if ( !IsClient64() )
    {
        prxDebugOut(( DEB_ITRACE, "32bit client querying 64bit server, fixing columns!\n" ));

        //
        // The client gave us a 32-bit column description
        // Fix the column descriptions to work on Win64
        //
        FixColumns ( Cols.Acquire() );

        _cbRowWidth32 = request.GetRowLength();

        // SetBindings with the 64 bit table
        _pQuery->SetBindings( request.GetCursor(),
                              _cbRowWidth64,
                              _xCols64.GetReference(),
                              Pidmap.GetReference() );

        prxDebugOut(( DEB_ITRACE, "\tRow Length: Win32 %d Win64 %d\n", _cbRowWidth32, _cbRowWidth64 ));
        prxDebugOut(( DEB_ITRACE, "\t\t_xCols64 0x%I64x\n\t\t_xCols32 0x%I64x\n", 
                      _xCols64.GetPointer(), _xCols32.GetPointer() ));
    }
    else
    {
#endif

    _pQuery->SetBindings( request.GetCursor(),
                          request.GetRowLength(),
                          Cols.GetReference(),
                          Pidmap.GetReference() );

#ifdef _WIN64
    }
#endif

    return stateContinue;


} //DoSetBindings

#ifdef _WIN64
//+-------------------------------------------------------------------------
//
//  Member:     CRequestServer::FixColumns, private
//
//  Synopsis:   Adjusts a Win32 column description to a Win64 column description
//              and saves both descriptions
//
//  Arguments:  [pCols32]   - 32 bit table column set description
//
//  History:    12-Nov-99   KLam       Created.
//
//--------------------------------------------------------------------------

void CRequestServer::FixColumns ( CTableColumnSet * pCols32 )
{
    USHORT iEnd = 0;
    USHORT iAlignment;

    // Make sure these were freed from FreeQuery
    if ( !_xCols64.IsNull() && !_xCols32.IsNull() )
        THROW ( CException( STATUS_INVALID_PARAMETER ) );

    // Create a 64 bit column description
    _xCols64.Set( new CTableColumnSet ( pCols32->Count() ) );
    _xCols32.Set( pCols32 );

    for (unsigned iCol = 0; iCol < _xCols32->Count(); iCol++)
    {
        CTableColumn *pColumn = _xCols32->Get(iCol);

        VARTYPE vt = pColumn->GetStoredType();

        Win4Assert ( VT_VARIANT == vt|| VT_I4 == vt );

        XPtr<CTableColumn> xColumn64 (new CTableColumn( iCol ));

        if ( pColumn->IsValueStored() )
        {

            prxDebugOut(( DEB_ITRACE, 
                          "\n\tFound value type: %d width: %d offset: %d\n",
                          pColumn->GetStoredType(), pColumn->GetValueSize(), pColumn->GetValueOffset() ));

            if ( VT_VARIANT == vt )
            {
                iAlignment = (USHORT)AlignBlock ( iEnd, sizeof(LONGLONG) );
                iEnd = iAlignment + sizeof (PROPVARIANT);

                xColumn64->SetValueField( VT_VARIANT,
                                          iAlignment,
                                          sizeof (PROPVARIANT) );
            }
            else if ( VT_I4 == vt )
            {
                iAlignment = (USHORT)AlignBlock ( iEnd, sizeof(ULONG) );
                iEnd = iAlignment + sizeof (ULONG);

                xColumn64->SetValueField( VT_I4,
                                          iAlignment,
                                          sizeof (ULONG) );
            }

            prxDebugOut(( DEB_ITRACE, 
                          "\t  Replacing with width: %d offset: %d\n",
                          xColumn64->GetValueSize(), xColumn64->GetValueOffset() )); 
        }

        if ( pColumn->IsStatusStored() )
        {
            prxDebugOut(( DEB_ITRACE, 
                          "\tFound status width: %d offset: %d\n",
                          pColumn->GetStatusSize(), pColumn->GetStatusOffset() ));

            iAlignment = (USHORT)AlignBlock ( iEnd, sizeof (BYTE) );
            iEnd = iAlignment + sizeof (BYTE);

            xColumn64->SetStatusField( iAlignment, sizeof (BYTE) );

            prxDebugOut(( DEB_ITRACE, 
                          "\t  Replacing with width: %d offset: %d\n",
                          xColumn64->GetStatusSize(), xColumn64->GetStatusOffset() )); 
        }

        if ( pColumn->IsLengthStored() )
        {
            prxDebugOut(( DEB_ITRACE, 
                          "\tFound length width: %d offset: %d\n",
                          pColumn->GetLengthSize(), pColumn->GetLengthOffset() ));

            iAlignment = (USHORT)AlignBlock ( iEnd, sizeof (ULONG) );
            iEnd = iAlignment + sizeof (ULONG);

            xColumn64->SetLengthField( iAlignment, sizeof (ULONG) );

            prxDebugOut(( DEB_ITRACE, 
                          "\t  Replacing with width: %d offset: %d\n",
                          xColumn64->GetLengthSize(), xColumn64->GetLengthOffset() )); 
        }

        // Store the column in the column set
        _xCols64->Add( xColumn64.GetPointer(), iCol );
        xColumn64.Acquire();
    }

    _cbRowWidth64 = AlignBlock( iEnd, sizeof(ULONGLONG) );

} // FixColumns
#endif // _WIN64

//+-------------------------------------------------------------------------
//
//  Member:     CRequestServer::DoGetNotify, private
//
//  Synopsis:   Handles a get notify message.
//
//  Arguments:  [cbRequest] - Size of the request in _Buffer()
//              [cbToWrite] - On output, set to the # of bytes to write if
//                            other than sizeof CProxyMessage.
//
//  History:    16-Sep-96   dlee       Created.
//
//--------------------------------------------------------------------------

RequestState CRequestServer::DoGetNotify(
    DWORD   cbRequest,
    DWORD & cbToWrite )
{
    // _pQuery will be zero if the notify thread comes in after the last
    // FreeCursor.  This is a rare non-fatal condition and it is ok to
    // throw an error.

    if ( 0 == _pQuery )
        QUIETTHROW( CException( STATUS_INVALID_PARAMETER ) );

    CProxyMessage &msg = * (CProxyMessage *) _Buffer();
    Win4Assert( pmGetNotify == msg.GetMessage() );

    CNotificationSync sync( this );
    DBWATCHNOTIFY wn;
    SCODE sc = _pQuery->GetNotifications( sync, wn );

    // If STATUS_PENDING, just return the pmGetNotify and the client will
    // be expecting 0 or 1 pmSendNotify messages at some later time.
    // Otherwise, the notification is available and it is returned here.

    if ( STATUS_PENDING != sc )
    {
        Win4Assert( S_OK == sc ); // GetNotifications throws on failure

        CPMSendNotifyOut *pReply = new( _Buffer() ) CPMSendNotifyOut( wn );
        cbToWrite = sizeof CPMSendNotifyOut;
    }

    return stateContinue;
} //DoGetNotify

//+-------------------------------------------------------------------------
//
//  Member:     CRequestServer::DoSendNotify, private
//
//  Synopsis:   Handles a send notify message.
//
//  Arguments:  [cbRequest] - Size of the request in _Buffer()
//              [cbToWrite] - On output, set to the # of bytes to write if
//                            other than sizeof CProxyMessage.
//
//  History:    16-Sep-96   dlee       Created.
//
//--------------------------------------------------------------------------

RequestState CRequestServer::DoSendNotify(
    DWORD   cbRequest,
    DWORD & cbToWrite )
{
    Win4Assert( FALSE && !"pmSendNotify is server to client only!" );
    return stateDisconnect;
} //DoSendNotify

//+-------------------------------------------------------------------------
//
//  Member:     CRequestServer::DoSetWatchMode, private
//
//  Synopsis:   Handles a set watch mode message.
//
//  Arguments:  [cbRequest] - Size of the request in _Buffer()
//              [cbToWrite] - On output, set to the # of bytes to write if
//                            other than sizeof CProxyMessage.
//
//  History:    16-Sep-96   dlee       Created.
//
//--------------------------------------------------------------------------

RequestState CRequestServer::DoSetWatchMode(
    DWORD   cbRequest,
    DWORD & cbToWrite )
{
    if ( 0 == _pQuery )
        THROW( CException( STATUS_INVALID_PARAMETER ) );

    CPMSetWatchModeIn &request = * (CPMSetWatchModeIn *) _Buffer();

    HWATCHREGION hRegion = request.GetRegion();
    _pQuery->SetWatchMode( &hRegion, request.GetMode() );

    CPMSetWatchModeOut &reply = * (CPMSetWatchModeOut *) _Buffer();
    reply.Region() = hRegion;
    cbToWrite = sizeof reply;

    return stateContinue;
} //DoSetWatchMode

//+-------------------------------------------------------------------------
//
//  Member:     CRequestServer::DoGetWatchInfo, private
//
//  Synopsis:   Handles a get watch info message.
//
//  Arguments:  [cbRequest] - Size of the request in _Buffer()
//              [cbToWrite] - On output, set to the # of bytes to write if
//                            other than sizeof CProxyMessage.
//
//  History:    16-Sep-96   dlee       Created.
//
//--------------------------------------------------------------------------

RequestState CRequestServer::DoGetWatchInfo(
    DWORD   cbRequest,
    DWORD & cbToWrite )
{
    if ( 0 == _pQuery )
        THROW( CException( STATUS_INVALID_PARAMETER ) );

    CPMGetWatchInfoIn &request = * (CPMGetWatchInfoIn *) _Buffer();
    CPMGetWatchInfoOut &reply = * (CPMGetWatchInfoOut *) _Buffer();

    DBCOUNTITEM cRows;

    _pQuery->GetWatchInfo( request.GetRegion(),
                           & reply.Mode(),
                           & reply.Chapter(),
                           & reply.Bookmark(),
                           & cRows );

    reply.RowCount() = (ULONG) cRows;

    cbToWrite = sizeof reply;
    return stateContinue;
} //DoGetWatchInfo

//+-------------------------------------------------------------------------
//
//  Member:     CRequestServer::DoShrinkWatchRegion, private
//
//  Synopsis:   Handles a shrink watch region message.
//
//  Arguments:  [cbRequest] - Size of the request in _Buffer()
//              [cbToWrite] - On output, set to the # of bytes to write if
//                            other than sizeof CProxyMessage.
//
//  History:    16-Sep-96   dlee       Created.
//
//--------------------------------------------------------------------------

RequestState CRequestServer::DoShrinkWatchRegion(
    DWORD   cbRequest,
    DWORD & cbToWrite )
{
    if ( 0 == _pQuery )
        THROW( CException( STATUS_INVALID_PARAMETER ) );

    CPMShrinkWatchRegionIn &request = * (CPMShrinkWatchRegionIn *) _Buffer();

    _pQuery->ShrinkWatchRegion( request.GetRegion(),
                                request.GetChapter(),
                                request.GetBookmark(),
                                request.GetRowCount() );

    return stateContinue;
} //DoShrinkWatchRegion

//+-------------------------------------------------------------------------
//
//  Member:     CRequestServer::DoRefresh, private
//
//  Synopsis:   Handles a refresh message.
//
//  Arguments:  [cbRequest] - Size of the request in _Buffer()
//              [cbToWrite] - On output, set to the # of bytes to write if
//                            other than sizeof CProxyMessage.
//
//  History:    16-Sep-96   dlee       Created.
//
//--------------------------------------------------------------------------

RequestState CRequestServer::DoRefresh(
    DWORD   cbRequest,
    DWORD & cbToWrite )
{
    if ( 0 == _pQuery )
        THROW( CException( STATUS_INVALID_PARAMETER ) );

    _pQuery->Refresh();

    return stateContinue;
} //DoRefresh

//+-------------------------------------------------------------------------
//
//  Member:     CRequestServer::DoGetQueryStatus, private
//
//  Synopsis:   Handles a get query status message.
//
//  Arguments:  [cbRequest] - Size of the request in _Buffer()
//              [cbToWrite] - On output, set to the # of bytes to write if
//                            other than sizeof CProxyMessage.
//
//  History:    16-Sep-96   dlee       Created.
//
//--------------------------------------------------------------------------

RequestState CRequestServer::DoGetQueryStatus(
    DWORD   cbRequest,
    DWORD & cbToWrite )
{
    if ( 0 == _pQuery )
        THROW( CException( STATUS_INVALID_PARAMETER ) );

    CPMGetQueryStatusIn &request = * (CPMGetQueryStatusIn *) _Buffer();
    CPMGetQueryStatusOut &reply = * (CPMGetQueryStatusOut *) _Buffer();

    _pQuery->GetQueryStatus( request.GetCursor(),
                             reply.QueryStatus() );

    cbToWrite = sizeof reply;
    return stateContinue;
} //DoGetQueryStatus

//+-------------------------------------------------------------------------
//
//  Member:     CRequestServer::DoGetQueryStatusEx, private
//
//  Synopsis:   Handles a get query status message.
//
//  Arguments:  [cbRequest] - Size of the request in _Buffer()
//              [cbToWrite] - On output, set to the # of bytes to write if
//                            other than sizeof CProxyMessage.
//
//  History:    16-Sep-96   dlee       Created.
//
//--------------------------------------------------------------------------

RequestState CRequestServer::DoGetQueryStatusEx(
    DWORD   cbRequest,
    DWORD & cbToWrite )
{
    if ( 0 == _pQuery )
        THROW( CException( STATUS_INVALID_PARAMETER ) );

    CPMGetQueryStatusExIn &request = * (CPMGetQueryStatusExIn *) _Buffer();
    CPMGetQueryStatusExOut &reply = * (CPMGetQueryStatusExOut *) _Buffer();

    DBCOUNTITEM den, num, iBmk, cRows;

    _pQuery->GetQueryStatusEx( request.GetCursor(),
                               reply.QueryStatus(),
                               reply.FilteredDocuments(),
                               reply.DocumentsToFilter(),
                               den,
                               num,
                               request.GetBookmark(),
                               iBmk,
                               cRows );

    reply.RatioFinishedDenominator() = (ULONG) den;
    reply.RatioFinishedNumerator() = (ULONG) num;
    reply.RowBmk() = (ULONG) iBmk;
    reply.RowsTotal() = (ULONG) cRows;

    cbToWrite = sizeof reply;
    return stateContinue;
} //DoGetQueryStatusEx

//+-------------------------------------------------------------------------
//
//  Member:     CRequestServer::DoCiState, private
//
//  Synopsis:   Handles a ci state message.
//
//  Arguments:  [cbRequest] - Size of the request in _Buffer()
//              [cbToWrite] - On output, set to the # of bytes to write if
//                            other than sizeof CProxyMessage.
//
//  History:    16-Sep-96   dlee       Created.
//
//--------------------------------------------------------------------------

RequestState CRequestServer::DoCiState(
    DWORD   cbRequest,
    DWORD & cbToWrite )
{
    CPMCiStateInOut &request = * (CPMCiStateInOut *) _Buffer();

    if ( _xDocStore.IsNull() )
        THROW( CException( STATUS_INVALID_PARAMETER ) );

    XInterface<IFsCiAdmin> xFsCiAdmin;
    SCODE sc = _xDocStore->QueryInterface( IID_IFsCiAdmin,
                                           xFsCiAdmin.GetQIPointer() );

    if ( S_OK == sc )
    {
        sc = xFsCiAdmin->CiState( &(request.GetState()) );
        request.SetStatus( sc );
    }

    cbToWrite = sizeof CProxyMessage + request.GetState().cbStruct;
    return stateContinue;
} //DoCiState

//+-------------------------------------------------------------------------
//
//  Member:     CRequestServer::DoObsolete, private
//
//  Synopsis:   Handles an obsolete message
//
//  Arguments:  [cbRequest] - Size of the request in _Buffer()
//              [cbToWrite] - On output, set to the # of bytes to write if
//                            other than sizeof CProxyMessage.
//
//  History:    16-Sep-96   dlee       Created.
//
//--------------------------------------------------------------------------

RequestState CRequestServer::DoObsolete(
    DWORD   cbRequest,
    DWORD & cbToWrite )
{
    // This request is obsolete

    THROW( CException( STATUS_INVALID_PARAMETER ) );
    return stateDisconnect;
} //DoObsolete

//+-------------------------------------------------------------------------
//
//  Member:     CRequestServer::DoForceMerge, private
//
//  Synopsis:   Handles a force merge message.
//
//  Arguments:  [cbRequest] - Size of the request in _Buffer()
//              [cbToWrite] - On output, set to the # of bytes to write if
//                            other than sizeof CProxyMessage.
//
//  History:    16-Sep-96   dlee       Created.
//
//--------------------------------------------------------------------------

RequestState CRequestServer::DoForceMerge(
    DWORD   cbRequest,
    DWORD & cbToWrite )
{
    VerifyThreadHasAdminPrivilege();

    if ( _xDocStore.IsNull() )
        THROW( CException( STATUS_INVALID_PARAMETER ) );

    CPMForceMergeIn &request = * (CPMForceMergeIn *) _Buffer();

    XInterface<IFsCiAdmin> xFsCiAdmin;
    SCODE sc = _xDocStore->QueryInterface( IID_IFsCiAdmin,
                                           xFsCiAdmin.GetQIPointer() );

    if ( SUCCEEDED(sc) )
        sc = xFsCiAdmin->ForceMerge( request.GetPartID() );

    if ( !SUCCEEDED(sc) )
        THROW( CException(sc) );

    return stateContinue;
} //DoForceMerge

//+-------------------------------------------------------------------------
//
//  Member:     CRequestServer::DoAbortMerge, private
//
//  Synopsis:   Handles an abort merge message.
//
//  Arguments:  [cbRequest] - Size of the request in _Buffer()
//              [cbToWrite] - On output, set to the # of bytes to write if
//                            other than sizeof CProxyMessage.
//
//  History:    16-Sep-96   dlee       Created.
//
//--------------------------------------------------------------------------

RequestState CRequestServer::DoAbortMerge(
    DWORD   cbRequest,
    DWORD & cbToWrite )
{
    VerifyThreadHasAdminPrivilege();

    if ( _xDocStore.IsNull() )
        THROW( CException( STATUS_INVALID_PARAMETER ) );

    CPMAbortMergeIn &request = * (CPMAbortMergeIn *) _Buffer();

    XInterface<IFsCiAdmin> xFsCiAdmin;
    SCODE sc = _xDocStore->QueryInterface( IID_IFsCiAdmin,
                                           xFsCiAdmin.GetQIPointer() );

    if ( SUCCEEDED(sc) )
        sc = xFsCiAdmin->AbortMerge( request.GetPartID() );

    if ( !SUCCEEDED(sc) )
        THROW( CException(sc) );

    return stateContinue;
} //DoAbortMerge

//+-------------------------------------------------------------------------
//
//  Member:     CRequestServer::DoSetCatState, private
//
//  Synopsis:   Handles a SetCiCatState message.
//
//  Arguments:  [cbRequest] - Size of the request in _Buffer()
//              [cbToWrite] - On output, set to the # of bytes to write if
//                            other than sizeof CProxyMessage.
//
//  History:    14-Apr-98   kitmanh       Created.
//
//--------------------------------------------------------------------------

RequestState CRequestServer::DoSetCatState(
    DWORD   cbRequest,
    DWORD & cbToWrite )
{
    ciDebugOut(( DEB_ITRACE, "DoSetCatState is called\n" ));
    VerifyThreadHasAdminPrivilege();

    CPMSetCatStateIn &request = * (CPMSetCatStateIn *) _Buffer();
    CPMSetCatStateOut &reply = * (CPMSetCatStateOut *) _Buffer();

    XInterface<ICiCDocStoreLocator> xLocator( _requestQueue.DocStoreLocator() );
    Win4Assert( !xLocator.IsNull() );

    BOOL fAbsUnWritable = FALSE; //temp value
    DWORD dwOldState;

    // hack to check if all catalogs are up, catalog name is ignored
    if ( CICAT_ALL_OPENED == request.GetNewState() )
    {
        reply.GetOldState() = _requestQueue.AreDocStoresOpen(); // make a constant
        cbToWrite = sizeof reply;
        return stateContinue;
    }
    // end of hack

    ICiCDocStore * pDocStore = 0;

    // Make sure we have a valid catalog name

    WCHAR *szCatalog = request.GetCatName();

    if ( 0 == szCatalog )
        THROW( CException( STATUS_INVALID_PARAMETER ) );

    unsigned cwc = wcslen( szCatalog );

    if ( ( 0 == cwc) || ( cwc >= ( MAX_PATH-1 ) ) )
        THROW( CException( STATUS_INVALID_PARAMETER ) );

    // check old state of docstore

    SCODE sc = xLocator->GetDocStoreState( szCatalog,
                                           &pDocStore,
                                           &dwOldState );
    reply.GetOldState() = dwOldState;

    if ( SUCCEEDED(sc) )
    {
        _xDocStore.Set( pDocStore );

        if ( 0 != pDocStore )
        {
            // This QI is just about guaranteed to work.

            XInterface<ICiCAdviseStatus> xAdviseStatus;

            sc = pDocStore->QueryInterface( IID_ICiCAdviseStatus,
                                            xAdviseStatus.GetQIPointer() );
            if ( S_OK != sc )
                THROW( CException(sc) );

            xAdviseStatus->IncrementPerfCounterValue( CI_PERF_RUNNING_QUERIES );
        }

        //Push an item onto the _stateChangeArray for the (make this a function name DoDisConnect

        SCWorkItem newItem;

        ciDebugOut(( DEB_ITRACE, "request.GetNewState == %d\n", request.GetNewState() ));

        switch ( request.GetNewState() )
        {
            case CICAT_GET_STATE:
                break;
            case CICAT_STOPPED:
                if ( 0 == (CICAT_STOPPED & dwOldState) )
                {
                    newItem.type = eStopCat;
                    newItem.pDocStore = _xDocStore.GetPointer();
                    _requestQueue.AddSCItem( &newItem , 0 );
                    xLocator->AddStoppedCat( dwOldState, request.GetCatName() );
                    CLock lock( g_mtxStartStop );
                    sc = StopFWCiSvcWork( eStopCat );
                }

                break;

            case CICAT_READONLY:
                ciDebugOut(( DEB_ITRACE, "New state is CICAT_READONLY\n" ));
                if ( 0 == (CICAT_READONLY & dwOldState) )
                {
                    newItem.type = eCatRO;
                    newItem.pDocStore = _xDocStore.GetPointer();

                    if ( 0 != (CICAT_STOPPED & dwOldState) )
                    {
                        ciDebugOut(( DEB_ITRACE, "DoSetCatState, restarting a stopped cat\n" ));
                        ciDebugOut(( DEB_ITRACE, "DoSetCatState.. newItem.StoppedCat is %ws\n", request.GetCatName() ));
                        _requestQueue.AddSCItem( &newItem, request.GetCatName() );
                    }
                    else
                        _requestQueue.AddSCItem( &newItem , 0 );

                    CLock lock( g_mtxStartStop );
                    sc = StopFWCiSvcWork( eCatRO );
                }

                ciDebugOut(( DEB_ITRACE, "Done setting CICAT_READONLY\n" ));
                break;

            case CICAT_WRITABLE:

                if ( 0 == (CICAT_WRITABLE & dwOldState) )
                {
                    sc = xLocator->IsMarkedReadOnly( request.GetCatName(), &fAbsUnWritable );
                    if ( FAILED(sc) )
                        THROW( CException(sc) );

                    ciDebugOut(( DEB_ITRACE, "DoSetCatState.. IsMarkedReadOnly is %d\n", fAbsUnWritable ));

                    if ( CICAT_STOPPED == dwOldState )
                    {
                        if ( !fAbsUnWritable )
                        {
                            sc = xLocator->IsVolumeOrDirRO( request.GetCatName(), &fAbsUnWritable );
                            if ( FAILED(sc) )
                                THROW( CException(sc) );
                        }

                        ciDebugOut(( DEB_ITRACE, "DoSetCatState.. fAbsUnWritable is %d\n", fAbsUnWritable ));
                    }

                    if ( fAbsUnWritable )
                    {
                        //Catalog cannot be open for r/w, open as r/o instead
                        //can't throw, must finish work
                        newItem.type = eCatRO;
                    }
                    else
                        newItem.type = eCatW;

                    newItem.pDocStore = _xDocStore.GetPointer();
                    if ( CICAT_STOPPED == dwOldState )
                    {
                        ciDebugOut(( DEB_ITRACE, "DoSetCatState, restarting a stopped cat\n" ));
                        ciDebugOut(( DEB_ITRACE, "DoSetCatState.. newItem.StoppedCat is %ws\n", request.GetCatName() ));
                        _requestQueue.AddSCItem( &newItem, request.GetCatName() );
                    }
                    else
                        _requestQueue.AddSCItem( &newItem, 0 );

                    CLock lock( g_mtxStartStop );
                    sc = StopFWCiSvcWork( newItem.type );
                }

                break;

            case CICAT_NO_QUERY:
                if ( 0 == (CICAT_NO_QUERY & dwOldState) )
                {
                    newItem.type = eNoQuery;

                    sc = xLocator->IsMarkedReadOnly( request.GetCatName(), &fAbsUnWritable );
                    if ( FAILED(sc) )
                        THROW( CException(sc) );

                    ciDebugOut(( DEB_ITRACE, "DoSetCatState.. IsMarkedReadOnly is %d\n", fAbsUnWritable ));

                    if ( 0 != (CICAT_STOPPED & dwOldState) )
                    {
                        if ( !fAbsUnWritable )
                        {
                            sc = xLocator->IsVolumeOrDirRO( request.GetCatName(), &fAbsUnWritable );
                            if ( FAILED(sc) )
                                THROW( CException(sc) );
                        }
                        ciDebugOut(( DEB_ITRACE, "DoSetCatState(NoQuery).. fAbsUnWritable is %d\n", fAbsUnWritable ));
                    }

                    if ( fAbsUnWritable )
                    {
                        //The catalog is absolutely unwritable, opening for ReadOnly instead
                        //can't throw here, since work needs to be done.
                        newItem.fNoQueryRW = FALSE;
                    }
                    else
                        newItem.fNoQueryRW = TRUE;

                    newItem.pDocStore = _xDocStore.GetPointer();
                    if ( 0 != (CICAT_STOPPED & dwOldState) )
                        _requestQueue.AddSCItem( &newItem, request.GetCatName() );
                    else
                        _requestQueue.AddSCItem( &newItem, 0 );

                    CLock lock( g_mtxStartStop );
                    sc = StopFWCiSvcWork( newItem.type );
                }
                break;

            default:
                THROW( CException( STATUS_INVALID_PARAMETER ) );
        }
    }

    ciDebugOut(( DEB_ITRACE, "DoSetCatState sc is %d or %#x\n", sc ));

    if ( !SUCCEEDED(sc) )
        THROW( CException(sc) );

    cbToWrite = sizeof reply;
    return stateContinue;
} //DoSetCatState


//+-------------------------------------------------------------------------
//
//  Member:     CRequestServer::DoFetchValue, private
//
//  Synopsis:   Retrieves a value from the property cache
//
//  Arguments:  [cbRequest] - Size of the request in _Buffer()
//              [cbToWrite] - On output, set to the # of bytes to write if
//                            other than sizeof CProxyMessage.
//
//  History:    16-Sep-96   dlee       Created.
//
//--------------------------------------------------------------------------

RequestState CRequestServer::DoFetchValue(
    DWORD   cbRequest,
    DWORD & cbToWrite )
{
    if ( 0 == _pQuery )
        THROW( CException( STATUS_INVALID_PARAMETER ) );

    CPMFetchValueIn &request = * (CPMFetchValueIn *) _Buffer();
    request.ValidateCheckSum( GetClientVersion(), cbRequest );

    WORKID wid = request.GetWID();
    DWORD cbSoFar = request.GetSoFar();
    DWORD cbPropSpec = request.GetPSSize();
    DWORD cbChunk = request.GetChunkSize();

    // if this is the first request for the value, fetch the value

    if ( 0 == cbSoFar )
    {
        _xFetchedValue.Free();
        _cbFetchedValueSoFar = 0;

        Win4Assert( isQWordAligned( request.GetPS() ) );
        CMemDeSerStream stmDeser( request.GetPS(), cbPropSpec );
        CFullPropSpec ps( stmDeser );
        if ( !ps.IsValid() )
            THROW( CException( E_OUTOFMEMORY ) );

        PROPVARIANT var;
        if ( !_pQuery->FetchDeferredValue( wid, ps, var ) || var.vt == VT_EMPTY )
        {
            //
            // FetchDeferredValue does a security check to make sure the client has access
            // to this wid.  A hacker could pass in a wid not returned in a query.
            //
            CPMFetchValueOut &reply = * (CPMFetchValueOut *) _Buffer();
            reply.ValueExists() = FALSE;
            cbToWrite = sizeof reply;
            return stateContinue;
        }

        // marshall the property value and save it in _xFetchedValue

        SPropVariant xvar( &var );
        ULONG cb = 0;
        StgConvertVariantToProperty( &var,
                                     CP_WINUNICODE,
                                     0,
                                     &cb,
                                     pidInvalid,
                                     FALSE,
                                     0 );
        _xFetchedValue.Init( cb );
        StgConvertVariantToProperty( &var,
                                     CP_WINUNICODE,
                                     (SERIALIZEDPROPERTYVALUE *)_xFetchedValue.Get(),
                                     &cb,
                                     pidInvalid,
                                     FALSE,
                                     0 );
    }

    // send a chunk (or all) of the marshalled property value

    Win4Assert( cbSoFar == _cbFetchedValueSoFar );
    Win4Assert( 0 != _xFetchedValue.Get() );

    DWORD cbToGo = _xFetchedValue.SizeOf() - _cbFetchedValueSoFar;
    Win4Assert( sizeof CPMFetchValueOut < cbChunk );
    DWORD cbValToWrite = __min( cbChunk - sizeof CPMFetchValueOut, cbToGo );
    cbToWrite = sizeof CPMFetchValueOut + cbValToWrite;

    CPMFetchValueOut *pReply;
    if ( cbToWrite <= _BufferSize() )
    {
        pReply = (CPMFetchValueOut *) _Buffer();
    }
    else
    {
        _xTempBuffer.Init( cbToWrite );
        pReply = new( _xTempBuffer.Get() ) CPMFetchValueOut;
    }

    pReply->ValueExists() = TRUE;
    pReply->ValueSize() = cbValToWrite;
    pReply->MoreExists() = ( cbValToWrite != cbToGo );
    RtlCopyMemory( pReply->Value(),
                   _xFetchedValue.Get() + _cbFetchedValueSoFar,
                   cbValToWrite );
    _cbFetchedValueSoFar += cbValToWrite;

    if ( !pReply->MoreExists() )
    {
        _xFetchedValue.Free();
        _cbFetchedValueSoFar = 0;
    }

    return stateContinue;
} //DoFetchValue

//+-------------------------------------------------------------------------
//
//  Member:     CRequestServer::DoWorkIdToPath, private
//
//  Synopsis:   Converts a wid to a path.
//
//  Arguments:  [cbRequest] - Size of the request in _Buffer()
//              [cbToWrite] - On output, set to the # of bytes to write if
//                            other than sizeof CProxyMessage.
//
//  History:    16-Sep-96   dlee       Created.
//
//--------------------------------------------------------------------------

RequestState CRequestServer::DoWorkIdToPath(
    DWORD   cbRequest,
    DWORD & cbToWrite )
{
    if ( 0 == _pQuery )
        THROW( CException( STATUS_INVALID_PARAMETER ) );

    CPMWorkIdToPathIn &request = * (CPMWorkIdToPathIn *) _Buffer();
    CFunnyPath funnyPath;

    _pQuery->WorkIdToPath( request.GetWorkId(), funnyPath );

    CPMWorkIdToPathOut &reply = * (CPMWorkIdToPathOut *) _Buffer();

    cbToWrite = sizeof reply;
    if ( 0 != funnyPath.GetActualLength() )
    {
        reply.Any() = TRUE;
        ULONG cbPath = (funnyPath.GetActualLength()+1) * sizeof(WCHAR);
        RtlCopyMemory( reply.Path(), funnyPath.GetActualPath(), cbPath );
        cbToWrite += cbPath;
    }
    else
    {
        reply.Any() = FALSE;
    }

    return stateContinue;
} //DoWorkIdToPath

//+-------------------------------------------------------------------------
//
//  Member:     CRequestServer::DoUpdateDocuments, private
//
//  Synopsis:   Handles an update documents message.
//
//  Arguments:  [cbRequest] - Size of the request in _Buffer()
//              [cbToWrite] - On output, set to the # of bytes to write if
//                            other than sizeof CProxyMessage.
//
//  History:    16-Sep-96   dlee       Created.
//
//--------------------------------------------------------------------------

RequestState CRequestServer::DoUpdateDocuments(
    DWORD   cbRequest,
    DWORD & cbToWrite )
{
    VerifyThreadHasAdminPrivilege();

    if ( _xDocStore.IsNull() )
        THROW( CException( STATUS_INVALID_PARAMETER ) );

    CPMUpdateDocumentsIn &request = * (CPMUpdateDocumentsIn *) _Buffer();

    XInterface<IFsCiAdmin> xFsCiAdmin;
    SCODE sc = _xDocStore->QueryInterface( IID_IFsCiAdmin,
                                           xFsCiAdmin.GetQIPointer() );

    if ( wcslen( request.GetRootPath() ) >= MAX_PATH )
        THROW( CException( STATUS_INVALID_PARAMETER ) );

    if ( SUCCEEDED(sc) )
        sc = xFsCiAdmin->UpdateDocuments( request.GetRootPath(),
                                          request.GetFlag() );

    if ( !SUCCEEDED(sc) )
        THROW( CException(sc) );

    return stateContinue;
} //DoUpdateDocuments

//+-------------------------------------------------------------------------
//
//  Member:     CRequestServer::HandleRequestNoThrow, private
//
//  Synopsis:   Processes a request.  This method can't throw.  If there
//              is any problem with the request, set the status code
//              in the message to reflect the problem.
//
//  Arguments:  [cbRequest] - Size of the request in _Buffer()
//              [cbToWrite] - On output, set to the # of bytes to write if
//                            other than sizeof CProxyMessage.
//
//  Returns:    FALSE if the connection with the client should be terminated,
//              TRUE otherwise.
//
//  History:    16-Sep-96   dlee       Created.
//
//--------------------------------------------------------------------------

RequestState CRequestServer::HandleRequestNoThrow(
    DWORD   cbRequest,
    DWORD & cbToWrite )
{
    // buffers must be 8-byte aligned on both sides of the proxy or the
    // marshalling/unmarshalling will be inconsistent.

    Win4Assert( isQWordAligned( _Buffer() ) );

    CProxyMessage &msg = * (CProxyMessage *) _Buffer();

    // Require that messages sent from clients have status set to S_OK,
    // as the reply message is in the same buffer and there is no need
    // to re-write the S_OK.

    Win4Assert( S_OK == msg.GetStatus() );

    RequestState state = stateContinue;

    TRY
    {
        Win4Assert( _xTempBuffer.SizeOf() > _BufferSize() ||
                    _xTempBuffer.IsNull() );

        int iMsg = msg.GetMessage() - pmConnect;

        if ( ( iMsg < 0 ) || ( iMsg >= cProxyMessages ) )
            THROW( CException( STATUS_INVALID_PARAMETER ) );

        // Don't impersonate when not necessary -- it's slow

        XPipeImpersonation impersonate;

        if ( _afImpersonate[ iMsg ] )
            impersonate.Impersonate( GetPipe() );

        SetLastTouchedTime( GetTickCount() );

        state = ( this->*( _aMsgFunctions[ iMsg ] ) )( cbRequest,
                                                       cbToWrite );

        prxDebugOut(( DEB_ITRACE,
                      "finish msg %d, cb %d, sc 0x%x, to pipe 0x%x\n",
                      msg.GetMessage(),
                      cbToWrite,
                      msg.GetStatus(),
                      GetPipe() ));
    }
    CATCH( CException, ex )
    {
        prxDebugOut(( DEB_ITRACE,
                      "HandleRequestNoThrow rs 0x%x msg %d caught 0x%x\n",
                      this,
                      msg.GetMessage(),
                      ex.GetErrorCode() ));

        msg.SetStatus( ex.GetErrorCode() );
        cbToWrite = sizeof CProxyMessage;

        _xTempBuffer.Free();
    }
    END_CATCH;

    return state;
} //HandleRequestNoThrow

//+-------------------------------------------------------------------------
//
//  Member:     CRequestServer::DoAPC, private
//
//  Synopsis:   This method is called when an i/o operation is completed,
//              whether successfully or not.  If there was an error,
//              this method terminates the connection.
//
//  Arguments:  [dwError]       - Win32 error code for i/o operation
//              [cbTransferred] - # of bytes read or written
//
//  History:    16-Sep-96   dlee       Created.
//
//--------------------------------------------------------------------------

void CRequestServer::DoAPC(
    DWORD dwError,
    DWORD cbTransferred )
{
    prxDebugOut(( DEB_ITRACE, "apc %s t 0x%x p 0x%x msg %d cb %d err %d\n",
                  pipeStateRead == _state ? "read" :
                      pipeStateWrite == _state ? "write" : "none",
                  GetCurrentThreadId(),
                  GetPipe(),
                  * (int *) _Buffer(),
                  cbTransferred,
                  dwError ));

    // Ownership of the item is with the apc:  either issue another i/o
    // or recycle the request server.

    TRY
    {
        // if the apc operation completed with an error, abort the connection

        if ( 0 != dwError )
            QUIETTHROW( CException( HRESULT_FROM_WIN32( dwError ) ) );

        if ( IsBeingRemoved() )
            THROW( CException( STATUS_TOO_LATE ) );

        if ( pipeStateRead == _state )
        {
            // if the entire message didn't fit in the buffer (eg, there was
            // a large restriction) read the rest of the message.

            if ( _BufferSize() == cbTransferred )
            {
                prxDebugOut(( DEB_ITRACE, "more data available\n" ));
                BYTE *p = ReadRemainingSync( _Buffer(), cbTransferred );

                //
                // ReadRemainingSync returns 0 if there was an exact fit
                // into _Buffer, so there was nothing left to read.
                //

                if ( 0 != p )
                {
                    _xTempBuffer.Set( cbTransferred, p );
                    prxDebugOut(( DEB_ITRACE, "total msg cb: %d\n",
                                  cbTransferred ));

                    // Only certain msgs expect the input buffer to be in
                    // _xTempBuffer.  Fail if it's not one of these msgs.

                    int pm = * (int *)  _xTempBuffer.Get();
                    Win4Assert( ( pmCreateQuery == pm ) ||
                                ( pmConnect     == pm ) ||
                                ( pmSetBindings == pm ) );

                    if ( pmCreateQuery != pm &&
                         pmConnect     != pm &&
                         pmSetBindings != pm )
                        THROW( CException( STATUS_INVALID_PARAMETER ) );
                }
            }

            #if CIDBG == 1
                prxDebugOut(( DEB_PRX_MSGS,
                              "proxy read %d bytes at %p\n",
                              cbTransferred, _ActiveBuffer() ));
            #endif // CIDBG == 1

            #if CI_PIPE_TESTING
            
                if ( 0 != _requestQueue._pTraceRead )
                    (*_requestQueue._pTraceRead)( GetPipe(),
                                                  cbTransferred,
                                                  _ActiveBuffer() );
            
            #endif // CI_PIPE_TESTING
            
            // The read completed, process the request

            DWORD cbToWrite = sizeof CProxyMessage;
            RequestState state = HandleRequestNoThrow( cbTransferred, cbToWrite );

            if ( stateContinue == state )
            {
                Win4Assert( cbToWrite >= sizeof CProxyMessage );
                Win4Assert( ( cbToWrite <= _BufferSize() ) ||
                            ( !_xTempBuffer.IsNull() ) );
                Win4Assert( ( cbToWrite > _BufferSize() ) ||
                            ( _xTempBuffer.IsNull() ) );
                Win4Assert( ( _xTempBuffer.IsNull() ) ||
                            ( cbToWrite == _xTempBuffer.SizeOf() ) );

                _state = pipeStateWrite;

                #if CIDBG == 1
                    prxDebugOut(( DEB_PRX_MSGS,
                                  "proxy writing %d bytes at %p\n",
                                  cbToWrite, _ActiveBuffer() ));
                #endif // CIDBG == 1

                void * pvToWrite = _ActiveBuffer();

                #if CI_PIPE_TESTING
                
                    void * pvToWriteOrg = pvToWrite;
                    DWORD cbToWriteOrg = cbToWrite;

                    if ( 0 != _requestQueue._pTraceBefore )
                        (*_requestQueue._pTraceBefore)( GetPipe(),
                                                        cbToWriteOrg,
                                                        pvToWriteOrg,
                                                        cbToWrite,
                                                        pvToWrite );

                
                #endif // CI_PIPE_TESTING

                Write( pvToWrite, cbToWrite, APCRoutine );

                #if CI_PIPE_TESTING
                
                    if ( 0 != _requestQueue._pTraceAfter )
                        (*_requestQueue._pTraceAfter)( GetPipe(),
                                                       cbToWriteOrg,
                                                       pvToWriteOrg,
                                                       cbToWrite,
                                                       pvToWrite );
                
                #endif // CI_PIPE_TESTING
                
                if ( IsBeingRemoved() )
                    CancelIO();
            }
            else if ( stateDisconnect == state )
            {
                Win4Assert( _xTempBuffer.IsNull() );
                _state = pipeStateNone;
                _requestQueue.RecycleRequestServerNoThrow( this );
            }
            else
            {
                Win4Assert( statePending == state );
            }
        }
        else
        {
            Win4Assert( pipeStateWrite == _state );

            // cleanup temp buffer if allocated for the write

            _xTempBuffer.Free();

            // the write completed, so schedule another read

            _state = pipeStateRead;
            Read( _Buffer(), _BufferSize(), APCRoutine );

            if ( IsBeingRemoved() )
                CancelIO();
        }
    }
    CATCH( CException, ex )
    {
        prxDebugOut(( DEB_WARN,
                      "exception in APC error %#x, this %#x, pipe %#x\n",
                      ex.GetErrorCode(),
                      this,
                      GetPipe() ));

        // disconnect the pipe (and the query if it exists)

        _state = pipeStateNone;
        _requestQueue.RecycleRequestServerNoThrow( this );
    }
    END_CATCH;
} //DoAPC

//+-------------------------------------------------------------------------
//
//  Member:     CRequestServer::APCRoutine, private, static
//
//  Synopsis:   This method is called when an i/o operation is completed.
//
//  Arguments:  [dwError]       - Win32 error code for i/o operation
//              [cbTransferred] - # of bytes read or written
//              [pOverlapped]   - Points to the overlapped for the i/o
//
//  History:    16-Sep-96   dlee       Created.
//
//--------------------------------------------------------------------------

void WINAPI CRequestServer::APCRoutine(
    DWORD        dwError,
    DWORD        cbTransferred,
    LPOVERLAPPED pOverlapped )
{

    // It would be a programming error to take an exception here, and it
    // has never been hit.  Leave the check in for checked builds.

#if CIDBG == 1
    TRY
    {
#endif

        // The request server was saved in the hEvent field, which the
        // Win32 doc says is a good place for a user's APC data

        CRequestServer &serv = * (CRequestServer *) (CPipeServer *)
                               ( pOverlapped->hEvent );
        serv.DoAPC( dwError, cbTransferred );

#if CIDBG == 1
    }
    CATCH( CException, e )
    {
        prxDebugOut(( DEB_ERROR, "exception in APCRoutine: 0x%x\n",
                      e.GetErrorCode() ));
        Win4Assert( !"caught an unexpected exception in an apc" );
    }
    END_CATCH;
#endif
} //APCRoutine

//+-------------------------------------------------------------------------
//
//  Member:     CRequestServer::CancelAPCRoutine, private/static
//
//  Synopsis:   Called in an APC when a connection should be cancelled
//
//  Arguments:  [dwParam] - The request server
//
//  History:    16-Sep-96   dlee       Created.
//
//--------------------------------------------------------------------------

void WINAPI CRequestServer::CancelAPCRoutine(
    DWORD_PTR dwParam )
{
    TRY
    {
        CRequestServer * pServer = (CRequestServer *) dwParam;
        XInterface<CRequestServer> xServer( pServer );

        prxDebugOut(( DEB_ITRACE,
                      "Canceling server 0x%x io pipe 0x%x\n",
                      pServer, pServer->GetPipe() ));

        // Freeing a pending query will result in the request server
        // being cleaned up, since _fShutdown/BeingRemoved is TRUE.
        // Cancelling pending IO will cause the IO APC routine to be
        // called with an error completion status, which will clean up
        // the request server.

        if ( pipeStatePending == pServer->_state )
            pServer->FreeQuery();
        else if ( pipeStateNone != pServer->_state )
            pServer->CancelIO();
    }
    CATCH( CException, e )
    {
        prxDebugOut(( DEB_ERROR, "cancelapc caught 0x%x\n",
                      e.GetErrorCode() ));
        Win4Assert( !"CancelAPC caught an exception" );
    }
    END_CATCH
} //CancelAPCRoutine

//+-------------------------------------------------------------------------
//
//  Member:     CRequestServer::Quiesce, private
//
//  Synopsis:   Called by the APC when a query with notifications quiesces
//
//  History:    16-Sep-96   dlee       Created.
//
//--------------------------------------------------------------------------

void CRequestServer::Quiesce()
{
    TRY
    {
        prxDebugOut(( DEB_ITRACE,
                      "Quiescing server 0x%x io pipe 0x%x\n",
                      this, GetPipe() ));

        Win4Assert( pipeStatePending == _state );

        // If we're shutting down or the server is disconnecting the client
        // for being idle for too long, abort now.

        if ( _requestQueue.IsShutdown() || IsBeingRemoved() )
            THROW( CException( STATUS_TOO_LATE ) );

        // the long-running operation completed, so do the write

        _xTempBuffer.Free();

        Win4Assert( 0 != _pQuery );
        _state = pipeStateWrite;

        //
        // If the first call to Quiesce is to tell us that the asynch
        // query execution failed, free the query.
        //

        if ( FAILED ( _scPendingStatus ) )
            FreeQuery();

        CProxyMessage &msg = * (CProxyMessage *) _Buffer();
        msg.SetStatus( _scPendingStatus );

        Write( (BYTE *) _Buffer(), _cbPendingWrite, APCRoutine );

        // Note that the state of these may have changed after the check above

        if ( IsBeingRemoved() )
            CancelIO();
    }
    CATCH( CException, ex )
    {
        prxDebugOut(( DEB_ITRACE,
                      "quiesce write error 0x%x, pipe 0x%x\n",
                      ex.GetErrorCode(),
                      GetPipe() ));

        _state = pipeStateNone;
        _requestQueue.RecycleRequestServerNoThrow( this );
    }
    END_CATCH;
} //Quiesce

//+-------------------------------------------------------------------------
//
//  Member:     CRequestServer::QuiesceAPCRoutine, private/static
//
//  Synopsis:   Called in an APC when a query with notifications quiesces
//
//  Arguments:  [dwParam] - The request server
//
//  History:    16-Sep-96   dlee       Created.
//
//--------------------------------------------------------------------------

void WINAPI CRequestServer::QuiesceAPCRoutine(
    ULONG_PTR dwParam )
{
    CRequestServer & Server = * (CRequestServer *) dwParam;
    Server.Quiesce();
} //QuiesceAPCRoutine

//+-------------------------------------------------------------------------
//
//  Member:     CRequestServer::QueryQuiesced, public
//
//  Synopsis:   Called when a synchronous query is quiesced, so the pending
//              request can be completed by the thread that created the query.
//
//  History:    16-Feb-97   dlee       Created.
//
//--------------------------------------------------------------------------

void CRequestServer::QueryQuiesced(
    BOOL  fSuccess,
    SCODE sc )
{
    // only complete the work if the query is truly quiesced

    if ( fSuccess || _requestQueue.IsShutdown() || IsBeingRemoved() )
    {
        Win4Assert( IsBeingRemoved() || 0 != _pQuery || _requestQueue.IsShutdown() );
        Win4Assert( pipeStatePending == _state );

        _scPendingStatus = sc;
        _requestQueue.DecrementPendingItems();

        QueueUserAPC( CRequestServer::QuiesceAPCRoutine,
                      GetWorkerThreadHandle(),
                      (ULONG_PTR) this );
    }
} //QueryQuiesced

//+-------------------------------------------------------------------------
//
//  Member:     CRequestServer::CompleteNotification, public
//
//  Synopsis:   This is called by a query worker thread when a notification
//              should be delivered to the client.
//
//  Arguments:  [dwChangeType] - The notification
//
//  History:    16-Sep-96   dlee       Created.
//
//--------------------------------------------------------------------------

void CRequestServer::CompleteNotification(
    DWORD dwChangeType )
{
    // _pQuery can be 0 if the query is in the process of being deleted,
    // since _pQuery is set to 0 before it's actually deleted in FreeQuery(),
    // and queries that haven't quiesced yet always quiesce on destruction.

    if ( 0 != _pQuery )
    {
        CPMSendNotifyOut notify( dwChangeType );
        WriteSync( &notify, sizeof notify );
    }
} //CompleteNotification

//+-------------------------------------------------------------------------
//
//  Member:     CRequestServer::Cleanup, public
//
//  Synopsis:   Frees data and refcounts associated with the request server.
//
//  History:    16-Sep-96   dlee       Created.
//
//--------------------------------------------------------------------------

void CRequestServer::Cleanup()
{
    FreeQuery();
    _xTempBuffer.Free();
    _fClientIsRemote = FALSE;
    _state = pipeStateNone;
    _cbPendingWrite = 0;
    _scPendingStatus = S_OK;

    _xClientMachine.Free();
    _xClientUser.Free();

    // Remove the refcount on the thread since it no longer has to process
    // APCs on behalf of this request server.

    if ( 0 != _pWorkThread )
    {
        _hWorkThread = INVALID_HANDLE_VALUE;
        _workQueue.Release( _pWorkThread );
        _pWorkThread = 0;
    }

    _xDbProperties.Free();

    if ( !_xDocStore.IsNull() )
    {
        XInterface<ICiCAdviseStatus> xAdviseStatus;
        SCODE sc = _xDocStore->QueryInterface( IID_ICiCAdviseStatus,
                                               xAdviseStatus.GetQIPointer() );

        // It would be a bug if this QI actually failed.

        if ( S_OK == sc )
            xAdviseStatus->DecrementPerfCounterValue( CI_PERF_RUNNING_QUERIES );

        _xDocStore.Free();
    }
} //Cleanup

//+-------------------------------------------------------------------------
//
//  Member:     CRequestQueue::CRequestQueue, public
//
//  Synopsis:   Constructs a request queue
//
//  Arguments:  [cMaxCachedServerItems]    -- max # of cached pipe objects
//              [cMaxSimultaneousRequests] -- max # of simultaneous pipes
//
//  History:    16-Sep-96   dlee       Created.
//              30-Mar-98   kitmanh    Initialized _fNetPause
//                                     and _fNetContinue to FALSE
//
//--------------------------------------------------------------------------

CRequestQueue::CRequestQueue(
    unsigned     cMaxCachedServerItems,
    unsigned     cMaxSimultaneousRequests,
    unsigned     cmsDefaultClientTimeout,
    BOOL         fMinimizeWorkingSet,
    unsigned     cMinClientIdleTime,
    unsigned     cmsStartupDelay,
    const GUID & guidDocStoreClient ) :
    _guidDocStoreClient( guidDocStoreClient ),
    _cMaxSimultaneousRequests( cMaxSimultaneousRequests ),
    _cmsDefaultClientTimeout( cmsDefaultClientTimeout ),
    _fMinimizeWorkingSet( fMinimizeWorkingSet ),
    _cMinClientIdleTime( cMinClientIdleTime ),
    _cmsStartupDelay( cmsStartupDelay ),
    _fDocStoresOpen( FALSE ),
    _fShutdown( FALSE ),
    _cBusyItems( 0 ),
    _cPendingItems( 0 ),
    _workQueue( 1, CWorkQueue::workQueueRequest ),
    _tableActiveServers( cMaxSimultaneousRequests ),
    _queueCachedServers( cMaxCachedServerItems ),
    _fNetPause( FALSE ),
    _fNetContinue( FALSE ),
    _fNetStop( FALSE )
{

#if CI_PIPE_TESTING

    _pTraceBefore = 0;
    _pTraceAfter = 0;
    _pTraceRead = 0;
    _hTraceDll = LoadLibraryEx( L"cipipetrace.dll", 0,
                                LOAD_WITH_ALTERED_SEARCH_PATH );

    if ( 0 != _hTraceDll )
    {
        _pTraceBefore = (PipeTraceServerBeforeCall)
                        GetProcAddress( _hTraceDll, "ServerBefore" );
        _pTraceAfter = (PipeTraceServerAfterCall)
                        GetProcAddress( _hTraceDll, "ServerAfter" );
        _pTraceRead = (PipeTraceServerReadCall)
                        GetProcAddress( _hTraceDll, "ServerRead" );
    }

#endif // CI_PIPE_TESTING

    _workQueue.Init();

    //
    // Read the worker queue registry settings in the SYSTEM context
    // and initialize the parameters.
    //
    ULONG cMaxActiveThreads, cMinIdleThreads;
    _workQueue.GetWorkQueueRegParams( cMaxActiveThreads,
                                      cMinIdleThreads );
    _workQueue.RefreshParams( cMaxActiveThreads, cMinIdleThreads );

    //
    // The security checks for the pipe are done in this order:
    //   The system account can create instances of this pipe.
    //   No-one can write DAC or OWNER, or create pipe instances.
    //   Everyone can read, write, and synchronize around this pipe.
    //
    // Actual query result and admin security checking is done when the
    // requests are made, and are based on the pipe impersonation.
    //
    // This data really is const, but the Win32 security APIs don't do const.
    //

    static SID sidLocalSystem = { SID_REVISION,
                                  1,
                                  SECURITY_NT_AUTHORITY,
                                  SECURITY_LOCAL_SYSTEM_RID };
    static SID sidWorld = { SID_REVISION,
                            1,
                            SECURITY_WORLD_SID_AUTHORITY,
                            SECURITY_WORLD_RID };

    // NTRAID#DB-NTBUG9-83834-2000/07/31-dlee No way to protect Indexing Service named pipes.
    // FILE_CREATE_PIPE_INSTANCE no longer works, so we can't include
    // it in the AceData.  There is no good way to secure the pipe.

    ACE_DATA AceData[] =
    {
        //{ ACCESS_ALLOWED_ACE_TYPE, 0, 0,
        //  FILE_CREATE_PIPE_INSTANCE,
        //  &sidLocalSystem },
        { ACCESS_DENIED_ACE_TYPE, 0, 0,
          WRITE_DAC | WRITE_OWNER /*| FILE_CREATE_PIPE_INSTANCE */,
          &sidWorld },
        { ACCESS_ALLOWED_ACE_TYPE, 0, 0,
          GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
          &sidWorld },
    };
    const ULONG cAces = sizeof AceData / sizeof ACE_DATA;

    CiCreateSecurityDescriptor( AceData,
                                cAces,
                                &sidLocalSystem,
                                &sidLocalSystem,
                                _xSecurityDescriptor );
} //CRequestQueue

//+-------------------------------------------------------------------------
//
//  Member:     CRequestQueue::RecycleRequestServerNoThrow, public
//
//  Synopsis:   A request server has become available.  Either delete it
//              or cache it for use later.  There is a transfer of ownership.
//
//  Arguments:  [pServer] - The request server to be reused or deleted
//
//  History:    16-Sep-96   dlee       Created.
//
//  Notes:      This method must not throw.
//
//--------------------------------------------------------------------------

void CRequestQueue::RecycleRequestServerNoThrow(
    CRequestServer * pServer )
{
    Win4Assert( pServer->NoOutstandingAPCs() );

    CServerItem item( pServer );

    BOOL fDisconnectOk = pServer->Disconnect();
    if ( !fDisconnectOk )
    {
        prxDebugOut(( DEB_WARN, "Disconnect of server 0x%x failed %d\n",
                      pServer, GetLastError() ));
    }

    pServer->Cleanup();

    // Only try to reuse the server if the refcount is 1 (it's available).
    // The only reason the refcount might not be 1 is if we took an exception
    // in DoIt(). Wake up the main thread if a request server is available.

    if ( pServer->IsAvailable() )
    {
        // The Add will fail if the cache is full or there is an admin
        // operation like shutdown going on.

        if ( _queueCachedServers.Add( item ) )
            _event.Set();
    }

    long cCurrent = InterlockedDecrement( &_cBusyItems );

    // If no request servers are to be cached and we've fallen just
    // under the ceiling on the # of items, set the event so the main
    // thread can wake up and create another request server.

    if ( ( !_fShutdown ) &&
         ( 0 == _queueCachedServers.MaxRequests() ) &&
         ( cCurrent == (long) ( _cMaxSimultaneousRequests - 1 ) ) )
        _event.Set();
} //RecycleRequestServerNoThrow

//+-------------------------------------------------------------------------
//
//  Member:     CRequestQueue::OpenAllDocStores, private
//
//  Synopsis:   Tells the DocStore admin to open all docstores if they
//              aren't open yet.
//
//  History:    19-Jun-98   dlee       Created.
//
//--------------------------------------------------------------------------

void CRequestQueue::OpenAllDocStores()
{
    if ( !_fDocStoresOpen )
    {
        XInterface<ICiCDocStoreLocator> xLocator( DocStoreLocator() );
        Win4Assert( !xLocator.IsNull() );

        // Ignore failures to open docstores -- the docstore is responsible

        xLocator->OpenAllDocStores();

        _fDocStoresOpen = TRUE;
    }
} //OpenAllDocStores

//+-------------------------------------------------------------------------
//
//  Member:     CRequestQueue::DocStoreLocator, public
//
//  Synopsis:   Retrieves the Doc Store Locator for the client
//
//  History:    19-Jun-98   dlee       Created.
//
//--------------------------------------------------------------------------

ICiCDocStoreLocator * CRequestQueue::DocStoreLocator()
{
    return g_svcDocStoreLocator.Get( _guidDocStoreClient );
} //DocStoreLocator

//+-------------------------------------------------------------------------
//
//  Member:     CRequestQueue::DoWork, public
//
//  Synopsis:   This is the main loop for the CI service.  It makes
//              available pipes to which clients can connect.  When the
//              _evtStateChange event is triggered, the method exits.
//
//  History:    16-Sep-96   dlee       Created.
//
//--------------------------------------------------------------------------

void CRequestQueue::DoWork()
{
    // Throw away all that startup code

    SetProcessWorkingSetSize( GetCurrentProcess(), ~0, ~0 );

    HANDLE ah[2];
    ah[0] = _evtStateChange.GetHandle();

    do
    {
        CServerItem item;

        TRY
        {
            // If a cached server item is available, use it.  Otherwise,
            // create a non-cached item if we are under the limit.

            prxDebugOut(( DEB_ITRACE, "cached/busy items: %d / %d\n",
                          _queueCachedServers.Count(), _cBusyItems ));

            if ( ( !_queueCachedServers.AcquireTop( item ) ) &&
                 ( _cBusyItems < (LONG) _cMaxSimultaneousRequests ) )
                item.Create( CI_SERVER_PIPE_NAME,
                             _cmsDefaultClientTimeout,
                             *this,
                             _workQueue );

            if ( 0 != item.Get() )
            {
                // make sure it doesn't get thrown away right away

                item.Get()->SetLastTouchedTime( GetTickCount() );
                InterlockedIncrement( & _cBusyItems );
                BOOL fConnected = item.Get()->Connect();

                // if it's not connected yet, we have to wait on an event

                if ( !fConnected )
                {
                    prxDebugOut(( DEB_ITRACE,
                                  "waiting for connect of pipe 0x%x\n",
                                  item.Get()->GetPipe() ));

                    ah[1] = item.Get()->GetEvent();
                    DWORD dwTimeout = _fMinimizeWorkingSet ? 30000 : INFINITE;

                    DWORD dw;
                    do
                    {
                        //
                        // If catalogs haven't been opened yet due to the
                        // startup delay and it's a good time, open them.
                        //

                        if ( !_fDocStoresOpen )
                        {
                            if ( GetTickCount() >= _cmsStartupDelay )
                            {
                                OpenAllDocStores();
                            }
                            else
                            {
                                dwTimeout = 0;
                                DWORD dwTC = GetTickCount();
                                if ( _cmsStartupDelay > dwTC )
                                    dwTimeout += ( _cmsStartupDelay - dwTC );
                            }
                        }

                        // event 0 -- shutdown
                        // event 1 -- a client connected

                        dw = WaitForMultipleObjectsEx( 2,
                                                       ah,
                                                       FALSE,
                                                       dwTimeout,
                                                       FALSE );

                        // if an event triggered, handle it

                        if ( WAIT_TIMEOUT != dw )
                            break;

                        // If there isn't anything else going on, trim our
                        // working set, and back off on the next time

                        if ( 1 == _cBusyItems )
                        {
                            dwTimeout = 120000; // 2 minutes
                            SetProcessWorkingSetSize( GetCurrentProcess(), ~0, ~0 );
                        }
                    } while ( TRUE );

                    Win4Assert( ( 0 == dw ) || ( 1 == dw ) );

                    if ( 0 == dw )
                    {
                        item.Free();
                        InterlockedDecrement( & _cBusyItems );
                        break; // out of the do loop
                    }
                    else if ( 1 != dw )
                        THROW( CException() );

                    item.Get()->ResetEvent();
                }

                //
                // Open the docstores now since the connection will not
                // force in a catalog if it's for administration.
                //

                OpenAllDocStores();

                _workQueue.Add( item.Get() );
                item.Acquire();

                //
                // Fix for 123796. If we have no more request servers left
                // we should attempt to free one from an idle client.
                //

                if ( _cBusyItems == (LONG) _cMaxSimultaneousRequests )
                    WrestReqServerFromIdleClient();
            }
            else
            {
                //
                // Wait for a request server to become available, either
                // in the cache or under the max ceiling.
                // event 0 -- shutdown
                // event 1 -- request server is available
                // timeout: look for an idle connection to destroy
                //

                prxDebugOut(( DEB_ITRACE, "wait for RequestServer\n" ));

                DWORD dwTimeout = ( _cBusyItems == (LONG) _cMaxSimultaneousRequests ) ?
                                  10000 : INFINITE;

                ah[1] = _event.GetHandle();
                DWORD dw = WaitForMultipleObjectsEx( 2,
                                                     ah,
                                                     FALSE,
                                                     dwTimeout,
                                                     FALSE );

                prxDebugOut(( DEB_ITRACE, "wfsoex wakeup: %d\n", dw ));

                Win4Assert(( 0 == dw || 1 == dw || WAIT_TIMEOUT == dw));

                if ( WAIT_TIMEOUT == dw )
                    WrestReqServerFromIdleClient();
                else if ( 0 == dw )
                    break; // out of the do loop
                else if ( 1 != dw )
                    THROW( CException() );
            }
        }
        CATCH( CException, ex )
        {
            prxDebugOut(( DEB_WARN, "DoWork exception error 0x%x\n",
                          ex.GetErrorCode() ));

            if ( 0 != item.Get() )
            {
                item.Free();
                InterlockedDecrement( & _cBusyItems );
            }
        }
        END_CATCH;
    } while ( TRUE );

    Shutdown();
} //DoWork

//+-------------------------------------------------------------------------
//
//  Member:     CRequestQueue::WrestReqServerFromIdleClient, private
//
//  Synopsis:   Looks for connection that have been idle for more than
//              a predetermined amt of time. Flags the worst offender
//              and kicks it off to pave way for a new connection.
//
//  History:    03-Feb-98   KrishnaN       Created.
//
//--------------------------------------------------------------------------

void CRequestQueue::WrestReqServerFromIdleClient()
{
    // look for the most idle connection that crossed the threshold

    //
    // We could be a little more efficient if we kick out the
    // first eligible idle client instead of looking for the most
    // idle one.  But this is infrequent enough and not a big enough
    // problem that it's worth fixing.
    //

    XInterface<CRequestServer> xIdle;

    {
        CRequestServer * pMostIdleServer = 0;
        DWORD dwCurrentTick = GetTickCount();
        DWORD dwMaxIdleTime = 0;

        CLock lock( _mutex );
    
        for ( ULONG i = 0; i < _tableActiveServers.Size(); i++ )
        {
            CRequestServer *pServer = _tableActiveServers.GetEntry( i );
    
            if ( !_tableActiveServers.IsFree( pServer ) &&
                 INVALID_HANDLE_VALUE != pServer->GetWorkerThreadHandle() )
            {
                DWORD dwLast = pServer->GetLastTouchedTime();
    
                // The windows tick count wraps around every 50 days.
                // Unsigned arithmetic accounts for that, so we don't have to.
    
                DWORD dwIdleTime = dwCurrentTick - dwLast;
    
                if ( dwIdleTime > _cMinClientIdleTime &&
                     dwIdleTime > dwMaxIdleTime )
                {
                    dwMaxIdleTime = dwIdleTime;
                    pMostIdleServer = pServer;
                }
            }
        }

        if ( 0 != pMostIdleServer )
        {
            pMostIdleServer->AddRef();
            xIdle.Set( pMostIdleServer );
        }
    }

    if ( !xIdle.IsNull() )
    {
        // Make sure this isn't in the cache or it won't get deleted

        _queueCachedServers.DisableAdditions();
        FreeCachedServers();

        // Disconnect this most-idle connection
    
        CEventSem evt;
        xIdle->BeingRemoved( &evt );
    
        // Attempt to cancel it if it's still associated with a worker thread

        HANDLE hThrd = xIdle->GetWorkerThreadHandle();
        if ( INVALID_HANDLE_VALUE != hThrd )
        {
            DWORD dwRet = QueueUserAPC( CRequestServer::CancelAPCRoutine,
                                        hThrd,
                                        (ULONG_PTR) xIdle.GetPointer() );
            Win4Assert( 0 != dwRet );

            xIdle.Acquire();
        }

        // Wait for the connection to completely go away

        xIdle.Free();
        evt.Wait();

        _queueCachedServers.EnableAdditions();

        prxDebugOut(( DEB_ITRACE, "Successfully disconnected an idle client.\n" ));
    }
} //WrestReqServerFromIdleClient

//+-------------------------------------------------------------------------
//
//  Member:     CRequestQueue::FreeCachedServers, private
//
//  Synopsis:   Removes all cached request servers
//
//  History:    01-July-99   dlee       Created from existing code
//
//--------------------------------------------------------------------------

void CRequestQueue::FreeCachedServers()
{
    CServerItem item;
    while ( _queueCachedServers.AcquireTop( item ) )
        item.Free();
} //FreeCachedServers

//+-------------------------------------------------------------------------
//
//  Member:     CRequestQueue::ShutdownActiveServers, private
//
//  Synopsis:   Cancels pending IO on each active request server.  It may
//              be that a given request server has no IO pending, and
//              that will be taken care of when the IO is requested.
//
//  Arguments:  [pDocStore] - pointer to a docstore
//              if pDocStore == NULL, shutdown all active servers
//
//  History:    19-May-97   dlee       Created.
//              24-Apr-98   kitmanh    Shut down active servers associated
//                                     with the specified docstore
//
//  Note:       Always addref to everyone in the first loop, since the
//              in the 2nd loop, the smart pointer xServer will release
//              the pServer when it's out of scope, whether the docstore
//              matches or not.
//
//--------------------------------------------------------------------------

void CRequestQueue::ShutdownActiveServers( ICiCDocStore * pDocStore )
{
    ciDebugOut(( DEB_ITRACE, "ShutdownActiveServers is called\n" ));

    // AddRef all outstanding request servers so they won't go away

    {
        CLock lock( _mutex );

        for ( ULONG i = 0; i < _tableActiveServers.Size(); i++ )
        {
            CRequestServer *pServer = _tableActiveServers.GetEntry( i );

            if ( !_tableActiveServers.IsFree( pServer ) )
                pServer->AddRef();
        }
    }

    // Make sure whatever request servers are freed aren't in the cache

    _queueCachedServers.DisableAdditions();
    FreeCachedServers();

    //
    // For each of the active request servers, file an APC so that the io
    // for that thread will be canceled by that thread, and so that
    // pending queries are cancelled.  No items will be added to
    // _tableActiveServers during this loop, but items may be deleted.
    //

    for ( ULONG i = 0; i < _tableActiveServers.Size(); i++ )
    {
        CRequestServer *pServer = _tableActiveServers.GetEntry( i );

        if ( !_tableActiveServers.IsFree( pServer ) )
        {
            XInterface<CRequestServer> xServer( pServer );

            // The thread handle will be invalid if no worker thread is yet
            // associated or the association has been terminated.  In either
            // case by definition there can be no outstanding APCs.

            // Note that the thread can't go away from under us since all
            // threads in the worker pool were addref'ed earlier

            // Disconnect it even if it has no docstore since it may be
            // processing a connect and be about to get a docstore.

            if ( ( 0 == pDocStore ) ||
                 ( pServer->GetDocStore() == 0 ) ||
                 ( pServer->GetDocStore() == pDocStore ) )
            {
                CEventSem evt;
                pServer->BeingRemoved( &evt );

                HANDLE hThread = pServer->GetWorkerThreadHandle();

                if ( INVALID_HANDLE_VALUE != hThread &&
                     QueueUserAPC( CRequestServer::CancelAPCRoutine,
                                   hThread,
                                   (ULONG_PTR) pServer ) )
                    xServer.Acquire();

                // Wait for the client to stop using the docstore

                xServer.Free();
                evt.Wait();
            }
        }
    }

    _queueCachedServers.EnableAdditions();
} //ShutdownActiveServers

//+-------------------------------------------------------------------------
//
//  Method:     CRequestQueue::Shutdown
//
//  Synopsis:   Cleans up after the request queue.  Can't throw.
//              handles both net stop and net pause
//
//  History:    16-Sep-96   dlee       Created.
//              30-Mar-98   kitmanh    Don't shutdown workqueue if we're
//                                     doing a net pause or a net continue
//              06-25-98    kitmanh    Update
//
//--------------------------------------------------------------------------

void CRequestQueue::Shutdown()
{
    prxDebugOut(( DEB_ITRACE, "Shutdown\n" ));

    // Make sure the worker threads don't go away

    _workQueue.AddRefWorkThreads();

    unsigned i = 0;
    SCWorkItem WorkItem;

    {
        CLock lock( _mutex );

        if ( _fNetStop )
            _fShutdown = TRUE;

        if ( _stateChangeArray.Count() > 0 )
            WorkItem = _stateChangeArray.Get(i);
        else
        {
            ciDebugOut(( DEB_ITRACE, "Shutdown:: eNetStop\n" ));

            // No workitems exist, must be net stop, pause or continue
            // create a stop work item with NULL docstore

            WorkItem.type = eNetStop;  // note: workitem.type is not really
                                       // important here

            ciDebugOut(( DEB_ITRACE, "WorkItem.type is %d\n", WorkItem.type ));
            WorkItem.pDocStore = 0;
            WorkItem.StoppedCat = 0;
        }
    }

    ciDebugOut(( DEB_ITRACE, "WorkItem.Count is %d\n", _stateChangeArray.Count() ));

    do
    {
        ciDebugOut(( DEB_ITRACE, "i is %d\n", i ));

        // now loop thru the workitem list and do the work

        i++;

        if ( 0 == WorkItem.StoppedCat )
            ShutdownActiveServers( WorkItem.pDocStore );

        {
            CLock lock( _mutex );

            if ( i < _stateChangeArray.Count() )
                WorkItem = _stateChangeArray.Get(i);
        }
    } while ( i < _stateChangeArray.Count() );

    if ( _fNetStop || _fNetPause || _fNetContinue )
    {
        if ( _stateChangeArray.Count() > 0 )
        {
            // No workitem has been created at the top of the function,
            // thus need to run ShutdownActiveServers here

            ShutdownActiveServers( 0 );
        }

        Win4Assert( !_queueCachedServers.Any() );
        Win4Assert( !_tableActiveServers.Any() );
        Win4Assert( 0 == _cBusyItems );
        Win4Assert( 0 == _cPendingItems );
    }

    _workQueue.ReleaseWorkThreads();

    if ( _fNetStop )
        _workQueue.Shutdown();
} //Shutdown

//+---------------------------------------------------------------------------
//
//  Function:   StartFWCiSvcWork
//
//  Synopsis:   Is the main server loop. Is called by the cisvc to receive
//              requests and demultiplex on them.
//
//  Arguments:  [lock] -- to be released when it's ok to call StopFWCiSvcWork
//
//  History:    1-30-97   srikants   Created
//              4-13-98   kitmanh    Moved the creation of CRequestQueue to
//                                   StartCisvcWork and passed the pointer
//                                   in from StartCisvcWork to initialize
//                                   g_pRequestQueue
//
//  Notes:      Must be called in the SYSTEM context without any impersonation.
//
//----------------------------------------------------------------------------

SCODE StartFWCiSvcWork( CReleasableLock & lock,
                        CRequestQueue * pRequestQueue,
                        CEventSem & evt )
{
    ciDebugOut(( DEB_ITRACE, "StartFWCiSvcWork is called\n" ));

    SCODE sc = S_OK;

    TRY
    {
        ciDebugOut(( DEB_ITRACE, "NetPaused == %d and NetContinued == %d\n",
                     pRequestQueue->IsNetPause(), pRequestQueue->IsNetContinue() ));

        g_pRequestQueue = pRequestQueue;
        evt.Set();
        lock.Release();

        ciDebugOut(( DEB_ITRACE, "evtPauseContinue is set\n" ));
        ciDebugOut(( DEB_ITRACE, "StartFWCisvcWork.. About to DoWork()\n" ));

        g_pRequestQueue->DoWork();

        ciDebugOut(( DEB_ITRACE, "StartFWCisvcWork.. Just fell out from DoWork\n" ));
        ciDebugOut(( DEB_ITRACE, "g_pRequestQueue->IsShutdown() is %d\n",
                     g_pRequestQueue->IsShutdown() ));

        // Check if a netpause or netcontinue is requested during DoWork()
        if ( g_pRequestQueue->IsShutdown() &&
             ! ( g_pRequestQueue->IsNetPause() || g_pRequestQueue->IsNetContinue() ) )
        {
            g_svcDocStoreLocator.Shutdown();

            //
            // Shutdown the Content Index work queue.
            //

            TheWorkQueue.Shutdown();
        }
    }
    CATCH( CException,e )
    {
        ciDebugOut(( DEB_ERROR,
                     "Terminating - DoCiSvcServerWork(). Error 0x%X\n",
                     e.GetErrorCode() ));

        sc = e.GetErrorCode();
    }
    END_CATCH

    ciDebugOut(( DEB_ITRACE, "Request lock for resetting g_pRequestQueue to 0\n" ));
    lock.Request();
    ciDebugOut(( DEB_ITRACE, "Got lock\n" ));
    g_pRequestQueue = 0;
    ciDebugOut(( DEB_ITRACE, "About to release lock\n" ));
    lock.Release();

    return sc;
} //StartFWCiSvcWork

//+---------------------------------------------------------------------------
//
//  Function:   StopFWCiSvcWork
//
//  Synopsis:   Stops the server work. It is the Shutdown method called by
//              cisvc to stop the work.
//
//  Arguments:  [type]  -- type of work to do
//              [wcVol] -- volume letter (for eLockVol only)
//
//  History:    1-30-97   srikants   Created
//              3-30-98   kitmanh    If we're doing a net pause, set
//                                   _fNetPause member in the CRequestQueue
//
//----------------------------------------------------------------------------

SCODE StopFWCiSvcWork( ECiSvcActionType type, 
                       CReleasableLock * pLock, 
                       CEventSem * pEvt,
                       WCHAR wcVol )
{
    ciDebugOut(( DEB_ITRACE, "StopFWCiSvcWork.. type == %d\n", type ));
    
    SCODE sc = S_OK;

    // for the case where a shutdown occurs while the service is restarting
    // for pausing, continuing, stopping a catalog

    if ( eNetStop == type && 0 == g_pRequestQueue ) 
    {
       //block until g_pRequestQueue is non-zero
       ciDebugOut(( DEB_ITRACE, "0 == g_pRequestQueue\n" ));
       ciDebugOut(( DEB_ITRACE, "StopFWCiSvcWork: Release the lock\n" ));
       ciDebugOut(( DEB_ITRACE, "Block on g_pevtPauseContinue for eNetStop\n" ));
       
       //this lock need to be released, so the lock can be grabbed by the
       //thread just done shutting down to finish restarting (calling 
       //StartFWCiSvcWork)
       pLock->Release();
       pEvt->Wait();
       ciDebugOut(( DEB_ITRACE, "Done waiting on g_pevtPauseContinue\n" ));
       pLock->Request();
       ciDebugOut(( DEB_ITRACE, "StopFWCiSvcWork requested the lock\n" ));
    }

    if ( 0 != g_pRequestQueue )
    {
        ciDebugOut(( DEB_ITRACE, "0 != g_pRequestQueue\n" ));
        ciDebugOut(( DEB_ITRACE, "g_pRequestQueue->IsShutdown() is %d\n", g_pRequestQueue->IsShutdown()  ));        
        
        // Ignore all events if shutdown is initialized
        if ( g_pRequestQueue->IsShutdown() )
            return STATUS_TOO_LATE;

        XInterface<ICiCDocStoreLocator> xLocator( g_pRequestQueue->DocStoreLocator() );
        Win4Assert( !xLocator.IsNull() );

        switch ( type )
        {
        case eNetPause:
            g_pRequestQueue->SetNetPause();
            break;

        case eNetContinue:
            g_pRequestQueue->SetNetContinue();
            break;

        case eNetStop:
            g_pRequestQueue->SetNetStop();
            break;

        case eCatRO:
        case eCatW:
        case eStopCat:
        case eNoQuery:
            break;

        case eLockVol:
            sc = xLocator->StopCatalogsOnVol( wcVol, g_pRequestQueue );
            ciDebugOut(( DEB_ITRACE, "After StopCatalogsOnVol\n" ));
            break;

        case eUnLockVol:
            sc = xLocator->StartCatalogsOnVol( wcVol, g_pRequestQueue );
            ciDebugOut(( DEB_ITRACE, "After StartCatalogsOnVol\n" ));
            break;

        default:
            Win4Assert( !"StopFWCiSvcWork is called with invalid ECisvcActionType" );
        }
        ciDebugOut(( DEB_ITRACE, "Time to WakeForStateChange; type = %d\n", type ));
        g_pRequestQueue->WakeForStateChange();
    }
    else
    {
        ciDebugOut(( DEB_ITRACE, "g_pRequestQueue->IsShutdown() is 0\n" ));        
    }

    return sc;
} //StopFWCiSvcWork