windows-nt/Source/XPSP1/NT/inetsrv/query/common/crequest.cxx
2020-09-26 16:20:57 +08:00

1227 lines
38 KiB
C++

//+-------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1996 - 1998
//
// File: crequest.cxx
//
// Contents: Client side of catalog/query requests
//
// Classes: CPipeClient
// CRequestClient
//
// History: 16-Sep-96 dlee Created.
//
//--------------------------------------------------------------------------
#include <pch.cxx>
#pragma hdrstop
#include <query.hxx>
#include <cidbprop.hxx>
#include <sizeser.hxx>
#include <memser.hxx>
DECLARE_INFOLEVEL(prx);
//+-------------------------------------------------------------------------
//
// Member: CPipeClient::CPipeClient, protected
//
// Synopsis: Simple constructor
//
// History: 16-Sep-96 dlee Created.
//
//--------------------------------------------------------------------------
CPipeClient::CPipeClient() : _hPipe( INVALID_HANDLE_VALUE )
{
} //CPipeClient
//+-------------------------------------------------------------------------
//
// Member: CPipeClient::Init, protected
//
// Synopsis: Second phase constructor for a client pipe. The pipe is
// opened or an exception is thrown. This method may take
// awhile to complete depending on the availability of a pipe
// instance on the server and on the timeout for the pipe as
// set on the server.
//
// Arguments: [pwcMachine] - Name of the server or "." for local machine
// [pwcPipe] - Name of the pipe
//
// History: 16-Sep-96 dlee Created.
//
//--------------------------------------------------------------------------
void CPipeClient::Init(
WCHAR const * pwcMachine,
WCHAR const * pwcPipe )
{
#if CI_PIPE_TESTING
_pTraceBefore = 0;
_pTraceAfter = 0;
_hTraceDll = LoadLibraryEx( L"cipipetrace.dll", 0,
LOAD_WITH_ALTERED_SEARCH_PATH );
if ( 0 != _hTraceDll )
{
_pTraceBefore = (PipeTraceBeforeCall)
GetProcAddress( _hTraceDll, "Before" );
_pTraceAfter = (PipeTraceAfterCall)
GetProcAddress( _hTraceDll, "After" );
}
#endif // CI_PIPE_TESTING
RtlZeroMemory( &_overlapped, sizeof _overlapped );
RtlZeroMemory( &_overlappedWrite, sizeof _overlappedWrite );
_fServerIsRemote = ( L'.' != pwcMachine[0] );
WCHAR awcName[ MAX_PATH ];
unsigned cwc = wcslen( pwcMachine );
cwc += wcslen( pwcPipe );
cwc += 20;
if ( cwc >= ( sizeof awcName / sizeof WCHAR ) )
THROW( CException( E_INVALIDARG ) );
wcscpy( awcName, L"\\\\" );
wcscat( awcName, pwcMachine );
wcscat( awcName, L"\\pipe\\" );
wcscat( awcName, pwcPipe );
#if CIDBG == 1
WCHAR awcThisUser[ UNLEN ];
DWORD cbThisUser = sizeof awcThisUser / sizeof WCHAR;
GetUserName( awcThisUser, &cbThisUser );
prxDebugOut(( DEB_ITRACE,
"connecting tid %d to pipe '%ws' as user '%ws'\n",
GetCurrentThreadId(),
awcName,
awcThisUser ));
#endif // CIDBG == 1
do
{
// Timeout based on what the server specified if a pipe exists
// but no instances are available. Throw if no pipe exists.
if ( ! WaitNamedPipe( awcName, NMPWAIT_USE_DEFAULT_WAIT ) )
QUIETTHROW( CException() );
// At least one instance was available. This CreateFile will fail
// if some other app grabbed the instance before we could. If so,
// wait and try again.
_hPipe = CreateFile( awcName,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
0, // security
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
0 ); // template
if ( INVALID_HANDLE_VALUE != _hPipe )
{
// Local client pipes are always created in byte mode, not
// message mode, so set the mode to message.
DWORD dwMode = PIPE_READMODE_MESSAGE | PIPE_WAIT;
if ( ! SetNamedPipeHandleState( _hPipe, &dwMode, 0, 0 ) )
{
HRESULT hr = HRESULT_FROM_WIN32( GetLastError() );
CloseHandle( _hPipe );
_hPipe = INVALID_HANDLE_VALUE;
THROW( CException( hr ) );
}
break;
}
else if ( ERROR_PIPE_BUSY != GetLastError() )
{
THROW( CException() );
}
} while ( TRUE );
prxDebugOut(( DEB_ITRACE, "created pipe 0x%x\n", _hPipe ));
} //Init
//+-------------------------------------------------------------------------
//
// Member: CPipeClient::Close, protected
//
// Synopsis: Closes the pipe if it is open
//
// History: 16-Sep-96 dlee Created.
//
//--------------------------------------------------------------------------
void CPipeClient::Close()
{
if ( INVALID_HANDLE_VALUE != _hPipe )
{
prxDebugOut(( DEB_ITRACE, "closing pipe: 0x%x\n", _hPipe ));
BOOL fCloseOk = CloseHandle( _hPipe );
Win4Assert( fCloseOk );
_hPipe = INVALID_HANDLE_VALUE;
}
#if CI_PIPE_TESTING
if ( 0 != _hTraceDll )
{
FreeLibrary( _hTraceDll );
_hTraceDll = 0;
}
#endif // CI_PIPE_TESTING
} //Close
//+-------------------------------------------------------------------------
//
// Function: HandleClientWriteError
//
// Synopsis: Handles error case on pipe write commands
//
// Arguments: [hPipe] -- Pipe on which the operation failed
//
// Notes: When it looks like the connection has gone stale, throw
// STATUS_CONNECTION_DISCONNECTED, so the caller knows to
// open a new pipe to the server.
//
// History: 16-May-99 dlee Created.
//
//--------------------------------------------------------------------------
void HandleClientWriteError( HANDLE hPipe )
{
//
// This error state will happen for stale cached ICommands.
// Alternatively, the handle will be set to INVALID_HANDLE_VALUE by
// TerminateRudelyNoThrow(), and the pipe will no longer
// be connected if cisvc went down.
// Throw a well-known status so we can try to connect again.
//
DWORD dwError = GetLastError();
if ( INVALID_HANDLE_VALUE == hPipe ||
ERROR_PIPE_NOT_CONNECTED == dwError ||
ERROR_BAD_PIPE == dwError ||
ERROR_BAD_NET_RESP == dwError ) // rdr gives this sometimes
THROW( CException( STATUS_CONNECTION_DISCONNECTED ) );
THROW( CException() );
} //HandleClientWriteError
//+-------------------------------------------------------------------------
//
// Member: CPipeClient::TransactSync, protected
//
// Synopsis: Does a synchronous write/read transaction on the pipe.
//
// Arguments: [pvWrite] - Buffer to be written
// [cbToWrite] - # of bytes to write
// [pvRead] - Buffer for read result
// [cbReadRequest] - Size of pvRead
// [cbRead] - Returns # of bytes read
//
// History: 16-Sep-96 dlee Created.
//
//--------------------------------------------------------------------------
void CPipeClient::TransactSync(
void * pvWrite,
DWORD cbToWrite,
void * pvRead,
DWORD cbReadRequest,
DWORD & cbRead )
{
prxDebugOut(( DEB_ITRACE, "xact tid %d on pipe 0x%x\n",
GetCurrentThreadId(),
_hPipe ));
//
// Win32 named pipe operations require buffers < 64k. If you specify
// a larger buffer it succeeds without a failure code, but the server
// never sees the request. So do an explicit check here to validate
// the buffer size.
//
if ( cbToWrite > 0xffff )
THROW( CException( E_INVALIDARG ) );
_overlapped.hEvent = _event.GetHandle();
#if CI_PIPE_TESTING
void * pvWriteOrg = pvWrite;
DWORD cbToWriteOrg = cbToWrite;
if ( 0 != _pTraceBefore )
(*_pTraceBefore)( _hPipe,
cbToWriteOrg,
pvWriteOrg,
cbToWrite,
pvWrite );
#endif // CI_PIPE_TESTING
if ( ! TransactNamedPipe( _hPipe,
pvWrite,
cbToWrite,
pvRead,
cbReadRequest,
&cbRead,
&_overlapped ) )
{
if ( ERROR_IO_PENDING == GetLastError() )
{
if ( !GetOverlappedResult( _hPipe,
&_overlapped,
&cbRead,
TRUE ) )
HandleClientWriteError( _hPipe );
}
else
HandleClientWriteError( _hPipe );
}
#if CI_PIPE_TESTING
if ( 0 != _pTraceAfter )
(*_pTraceAfter)( _hPipe,
cbToWriteOrg,
pvWriteOrg,
cbToWrite,
pvWrite,
cbRead,
pvRead );
#endif // CI_PIPE_TESTING
} //TransactSync
//+-------------------------------------------------------------------------
//
// Member: CPipeClient::WriteSync, protected
//
// Synopsis: Does a synchronous write to the pipe.
//
// Arguments: [pvWrite] - Buffer to be written
// [cbToWrite] - # of bytes to write
//
// Notes: When it looks like the connection has gone stale, throw
// STATUS_CONNECTION_DISCONNECTED, so the caller knows to
// open a new pipe to the server,.
//
// History: 16-Sep-96 dlee Created.
//
//--------------------------------------------------------------------------
void CPipeClient::WriteSync(
void * pvWrite,
DWORD cbToWrite )
{
prxDebugOut(( DEB_ITRACE, "writesync tid %d on pipe 0x%x\n",
GetCurrentThreadId(),
_hPipe ));
//
// Win32 named pipe operations require buffers < 64k. If you specify
// a larger buffer it succeeds without a failure code, but the server
// never sees the request. So do an explicit check here to validate
// the buffer size.
//
if ( cbToWrite > 0xffff )
THROW( CException( E_INVALIDARG ) );
_overlappedWrite.hEvent = _eventWrite.GetHandle();
DWORD cbWritten;
if ( ! WriteFile( _hPipe,
pvWrite,
cbToWrite,
&cbWritten,
&_overlappedWrite ) )
{
if ( ERROR_IO_PENDING == GetLastError() )
{
if ( !GetOverlappedResult( _hPipe,
&_overlappedWrite,
&cbWritten,
TRUE ) )
HandleClientWriteError( _hPipe );
}
else
HandleClientWriteError( _hPipe );
}
} //WriteSync
//+-------------------------------------------------------------------------
//
// Member: CPipeClient::ReadSync, protected
//
// Synopsis: Does a synchronous from the pipe.
//
// Arguments: [pvRead] - Buffer for read result
// [cbReadRequest] - Size of pvRead
// [cbRead] - Returns # of bytes read
//
// History: 16-Sep-96 dlee Created.
//
//--------------------------------------------------------------------------
void CPipeClient::ReadSync(
void * pvRead,
DWORD cbToRead,
DWORD & cbRead )
{
prxDebugOut(( DEB_ITRACE, "readsync 1 tid %d on pipe 0x%x\n",
GetCurrentThreadId(),
_hPipe ));
_overlapped.hEvent = _event.GetHandle();
if ( ! ReadFile( _hPipe,
pvRead,
cbToRead,
&cbRead,
&_overlapped ) )
{
if ( ERROR_IO_PENDING == GetLastError() )
{
if ( !GetOverlappedResult( _hPipe,
&_overlapped,
&cbRead,
TRUE ) )
{
// If this assert hits, you probably added large notify msg
Win4Assert( ERROR_MORE_DATA != GetLastError() );
THROW( CException() );
}
}
else
THROW( CException() );
}
} //ReadSync
//+-------------------------------------------------------------------------
//
// Member: CPipeClient::ReadSync, protected
//
// Synopsis: Does a synchronous read from the pipe, aborting the read
// if hEvent is signalled before the read is started or
// completed.
//
// Arguments: [pvRead] - Buffer for read result
// [cbReadRequest] - Size of pvRead
// [cbRead] - Returns # of bytes read
// [hEvent] - When signalled, the read is aborted
//
// Returns: TRUE if hEvent has been triggered
//
// History: 16-Sep-96 dlee Created.
//
//--------------------------------------------------------------------------
BOOL CPipeClient::ReadSync(
void * pvRead,
DWORD cbToRead,
DWORD & cbRead,
HANDLE hEvent )
{
cbRead = 0;
// Since the read below can complete without going pending,
// check the terminate event here first.
if ( 0 == WaitForSingleObject( hEvent, 0 ) )
return TRUE;
prxDebugOut(( DEB_ITRACE, "readsync 2 tid %d on pipe 0x%x\n",
GetCurrentThreadId(),
_hPipe ));
_overlapped.hEvent = _event.GetHandle();
if ( ! ReadFile( _hPipe,
pvRead,
cbToRead,
&cbRead,
&_overlapped ) )
{
if ( ERROR_IO_PENDING == GetLastError() )
{
HANDLE ah[2];
ah[0] = _event.GetHandle();
ah[1] = hEvent;
DWORD dw = WaitForMultipleObjects( 2, ah, FALSE, INFINITE );
Win4Assert( 0 == dw || 1 == dw );
if ( 0 == dw )
{
if ( !GetOverlappedResult( _hPipe,
&_overlapped,
&cbRead,
FALSE ) )
THROW( CException() );
}
else if ( 1 == dw )
{
prxDebugOut(( DEB_ITRACE,
"notify thread told to die, cancel i/o\n" ));
// Cancel the io so that it won't complete on buffers that
// have been freed. No check can be made of the return code
// since the pipe may have been closed by now on the server.
BOOL fOK = CancelIo( _hPipe );
prxDebugOut(( DEB_ITRACE, "CancelIo: %d\n", fOK ));
if ( !fOK ) {
prxDebugOut(( DEB_ITRACE, "CancelIo error: %d\n",
GetLastError() ));
}
if ( !GetOverlappedResult( _hPipe,
&_overlapped,
&cbRead,
TRUE ) )
{
cbRead = 0;
DWORD dw = GetLastError();
prxDebugOut(( DEB_ITRACE,
"i/o cancelled, result: %d\n", dw ));
// benign assert if hit; I'm is just curious.
Win4Assert( ERROR_OPERATION_ABORTED == dw ||
ERROR_NO_DATA == dw );
}
return TRUE;
}
else THROW( CException() );
}
else THROW( CException() );
}
return FALSE;
} //ReadSync
//+-------------------------------------------------------------------------
//
// Function: TranslateNewPropsToOldProps
//
// Synopsis: Translates v 6+ client properties into version 5 props
//
// Arguments: [oldProps] - destination of translated values
// [newProps] - source of translated values
//
// History: 31-May-97 dlee Created.
//
//--------------------------------------------------------------------------
void TranslateNewPropsToOldProps(
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 new set
BSTR bstrMachine = 0;
BSTR bstrCatalog = 0;
CDynArrayInPlace<LONG> aDepths(2);
CDynArrayInPlace<BSTR> aScopes(2);
LONG lQueryType = 0;
for ( ULONG i = 0; i < newProps.Count(); i++ )
{
CDbPropSet & propSet = newProps.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++ )
{
VARIANT &v = pProp->vValue;
switch ( pProp->dwPropertyID )
{
case DBPROP_MACHINE :
{
if ( VT_BSTR == v.vt )
bstrMachine = v.bstrVal;
else if ( ( VT_ARRAY | VT_BSTR ) == v.vt )
{
ULONG cElem = v.parray->rgsabound[0].cElements;
WCHAR **ppMachines = (WCHAR **) v.parray->pvData;
if ( 0 != cElem )
{
// if not 1, it's a bug in higher level code
Win4Assert( 1 == cElem );
bstrMachine = ppMachines[0];
}
}
break;
}
}
}
}
else if ( guidFsClientPropset == pSet->guidPropertySet )
{
ULONG cProp = pSet->cProperties;
DBPROP * pProp = pSet->rgProperties;
for ( ULONG p = 0; p < pSet->cProperties; p++, pProp++ )
{
VARIANT &v = pProp->vValue;
prxDebugOut(( DEB_ITRACE, "converting from vt: 0x%x\n", v.vt ));
switch ( pProp->dwPropertyID )
{
case DBPROP_CI_INCLUDE_SCOPES :
{
// can be either a BSTR or a safearray of BSTRs
if ( VT_BSTR == v.vt )
{
aScopes[0] = v.bstrVal;
}
else if ( ( VT_ARRAY | VT_BSTR ) == v.vt )
{
ULONG cElem = v.parray->rgsabound[0].cElements;
WCHAR **ppScopes = (WCHAR **) v.parray->pvData;
for ( ULONG e = 0; e < cElem; e++ )
aScopes[ e ] = ppScopes[ e ];
}
break;
}
case DBPROP_CI_DEPTHS :
{
// can be either an I4 or an array of I4s
if ( VT_I4 == v.vt )
{
aDepths[0] = v.lVal;
}
else if ( ( VT_ARRAY | VT_I4 ) == v.vt )
{
ULONG cElem = v.parray->rgsabound[0].cElements;
ULONG *pElem = (ULONG *) v.parray->pvData;
for ( ULONG e = 0; e < cElem; e++ )
aDepths[ e ] = pElem[ e ];
}
break;
}
case DBPROP_CI_CATALOG_NAME :
{
if ( VT_BSTR == v.vt )
bstrCatalog = v.bstrVal;
else if ( ( VT_ARRAY | VT_BSTR ) == v.vt )
{
ULONG cElem = v.parray->rgsabound[0].cElements;
WCHAR **ppNames = (WCHAR **) v.parray->pvData;
if ( 0 != cElem )
{
// if not 1, it's a bug in higher level code
Win4Assert( 1 == cElem );
bstrCatalog = ppNames[0];
}
}
break;
}
case DBPROP_CI_QUERY_TYPE :
{
Win4Assert( VT_I4 == v.vt );
lQueryType = v.lVal;
break;
}
}
}
}
}
if ( 0 == bstrCatalog ||
0 == bstrMachine )
THROW( CException( STATUS_INVALID_PARAMETER ) );
prxDebugOut(( DEB_ITRACE,
"Converting new props to old props, catalog '%ws'\n",
bstrCatalog ));
prxDebugOut(( DEB_ITRACE,
"type %d, %d scopes, %d depths\n",
lQueryType,
aScopes.Count(),
aDepths.Count() ));
// create an old set of properties based on the info
DBPROPSET aNewSet[2];
DBPROP aFSProps[4];
RtlZeroMemory( aFSProps, sizeof aFSProps );
ULONG cNewSet = 2;
aNewSet[0].cProperties = 2;
aNewSet[0].guidPropertySet = guidFsClientPropset;
aNewSet[0].rgProperties = aFSProps;
aFSProps[0].dwPropertyID = DBPROP_CI_CATALOG_NAME;
aFSProps[0].vValue.vt = VT_LPWSTR;
aFSProps[0].vValue.bstrVal = bstrCatalog;
aFSProps[0].colid.eKind = DBKIND_GUID_PROPID;
aFSProps[1].dwPropertyID = DBPROP_CI_QUERY_TYPE;
aFSProps[1].vValue.vt = VT_I4;
aFSProps[1].vValue.lVal = lQueryType;
aFSProps[1].colid.eKind = DBKIND_GUID_PROPID;
if ( 0 != aDepths.Count() )
{
aFSProps[cNewSet].dwPropertyID = DBPROP_CI_DEPTHS;
aFSProps[cNewSet].colid.eKind = DBKIND_GUID_PROPID;
PROPVARIANT &vDepths = (PROPVARIANT &) aFSProps[cNewSet].vValue;
cNewSet++;
vDepths.vt = VT_VECTOR | VT_I4;
vDepths.cal.cElems = aDepths.Count();
vDepths.cal.pElems = (LONG *) aDepths.GetPointer();
}
if ( 0 != aScopes.Count() )
{
aFSProps[cNewSet].dwPropertyID = DBPROP_CI_INCLUDE_SCOPES;
aFSProps[cNewSet].colid.eKind = DBKIND_GUID_PROPID;
PROPVARIANT &vScopes = (PROPVARIANT &) aFSProps[cNewSet].vValue;
cNewSet++;
vScopes.vt = VT_VECTOR | VT_LPWSTR;
vScopes.calpwstr.cElems = aScopes.Count();
vScopes.calpwstr.pElems = (WCHAR **) aScopes.GetPointer();
}
aNewSet[0].cProperties = cNewSet;
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;
aQueryProps[0].colid.eKind = DBKIND_GUID_PROPID;
SCODE sc = oldProps.SetProperties( 2, aNewSet );
if ( FAILED( sc ) )
THROW( CException( sc ) );
} //TranslateNewPropsToOldProps
//+-------------------------------------------------------------------------
//
// Member: CRequestClient::CRequestClient, public
//
// Synopsis: Constructor for a client request
//
// Arguments: [pwcMachine] - Machine name of server
// [pDbProperties] - Client version 6 set of properties
//
// History: 16-Sep-96 dlee Created.
//
//--------------------------------------------------------------------------
CRequestClient::CRequestClient(
WCHAR const * pwcMachine,
IDBProperties * pDbProperties ) :
_fNotifyOn( FALSE ),
_fNotifyEverOn( FALSE ),
_fReadPending( FALSE ),
_pvDataTemp( 0 ),
_cbDataTemp( 0 )
{
WCHAR const * pwcMach = pwcMachine;
WCHAR const * pwcPipe = CI_PIPE_NAME;
WCHAR awcMachine[ MAX_PATH + 1 ];
WCHAR const * pwcColon = wcschr( pwcMachine, L':' );
if ( 0 != pwcColon )
{
unsigned cwc = (unsigned) ( pwcColon - pwcMachine );
if ( cwc >= MAX_PATH )
THROW( CException( E_INVALIDARG ) );
RtlCopyMemory( awcMachine, pwcMachine, sizeof( WCHAR ) * cwc );
awcMachine[ cwc ] = 0;
pwcMach = awcMachine;
pwcPipe = pwcColon + 1;
}
Init( pwcMach, pwcPipe );
// Send the pmConnect message to the server. For logging and
// debugging, send the machine and username with the message.
WCHAR awcThisMachine[ MAX_COMPUTERNAME_LENGTH + 1 ];
DWORD cwcThisMachine = sizeof awcThisMachine / sizeof WCHAR;
if ( !GetComputerName( awcThisMachine, &cwcThisMachine ) )
THROW( CException() );
WCHAR awcThisUser[ UNLEN + 1 ];
DWORD cwcThisUser = sizeof awcThisUser / sizeof WCHAR;
if ( !GetUserName( awcThisUser, &cwcThisUser ) )
THROW( CException() );
Win4Assert( 0 != pDbProperties );
//
// We know that the IDbProperties is implemented by CDbProperties.
//
CDbProperties & dbp2 = *((CDbProperties *) pDbProperties);
// Make and marshall v5 properties
XInterface<CDbProperties> xdbp( new CDbProperties() );
if ( xdbp.IsNull() )
THROW( CException( E_OUTOFMEMORY ) );
TranslateNewPropsToOldProps( xdbp.GetReference(), dbp2 );
CSizeSerStream ssSize;
xdbp->Marshall( ssSize );
ULONG cbProps = ssSize.Size();
//
// Compute the size of the dbproperties to be sent.
// The DBPROPSET_ROWSET properties aren't used on the server side in
// this form -- it's a waste to marshall/unmarshall 0x27 properties
// for no good reason. The rowset props we care about are sent in
// a compressed form when a query is issued.
//
CSizeSerStream ssSize2;
GUID guidRowsetProp = DBPROPSET_ROWSET;
dbp2.Marshall( ssSize2, 1, &guidRowsetProp );
ULONG cbProps2 = ssSize2.Size();
prxDebugOut(( DEB_ITRACE, "cb old props %d, cb new props: %d\n",
cbProps, cbProps2 ));
DWORD cbRequest = CPMConnectIn::SizeOf( awcThisMachine,
awcThisUser,
cbProps,
cbProps2 );
XArray<BYTE> xRequest( cbRequest );
CPMConnectIn *pRequest = new( xRequest.Get() )
CPMConnectIn( awcThisMachine,
awcThisUser,
IsServerRemote(),
cbProps,
cbProps2 );
//
// Serialize the DbProperties.
//
BYTE * pb = pRequest->GetBlobStartAddr();
CMemSerStream ss( pb, cbProps );
xdbp->Marshall( ss );
BYTE * pb2 = pRequest->GetBlob2StartAddr();
CMemSerStream ss2( pb2, cbProps2 );
dbp2.Marshall( ss2, 1, &guidRowsetProp );
pRequest->SetCheckSum( cbRequest );
CPMConnectOut reply;
DWORD cbReply;
DataWriteRead( pRequest,
cbRequest,
&reply,
sizeof reply,
cbReply );
Win4Assert( sizeof reply == cbReply );
_ServerVersion = reply.ServerVersion();
} //CRequestClient
//+-------------------------------------------------------------------------
//
// Member: CRequestClient::Disconnect, public
//
// Synopsis: Sends a disconnect message to the server, which will then
// do a Win32 disconnect from the pipe. After this call, the
// pipe handle can only be closed.
//
// History: 16-Sep-96 dlee Created.
//
//--------------------------------------------------------------------------
void CRequestClient::Disconnect()
{
CProxyMessage request( pmDisconnect );
DataWrite( &request, sizeof request );
} //Disconnect
//+-------------------------------------------------------------------------
//
// Member: CRequestClient::EnableNotify, public
//
// Synopsis: Tells the class that the notify thread will be doing reads
// for data threads.
//
// History: 16-Sep-96 dlee Created.
//
//--------------------------------------------------------------------------
void CRequestClient::EnableNotify()
{
CLock lock( _mutex );
prxDebugOut(( DEB_ITRACE, "enable notify\n" ));
Win4Assert( !_fNotifyOn );
_fNotifyOn = TRUE;
_fNotifyEverOn = TRUE;
prxDebugOut(( DEB_ITRACE, "enabled notify\n" ));
} //EnableNotify
//+-------------------------------------------------------------------------
//
// Member: CRequestClient::DisableNotify, public
//
// Synopsis: Tells the class that the notify thread is busy so
// data threads should wait for themselves.
//
// History: 16-Sep-96 dlee Created.
//
//--------------------------------------------------------------------------
void CRequestClient::DisableNotify()
{
CReleasableLock lock( _mutex );
prxDebugOut(( DEB_ITRACE, "disable notify\n" ));
Win4Assert( _fNotifyOn );
// If a read is pending for the notify thread to complete,
// wake the data thread up so it can wait for itself.
_fNotifyOn = FALSE;
if ( _fReadPending )
{
_pvDataTemp = 0;
_eventData.Set();
lock.Release();
_eventDataDone.Wait();
}
prxDebugOut(( DEB_ITRACE, "disabled notify\n" ));
} //DisableNotify
//+-------------------------------------------------------------------------
//
// Member: CRequestClient::DataWrite, public
//
// Synopsis: Writes data to the pipe
//
// Arguments: [pvWrite] - pointer to the buffer to be written
// [cbWrite] - # of bytes to write
//
// History: 16-Sep-96 dlee Created.
//
//--------------------------------------------------------------------------
void CRequestClient::DataWrite(
void * pvWrite,
DWORD cbWrite )
{
CLock lockData( _mutexData );
CLock lock( _mutex );
WriteSync( pvWrite, cbWrite );
} //DataWrite
//+-------------------------------------------------------------------------
//
// Member: CRequestClient::DataWriteRead, public
//
// Synopsis: Does a data (non-notification) write/read transaction with
// the server.
//
// Arguments: [pvWrite] - Buffer to be written
// [cbToWrite] - # of bytes to write
// [pvRead] - Buffer for read result
// [cbReadRequest] - Size of pvRead
// [cbRead] - Returns # of bytes read
//
// History: 16-Sep-96 dlee Created.
//
//--------------------------------------------------------------------------
void CRequestClient::DataWriteRead(
void * pvWrite,
DWORD cbWrite,
void * pvRead,
DWORD cbToRead,
DWORD & cbRead )
{
int ExpectedMsg = ((CProxyMessage *)pvWrite)->GetMessage();
prxDebugOut(( DEB_ITRACE, "DataWriteRead msg %d, cb %d\n",
ExpectedMsg, cbWrite ));
CLock lockData( _mutexData );
CReleasableLock lock( _mutex );
if ( _fNotifyOn )
{
WriteSync( pvWrite, cbWrite );
_fReadPending = TRUE;
lock.Release();
_eventData.Wait();
_fReadPending = FALSE;
prxDebugOut(( DEB_ITRACE,
"dwr eventdata triggered, _pvDataTemp: 0x%lx\n",
_pvDataTemp ));
// set this event when we fall out of scope
CEventSetter setter( _eventDataDone );
if ( 0 == _pvDataTemp )
{
// we can wait ourselves
lock.Request();
ReadSync( pvRead, cbToRead, cbRead );
prxDebugOut(( DEB_ITRACE, "dwr done, this, cb: %d\n", cbRead ));
}
else
{
// notify thread completed the read for us
Win4Assert( _cbDataTemp <= cbToRead );
RtlCopyMemory( pvRead, _pvDataTemp, _cbDataTemp );
cbRead = _cbDataTemp;
prxDebugOut(( DEB_ITRACE, "dwr done, other, cb: %d\n", cbRead ));
}
}
else
{
if ( _fNotifyEverOn )
{
// Spurious notify messages prohibit use of TransactSync. Use
// an individual Write, then Read until the appropriate msg is
// found, throwing away unwanted notification messages.
WriteSync( pvWrite, cbWrite );
do
{
// Some notification messages are larger than what we might
// be reading here, so we may need to use a temporary buffer.
void * pvReadBuffer = pvRead;
DWORD cbReadBuffer = cbToRead;
// CPMRatioFinishedOut is the largest notification msg.
// Change this code if you add a larger notification message.
Win4Assert( sizeof CPMRatioFinishedOut >=
sizeof CPMSendNotifyOut );
CPMRatioFinishedOut pmTmp;
if ( cbToRead < sizeof pmTmp )
{
pvReadBuffer = &pmTmp;
cbReadBuffer = sizeof pmTmp;
}
ReadSync( pvReadBuffer, cbReadBuffer, cbRead );
int msg = ((CProxyMessage *) pvReadBuffer)->GetMessage();
prxDebugOut(( DEB_ITRACE, "dwr done, msg %d cb: %d\n",
msg, cbRead ));
if ( ExpectedMsg == msg )
{
// Copy from the temporary buffer if it was used.
if ( pvReadBuffer != pvRead )
{
// Normal case and error case...
Win4Assert( cbToRead <= cbRead ||
sizeof CProxyMessage == cbRead );
RtlCopyMemory( pvRead, pvReadBuffer, cbRead );
}
break;
}
prxDebugOut(( DEB_WARN,
"dwr tossing spurious notify msg %d cb: %d\n",
msg, cbRead ));
} while ( TRUE );
}
else
{
TransactSync( pvWrite, cbWrite, pvRead, cbToRead, cbRead );
}
}
CProxyMessage &reply = * (CProxyMessage *) pvRead;
// If the message returned a failure code, throw it.
if ( ! SUCCEEDED( reply.GetStatus() ) )
QUIETTHROW( CException( reply.GetStatus() ) );
} //DataWriteRead
//+-------------------------------------------------------------------------
//
// Member: CRequestClient::NotifyWriteRead, public
//
// Synopsis: Does a notification (non-data) write/read transaction with
// the server. If the read is a message destined for another'
// thread, notify the thread that data is available and wait
// for another message.
//
// Arguments: [hStopNotify] - if signalled, read is cancelled
// [pvWrite] - Buffer to be written
// [cbWrite] - # of bytes to write
// [pvRead] - Buffer for read result
// [cbBuffer] - Size of pvRead
// [cbRead] - Returns # of bytes read
//
// Returns: TRUE if hStopNotify was signalled, FALSE otherwise
//
// Notes: This function knows about pmGetNotify and pmSendNotify
//
// History: 16-Sep-96 dlee Created.
//
//--------------------------------------------------------------------------
BOOL CRequestClient::NotifyWriteRead(
HANDLE hStopNotify,
void * pvWrite,
DWORD cbWrite,
void * pvRead,
DWORD cbBuffer,
DWORD & cbRead )
{
// First check if we are shutting down the query
if ( 0 == WaitForSingleObject( hStopNotify, 0 ) )
return TRUE;
// ensure notifications are disabled by the time we exit scope
Win4Assert( !_fNotifyOn );
CEnableNotify enable( *this );
Win4Assert( _fNotifyOn );
// If a pmGetNotify is sent and the reply is pmGetNotify, it means that
// notifications aren't available yet and a pmSendNotify will come later.
// If a pmSendNotify is replied, notifications were available.
int ExpectedMsg = ((CProxyMessage *)pvWrite)->GetMessage();
if ( pmGetNotify == ExpectedMsg )
ExpectedMsg = pmSendNotify;
{
CLock lock( _mutex );
WriteSync( pvWrite, cbWrite );
}
do
{
BYTE abBuf[ cbMaxProxyBuffer ];
cbRead = 0;
BOOL fStopNotify = ReadSync( abBuf,
sizeof abBuf,
cbRead,
hStopNotify );
// If we got any data, process the data even if "stop notify" is
// true, since we don't want to leave the data thread stranded.
if ( 0 != cbRead )
{
CProxyMessage &msg = * (CProxyMessage *) abBuf;
prxDebugOut(( DEB_ITRACE,
"NotifyWriteRead complete cb %d, msg %d\n",
cbRead,
msg.GetMessage() ));
// If the pmGetNotify came back, loop around and wait for
// a real notification or a message intended for a data thread.
if ( pmGetNotify == msg.GetMessage() )
{
if ( ! SUCCEEDED( msg.GetStatus() ) )
THROW( CException( msg.GetStatus() ) );
}
else
{
// If this is a notification, return so the client can be
// advised, then this function will be re-entered.
if ( msg.GetMessage() == ExpectedMsg )
{
CProxyMessage &reply = * (CProxyMessage *) pvRead;
if ( ! SUCCEEDED( msg.GetStatus() ) )
THROW( CException( msg.GetStatus() ) );
Win4Assert( cbRead <= cbBuffer );
RtlCopyMemory( pvRead, abBuf, cbRead );
return fStopNotify;
}
// Tell the thread waiting for data that it's available.
// That thread can throw if the status is failure.
_pvDataTemp = abBuf;
_cbDataTemp = cbRead;
_eventData.Set();
_eventDataDone.Wait();
}
}
if ( fStopNotify )
return TRUE;
} while ( TRUE );
return FALSE;
} //NotifyWriteRead