You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4453 lines
151 KiB
4453 lines
151 KiB
//+-------------------------------------------------------------------------
|
|
//
|
|
// 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
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: CRequestServer::DeferredAPC, public
|
|
//
|
|
// Synopsis: Performs an APC that was earlier deferred.
|
|
//
|
|
// History: 16-Sep-96 dlee Created.
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
void CRequestServer::DeferredAPC()
|
|
{
|
|
DoAPC( _dwDeferredAPCError, _cbDeferredAPCTransferred );
|
|
} //DeferredAPC
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// 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 we are recursing processing APCs, defer this APC.
|
|
// Why would we be processing APCs? Someone in the query path
|
|
// probably used RPC, and due to a bug in RPC we hit this.
|
|
//
|
|
|
|
if ( _pWorkThread->IsProcessingAPC() )
|
|
{
|
|
_dwDeferredAPCError = dwError;
|
|
_cbDeferredAPCTransferred = cbTransferred;
|
|
_pWorkThread->DeferAPC( this );
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Mark the thread as busy processing an APC and addref it so the
|
|
// destructor can unmark it.
|
|
//
|
|
|
|
XAPCWorkerThread apcWorker( _workQueue, _pWorkThread );
|
|
|
|
// 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( ¬ify, 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
|
|
|