Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

913 lines
29 KiB

//----------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1994 - 2000
//
// File: rownotfy.cxx
//
// Contents: Rowset notification connection points
//
// Classes: CRowsetNotification
// CRowsetAsynchNotification
//
// History: 16 Feb 1998 AlanW Created from conpt.cxx
//
//----------------------------------------------------------------------------
#include "pch.cxx"
#pragma hdrstop
#include <rownotfy.hxx>
#include <query.hxx>
#include "tabledbg.hxx"
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
//////////////// CRowsetAsynchNotification methods ///////////////
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
//+-------------------------------------------------------------------------
//
// Method: CRowsetAsynchNotification::CRowsetAsynchNotification, public
//
// Synopsis: Constructor for connection point container class.
//
// Arguments: [query] -- query object with notify info
// [pRowset] -- Rowset pointer
// [ErrorObject] -- OLE-DB error object
// [fWatch] -- TRUE if watch notification to be done
//
// History: 07-Oct-1994 dlee
//
//--------------------------------------------------------------------------
CRowsetAsynchNotification::CRowsetAsynchNotification(
PQuery & query,
ULONG hCursor,
IRowset * pRowset,
CCIOleDBError & ErrorObject,
BOOL fWatch)
: _query(query),
_hCursor(hCursor),
CRowsetNotification( ),
_AsynchConnectionPoint ( IID_IDBAsynchNotify ),
_WatchConnectionPoint ( IID_IRowsetWatchNotify ),
_fDoWatch (fWatch),
_fPopulationComplete (FALSE),
_cAdvise( 0 ),
_pRowset(pRowset),
_threadNotify(0),
_threadNotifyId( 0 )
{
_AsynchConnectionPoint.SetContrUnk( (IUnknown *)this );
if (_fDoWatch)
{
_WatchConnectionPoint.SetContrUnk( (IUnknown *)this );
}
} //CRowsetAsynchNotification
//+-------------------------------------------------------------------------
//
// Method: CRowsetAsynchNotification::~CRowsetAsynchNotification, public
//
// Synopsis: Destructor for rowset watch notification class
//
// History: 07-Oct-1994 dlee
//
//--------------------------------------------------------------------------
CRowsetAsynchNotification::~CRowsetAsynchNotification()
{
Win4Assert( _cRefs == 0 && _pContainer == 0 );
Win4Assert( _cAdvise == 0 );
if ( 0 != _pContainer )
StopNotifications();
} //~CRowsetAsynchNotification
//+-------------------------------------------------------------------------
//
// Method: CRowsetAsynchNotification::AddRef, public
//
// Synopsis: Increments aggregated object ref. count.
//
// Returns: ULONG
//
// History: 07-Oct-1994 dlee
//
//--------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CRowsetAsynchNotification::AddRef()
{
return InterlockedIncrement( (long *) &_cRefs );
} //AddRef
//+-------------------------------------------------------------------------
//
// Method: CRowsetAsynchNotification::Release, public
//
// Synopsis: Decrements aggregated obj. ref. count, deletes on final release.
//
// Returns: ULONG
//
// History: 07-Oct-1994 dlee
//
//--------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CRowsetAsynchNotification::Release()
{
long cRefs = InterlockedDecrement((long *) &_cRefs);
tbDebugOut(( DEB_NOTIFY, "conpt: release, new crefs: %lx\n", _cRefs ));
// If no references, make sure container doesn't know about me anymore
if ( 0 == cRefs )
{
Win4Assert( 0 == _pContainer );
if ( 0 != _pContainer )
{
// must have gotten here through excess client release
StopNotifications();
}
delete this;
}
return cRefs;
} //Release
//+-------------------------------------------------------------------------
//
// Method: CRowsetAsynchNotification::StopNotifications
//
// Synopsis: Shuts down the notification thread (if any)
//
// History: 07-Oct-1994 dlee
//
//--------------------------------------------------------------------------
void CRowsetAsynchNotification::StopNotifications()
{
if ( GetCurrentThreadId() == _threadNotifyId )
{
Win4Assert( !"Notification thread used illegally" );
return;
}
_EndNotifyThread();
Disconnect();
_AsynchConnectionPoint.Disconnect( );
if (_fDoWatch)
_WatchConnectionPoint.Disconnect( );
}
//+-------------------------------------------------------------------------
//
// Method: CRowsetAsynchNotification::_EndNotifyThread
//
// Synopsis: Shuts down the notification thread (if any)
//
// History: 07-Oct-1994 dlee
//
//--------------------------------------------------------------------------
void CRowsetAsynchNotification::_EndNotifyThread()
{
//
// Is there a thread out there to close?
//
if (0 != _threadNotify)
{
// Signal the thread to die and wait for it to do so, or just kill
// the thread if it looks like it might be lost in client code.
tbDebugOut(( DEB_NOTIFY,
"set notify thread die event %lx\n",
_evtEndNotifyThread.GetHandle() ));
_evtEndNotifyThread.Set();
DWORD dw = WaitForSingleObject( _threadNotify, INFINITE );
BOOL fCloseWorked = CloseHandle( _threadNotify );
Win4Assert( fCloseWorked && "CloseHandle of notify thread failed" );
_threadNotify = 0;
tbDebugOut(( DEB_NOTIFY, "notify thread is history \n"));
}
} //_EndNotifyThread
//+-------------------------------------------------------------------------
//
// Method: CRowsetAsynchNotification::_StartNotifyThread
//
// Synopsis: Starts the notification thread
//
// History: 17 Mar 1998 AlanW
//
//--------------------------------------------------------------------------
inline void CRowsetAsynchNotification::_StartNotifyThread()
{
tbDebugOut(( DEB_NOTIFY, "starting rowset notify thread\n" ));
// First advise, create the thread
Win4Assert(0 == _threadNotify);
_threadNotify = CreateThread( 0, 65536,
(LPTHREAD_START_ROUTINE) _NotifyThread,
this, 0, & _threadNotifyId );
if (0 == _threadNotify)
THROW( CException() );
}
//+-------------------------------------------------------------------------
//
// Method: CRowsetAsynchNotification::_NotifyThread, private
//
// Synopsis: Entry point for notification thread
//
// Arguments: [self] -- a container to call to do the work
//
// Returns: Thread exit code
//
// Notes: this function is "static"
//
// History: 07-Oct-1994 dlee
//
//--------------------------------------------------------------------------
DWORD CRowsetAsynchNotification::_NotifyThread(
CRowsetAsynchNotification *self)
{
TRANSLATE_EXCEPTIONS;
DWORD dw = self->_DoNotifications();
UNTRANSLATE_EXCEPTIONS;
return dw;
} //_NotifyThread
//+-------------------------------------------------------------------------
//
// Method: CRowsetAsynchNotification::_DoNotifications, private
//
// Synopsis: Collects notifications and passes them out to clients.
// Loops sending notifications until event to end thread
// arrives.
//
// Returns: Thread exit code
//
// History: 07-Oct-1994 dlee
//
//--------------------------------------------------------------------------
DWORD CRowsetAsynchNotification::_DoNotifications()
{
BOOL fContinue = TRUE;
do
{
TRY
{
if (fContinue && _AsynchConnectionPoint.GetAdviseCount() )
fContinue = _DoAsynchNotification();
if (fContinue && _fDoWatch &&
_WatchConnectionPoint.GetAdviseCount() )
fContinue = _DoWatchNotification();
if (_fPopulationComplete && !_fDoWatch)
fContinue = FALSE;
}
CATCH( CException, e )
{
// don't want to 'break' out of a catch block, use variable
fContinue = FALSE;
}
END_CATCH;
// Sleep for a bit, but wake up if the thread is to go away,
ULONG x = _evtEndNotifyThread.Wait( defNotificationSleepDuration,
FALSE );
if ( STATUS_WAIT_0 == x )
fContinue = FALSE;
} while ( fContinue );
return 0;
} //_DoNotifications
//+-------------------------------------------------------------------------
//
// Function: IsLowResources
//
// Synopsis: Returns TRUE if it looks like the error is resource related.
//
// Returns: BOOL - TRUE if low on resources
//
// History: 9 May 1999 dlee
//
//--------------------------------------------------------------------------
BOOL IsLowResources( SCODE sc )
{
return E_OUTOFMEMORY == sc ||
STATUS_NO_MEMORY == sc ||
STATUS_COMMITMENT_LIMIT == sc ||
STATUS_INSUFFICIENT_RESOURCES == sc ||
HRESULT_FROM_WIN32( ERROR_COMMITMENT_LIMIT ) == sc ||
HRESULT_FROM_WIN32( ERROR_NO_SYSTEM_RESOURCES ) == sc ||
STG_E_INSUFFICIENTMEMORY == sc;
} //IsLowResources
//+-------------------------------------------------------------------------
//
// Method: CRowsetAsynchNotification::_DoAsynchNotification, private
//
// Synopsis: Collects asynch notifications and passes them out to clients.
// Tracks whether rowset population is completed, after which
// no more notifications are given (except for completion
// notifications for new advises).
//
// Returns: BOOL - TRUE if notification thread should continue
//
// History: 18 Mar 1998 AlanW
//
//--------------------------------------------------------------------------
inline DWORD MapAsynchPhaseAndOp( ULONG op, ULONG phase )
{
Win4Assert( op == 0 && phase < 4 );
return (((op+1) << 4) | (1 << phase));
}
#define ASYNC_PHASE_MASK 0x0F
#define ASYNC_OP_MASK 0xF0
BOOL CRowsetAsynchNotification::_DoAsynchNotification()
{
DBCOUNTITEM ulDenominator = 0, ulNumerator = 0;
DBCOUNTITEM cRows = 0;
BOOL fNewRows = FALSE;
SCODE sc = S_OK;
TRY
{
CNotificationSync Sync( _evtEndNotifyThread.GetHandle() );
sc = _query.RatioFinished( Sync,
_hCursor,
ulDenominator,
ulNumerator,
cRows,
fNewRows );
// Did the main thread tell this thread to go away?
if ( SUCCEEDED( sc ) )
{
Win4Assert( ulDenominator > 0 && ulNumerator <= ulDenominator );
if (fNewRows)
OnRowChange( _pRowset, 0, 0, DBREASON_ROW_ASYNCHINSERT,
DBEVENTPHASE_DIDEVENT, TRUE );
if (!_fPopulationComplete && (ulNumerator == ulDenominator))
{
OnRowsetChange( _pRowset, DBREASON_ROWSET_POPULATIONCOMPLETE,
DBEVENTPHASE_DIDEVENT, TRUE );
_fPopulationComplete = TRUE;
}
}
}
CATCH( CException, e )
{
sc = e.GetErrorCode();
}
END_CATCH;
if ( STATUS_CANCELLED == sc )
return FALSE;
BOOL fLowResources = IsLowResources( sc );
if ( !fLowResources && !SUCCEEDED( sc ) )
return FALSE;
// Give notice to all active advises.
// Need to be careful here to avoid deadlock. The enumerator
// grabs the CPC's mutex. This limits what the client can do.
ULONG ulAsynchPhase = DBASYNCHPHASE_POPULATION;
if (_fPopulationComplete)
ulAsynchPhase = DBASYNCHPHASE_COMPLETE;
ULONG dwOpMask = MapAsynchPhaseAndOp( DBASYNCHOP_OPEN, ulAsynchPhase );
CEnumConnectionsLite Enum( _AsynchConnectionPoint );
CConnectionPointBase::CConnectionContext *pConnCtx = Enum.First();
while ( 0 != pConnCtx )
{
IDBAsynchNotify *pNotifyAsynch = (IDBAsynchNotify *)(pConnCtx->_pIUnk);
tbDebugOut(( DEB_NOTIFY, "rownotfy: pinging asynch client %x\n", pNotifyAsynch ));
SCODE sc = S_OK;
if ( fLowResources )
{
//
// Let the client know we're really wedged and they might not
// get more notifications consistently.
//
sc = pNotifyAsynch->OnLowResource( 0 );
}
else
{
if ( 0 == (pConnCtx->_dwSpare & dwOpMask) )
{
sc = pNotifyAsynch->OnProgress( DB_NULL_HCHAPTER,
DBASYNCHOP_OPEN,
ulNumerator, ulDenominator,
ulAsynchPhase, 0);
}
BOOL fOnStop = FALSE;
if ( _fPopulationComplete && S_OK == sc )
{
sc = DB_S_UNWANTEDPHASE;
fOnStop = TRUE;
}
if (DB_S_UNWANTEDPHASE == sc)
pConnCtx->_dwSpare |= (dwOpMask & ASYNC_PHASE_MASK);
else if (DB_S_UNWANTEDOPERATION == sc)
pConnCtx->_dwSpare |= (dwOpMask & ASYNC_OP_MASK);
else if (E_NOTIMPL == sc)
pConnCtx->_dwSpare |= (ASYNC_PHASE_MASK | ASYNC_OP_MASK);
if ( fOnStop )
pNotifyAsynch->OnStop( DB_NULL_HCHAPTER,
DBASYNCHOP_OPEN,
S_OK,
0 );
}
tbDebugOut(( DEB_NOTIFY, "rownotfy:(end) pinging asynch client\n" ));
pConnCtx = Enum.Next();
}
// Keep trying to get notifications even if fLowResources is TRUE
return TRUE;
} //_DoAsynchNotification
//+-------------------------------------------------------------------------
//
// Method: CRowsetAsynchNotification::_DoWatchNotification, private
//
// Synopsis: Collects watch notifications and passes them out to clients.
//
// Returns: BOOL - TRUE if notification thread should continue
//
// History: 18 Mar 1998 AlanW
//
//--------------------------------------------------------------------------
BOOL CRowsetAsynchNotification::_DoWatchNotification()
{
// get notification information from the cursor
CNotificationSync Sync( _evtEndNotifyThread.GetHandle() );
DBWATCHNOTIFY changeType;
SCODE sc = _query.GetNotifications( Sync, changeType );
// Did the main thread tell this thread to go away?
if (STATUS_CANCELLED == sc)
return FALSE;
if (!SUCCEEDED(sc))
return FALSE;
// got some, give them to all active advises
// Need to be careful here to avoid deadlock. The enumerator
// grabs the CPC's mutex. We need to break out of the loop
// if the notify thread needs to go away...
tbDebugOut(( DEB_NOTIFY, "rownotfy: watch type %d\n", changeType ));
CEnumConnectionsLite Enum( _WatchConnectionPoint );
CConnectionPointBase::CConnectionContext *pConnCtx = Enum.First();
while ( pConnCtx )
{
IRowsetWatchNotify *pNotifyWatch =
(IRowsetWatchNotify *)(pConnCtx->_pIUnk);
tbDebugOut(( DEB_NOTIFY, "rownotfy: pinging watch client %x\n", pNotifyWatch ));
pNotifyWatch->OnChange( _pRowset, changeType);
tbDebugOut(( DEB_NOTIFY, "rownotfy:(end) pinging watch client\n" ));
pConnCtx = Enum.Next();
}
return TRUE;
} //_DoWatchNotification
//+-------------------------------------------------------------------------
//
// Method: CRowsetAsynchNotification::AdviseHelper, private static
//
// Synopsis: Starts the notification thread if this is the first advise.
//
// Arguments: [pHelperContext] - "this" pointer
// [pConnPt] - the connection point, either Async or Watch CP
// [pConnCtx] - pointer to connection context
//
// Notes: CPC critical section is assumed to be held.
//
// History: 17 Mar 1998 AlanW
//
//--------------------------------------------------------------------------
void CRowsetAsynchNotification::AdviseHelper( PVOID pHelperContext,
CConnectionPointBase * pConnPt,
CConnectionContext * pConnCtx )
{
CRowsetAsynchNotification * pSelf =
(CRowsetAsynchNotification *) pHelperContext;
pSelf->_cAdvise++;
if (1 == pSelf->_cAdvise)
{
// First advise, create the notification thread
pSelf->_StartNotifyThread();
Win4Assert(0 != pSelf->_threadNotify);
}
}
//+-------------------------------------------------------------------------
//
// Method: CRowsetAsynchNotification::UnadviseHelper, private static
//
// Synopsis: Stops the notification thread if this is the last active advise.
//
// Arguments: [pHelperContext] -- "this" pointer
// [pConnPt] -- the connection point, either Async or
// Watch CP
// [pConnCtx] -- pointer to connection context
// [lock] -- CPC lock
//
// Notes: CPC critical section is assumed to be held.
//
// History: 17 Mar 1998 AlanW
//
//--------------------------------------------------------------------------
void CRowsetAsynchNotification::UnadviseHelper(
PVOID pHelperContext,
CConnectionPointBase * pConnPt,
CConnectionContext * pConnCtx,
CReleasableLock & lock )
{
CRowsetAsynchNotification * pSelf =
(CRowsetAsynchNotification *) pHelperContext;
Win4Assert( pSelf->_cAdvise > 0 );
if ( --pSelf->_cAdvise == 0 )
{
// Release the lock so the notify thread has a change to end
// when we tell it to end.
lock.Release();
pSelf->_EndNotifyThread();
}
}
//+-------------------------------------------------------------------------
//
// Function: MapPhaseAndReason, inline local
//
// Synopsis: Encode IRowsetNotify phase and reason into a bit mask
// for supported phases and reasons. Luckily, most supported
// reasons have only a single phase. Multi-phase reasons take
// 5 bits.
//
// Returns: DWORD - bit mask for the reason/phase combination
//
// History: 18 Mar 1998 AlanW
//
//--------------------------------------------------------------------------
const maxSinglePhaseReasons = 8;
const maxMultiPhaseReasons = 4;
const maxPhases = 5;
const bitDidNotifyVote = 0x80000000;
inline DWORD MapPhaseAndReason( ULONG phase, ULONG reason )
{
Win4Assert( reason <= 21 && phase <= 4 );
BOOL fSinglePhase = TRUE;
DWORD dwMappedReason = 0;
switch (reason)
{
// single-phase reasons
case DBREASON_ROW_ASYNCHINSERT:
dwMappedReason = 0; break;
case DBREASON_ROWSET_RELEASE:
dwMappedReason = 1; break;
case DBREASON_ROW_ACTIVATE:
dwMappedReason = 2; break;
case DBREASON_ROW_RELEASE:
dwMappedReason = 3; break;
case DBREASON_ROWSET_POPULATIONCOMPLETE:
dwMappedReason = 4; break;
case DBREASON_ROWSET_POPULATIONSTOPPED:
dwMappedReason = 5; break;
// multi-phase reasons
case DBREASON_ROWSET_FETCHPOSITIONCHANGE:
dwMappedReason = 0; fSinglePhase = FALSE; break;
default:
tbDebugOut(( DEB_ERROR, "MapPhaseAndReason - reason: %d phase: %d\n",
reason, phase ));
Win4Assert( !"MapPhaseAndReason: unhandled reason" );
}
Win4Assert( FALSE == fSinglePhase || phase == DBEVENTPHASE_DIDEVENT );
if (fSinglePhase)
{
Win4Assert( dwMappedReason < maxSinglePhaseReasons );
dwMappedReason = (1 << dwMappedReason);
}
else
{
dwMappedReason = dwMappedReason*maxPhases + phase + maxSinglePhaseReasons;
Win4Assert( dwMappedReason <= 30 );
dwMappedReason = (1 << dwMappedReason);
}
return dwMappedReason;
}
//+-------------------------------------------------------------------------
//
// Function: MapAllPhases, inline local
//
// Synopsis: Return a bit mask that includes all bits for all supported
// phases of the encoded reason.
//
// Arguments: [dwMappedReasonAndPhase] - a bit mask returned by
// MapPhaseAndReason.
//
// Returns: DWORD - bit mask for all phases
//
// History: 18 Mar 1998 AlanW
//
//--------------------------------------------------------------------------
inline DWORD MapAllPhases( DWORD dwMappedReasonAndPhase )
{
Win4Assert( dwMappedReasonAndPhase != 0 );
if (dwMappedReasonAndPhase < (1<<maxSinglePhaseReasons))
return dwMappedReasonAndPhase;
// dwMask == 0x00001F00
DWORD dwMask = (((1<<maxPhases)-1) << maxSinglePhaseReasons);
for (unsigned i=0; i<maxMultiPhaseReasons; i++)
{
if (dwMask & dwMappedReasonAndPhase)
return (dwMappedReasonAndPhase | dwMask);
dwMask = dwMask << maxPhases;
}
Win4Assert(! "MapAllPhases - fell out of loop!" );
return 0;
}
//+-------------------------------------------------------------------------
//
// Method: IRowsetNotify::OnXxxxChange
//
// Synopsis: Dispatches CRowsetNotification notifications to clients.
//
// Returns: SCODE
//
// History: 11 Mar 1998 AlanW
//
//--------------------------------------------------------------------------
SCODE CRowsetNotification::OnFieldChange (
IRowset * pRowset,
HROW hRow,
DBORDINAL cColumns,
DBORDINAL rgColumns[],
DBREASON eReason,
DBEVENTPHASE ePhase,
BOOL fCantDeny )
{
SCODE sc = S_OK;
// NOTE: OnFieldChange is not generated by our rowsets. This code
// doesn't handle unwanted phases or reasons.
CEnumConnectionsLite Enum( *this );
CConnectionPointBase::CConnectionContext *pConnCtx = Enum.First();
while ( pConnCtx )
{
IRowsetNotify *pNotify = (IRowsetNotify *)(pConnCtx->_pIUnk);
tbDebugOut(( DEB_NOTIFY, "rownotfy: pinging notify client %x\n", pNotify ));
SCODE sc1 = pNotify->OnFieldChange( pRowset,
hRow,
cColumns,
rgColumns,
eReason,
ePhase,
fCantDeny );
tbDebugOut(( DEB_NOTIFY, "rownotfy:(end) pinging notify client\n" ));
pConnCtx = Enum.Next();
}
return sc;
}
SCODE CRowsetNotification::OnRowChange (
IRowset * pRowset,
DBCOUNTITEM cRows,
const HROW rghRows[],
DBREASON eReason,
DBEVENTPHASE ePhase,
BOOL fCantDeny )
{
// Note: we don't generate any row notifications which can be denied.
Win4Assert( fCantDeny && DBEVENTPHASE_DIDEVENT == ePhase );
DWORD dwMask = MapPhaseAndReason( ePhase, eReason );
CEnumConnectionsLite Enum( *this );
CConnectionPointBase::CConnectionContext *pConnCtx = Enum.First();
while ( pConnCtx )
{
if (0 == (pConnCtx->_dwSpare & dwMask))
{
IRowsetNotify *pNotify = (IRowsetNotify *)(pConnCtx->_pIUnk);
tbDebugOut(( DEB_NOTIFY, "rownotfy: pinging notify client %x\n", pNotify ));
SCODE sc1 = pNotify->OnRowChange( pRowset,
cRows,
rghRows,
eReason,
ePhase,
fCantDeny );
tbDebugOut(( DEB_NOTIFY, "rownotfy:(end) pinging notify client\n" ));
if (DB_S_UNWANTEDPHASE == sc1)
pConnCtx->_dwSpare |= dwMask;
else if (DB_S_UNWANTEDREASON == sc1)
pConnCtx->_dwSpare |= MapAllPhases(dwMask);
}
pConnCtx = Enum.Next();
}
return S_OK;
}
//+-------------------------------------------------------------------------
//
// Method: CRowsetNotification::DoRowsetChangeCallout, private
//
// Synopsis: Issues an OnRowsetChange notification to all active
// advises. If [fCantDeny] is FALSE, tracks which advises
// were successfully notified and checks the votes.
//
// Arguments: [Enum] - a connection context enumeration
// [pRowset] - the rowset issuing the notification
// [eReason] - the notification reason
// [ePhase] - the notification phase
// [fCantDeny] - if TRUE, notification can't be denied
//
// Notes: CPC critical section is assumed to be held.
//
// History: 08 Apr 1998 AlanW
//
//--------------------------------------------------------------------------
BOOL CRowsetNotification::DoRowsetChangeCallout (
CEnumConnectionsLite & Enum,
IRowset * pRowset,
DBREASON eReason,
DBEVENTPHASE ePhase,
BOOL fCantDeny )
{
DWORD dwMask = MapPhaseAndReason( ePhase, eReason );
CConnectionPointBase::CConnectionContext *pConnCtx;
BOOL fVeto = FALSE;
for ( pConnCtx = Enum.First(); pConnCtx; pConnCtx = Enum.Next() )
{
if ( 0 == (pConnCtx->_dwSpare & dwMask) )
{
IRowsetNotify *pNotify = (IRowsetNotify *)(pConnCtx->_pIUnk);
tbDebugOut(( DEB_NOTIFY, "rownotfy: pinging notify client %x\n", pNotify ));
SCODE sc1 = pNotify->OnRowsetChange( pRowset,
eReason,
ePhase,
fCantDeny );
if ( !fCantDeny )
{
if (S_FALSE == sc1)
{
tbDebugOut(( DEB_NOTIFY, "rownotfy: notify client veto'ed\n" ));
fVeto = TRUE;
break;
}
pConnCtx->_dwSpare |= bitDidNotifyVote;
}
if (DB_S_UNWANTEDPHASE == sc1)
pConnCtx->_dwSpare |= dwMask;
else if (DB_S_UNWANTEDREASON == sc1)
pConnCtx->_dwSpare |= MapAllPhases(dwMask);
tbDebugOut(( DEB_NOTIFY, "rownotfy:(end) pinging notify client\n" ));
}
}
return fVeto;
}
SCODE CRowsetNotification::OnRowsetChange (
IRowset * pRowset,
DBREASON eReason,
DBEVENTPHASE ePhase,
BOOL fCantDeny )
{
// Note: we don't use SYNCHAFTER, and ABOUTTODO is done here after voting
Win4Assert( DBEVENTPHASE_ABOUTTODO != ePhase &&
DBEVENTPHASE_SYNCHAFTER != ePhase );
BOOL fVeto = FALSE;
CEnumConnectionsLite Enum( *this );
if ( DBEVENTPHASE_OKTODO == ePhase )
{
Win4Assert( !fCantDeny );
fVeto = DoRowsetChangeCallout( Enum, pRowset, eReason,
DBEVENTPHASE_OKTODO, FALSE );
if ( ! fVeto )
fVeto = DoRowsetChangeCallout( Enum, pRowset, eReason,
DBEVENTPHASE_ABOUTTODO, FALSE );
DWORD dwMask = MapPhaseAndReason( DBEVENTPHASE_FAILEDTODO, eReason );
CConnectionPointBase::CConnectionContext *pConnCtx;
for ( pConnCtx = Enum.First(); pConnCtx; pConnCtx = Enum.Next() )
{
if (fVeto &&
0 == (pConnCtx->_dwSpare & dwMask) &&
0 != (pConnCtx->_dwSpare & bitDidNotifyVote) )
{
IRowsetNotify *pNotify = (IRowsetNotify *)(pConnCtx->_pIUnk);
tbDebugOut(( DEB_NOTIFY, "rownotfy: pinging notify client %x for FAIL\n", pNotify ));
SCODE sc1 = pNotify->OnRowsetChange( pRowset,
eReason,
DBEVENTPHASE_FAILEDTODO,
TRUE );
tbDebugOut(( DEB_NOTIFY, "rownotfy:(end) pinging notify client for FAIL\n" ));
if (DB_S_UNWANTEDPHASE == sc1)
pConnCtx->_dwSpare |= dwMask;
else if (DB_S_UNWANTEDREASON == sc1)
pConnCtx->_dwSpare |= MapAllPhases(dwMask);
}
pConnCtx->_dwSpare &= ~bitDidNotifyVote;
}
}
else
{
// a non-vetoable notification; just send the notifies
Win4Assert( fCantDeny );
DoRowsetChangeCallout( Enum, pRowset, eReason, ePhase, TRUE );
}
return fVeto ? S_FALSE : S_OK;
}