//+------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1996 - 2001 // // File: crequest.cxx // // Contents: Client side of catalog/query requests // // Classes: CPipeClient // CRequestClient // // History: 16-Sep-96 dlee Created. // //-------------------------------------------------------------------------- #include #pragma hdrstop #include #include #include #include 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 %#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: %#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 %#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 %#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 read 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 %#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 %#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. // V5 props are used for IS 2.0. // // 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 aDepths(2); CDynArrayInPlace 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: %#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 ) ) { // // For some configurations of asp.net the context in which the // thread runs doesn't have access to the user name. Fail this // case silently since the username is optional and only used for // logging. // if ( ERROR_ACCESS_DENIED == GetLastError() ) { awcThisUser[0] = 0; cwcThisUser = 0; } else THROW( CException() ); } Win4Assert( 0 != pDbProperties ); // // We know that the IDbProperties is implemented by CDbProperties. // CDbProperties & dbp2 = *((CDbProperties *) pDbProperties); // Make and marshall v5 properties XInterface 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 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: %p\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