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

  1. //----------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1994 - 2000
  5. //
  6. // File: rownotfy.cxx
  7. //
  8. // Contents: Rowset notification connection points
  9. //
  10. // Classes: CRowsetNotification
  11. // CRowsetAsynchNotification
  12. //
  13. // History: 16 Feb 1998 AlanW Created from conpt.cxx
  14. //
  15. //----------------------------------------------------------------------------
  16. #include "pch.cxx"
  17. #pragma hdrstop
  18. #include <rownotfy.hxx>
  19. #include <query.hxx>
  20. #include "tabledbg.hxx"
  21. /////////////////////////////////////////////////////////////////
  22. /////////////////////////////////////////////////////////////////
  23. //////////////// CRowsetAsynchNotification methods ///////////////
  24. /////////////////////////////////////////////////////////////////
  25. /////////////////////////////////////////////////////////////////
  26. //+-------------------------------------------------------------------------
  27. //
  28. // Method: CRowsetAsynchNotification::CRowsetAsynchNotification, public
  29. //
  30. // Synopsis: Constructor for connection point container class.
  31. //
  32. // Arguments: [query] -- query object with notify info
  33. // [pRowset] -- Rowset pointer
  34. // [ErrorObject] -- OLE-DB error object
  35. // [fWatch] -- TRUE if watch notification to be done
  36. //
  37. // History: 07-Oct-1994 dlee
  38. //
  39. //--------------------------------------------------------------------------
  40. CRowsetAsynchNotification::CRowsetAsynchNotification(
  41. PQuery & query,
  42. ULONG hCursor,
  43. IRowset * pRowset,
  44. CCIOleDBError & ErrorObject,
  45. BOOL fWatch)
  46. : _query(query),
  47. _hCursor(hCursor),
  48. CRowsetNotification( ),
  49. _AsynchConnectionPoint ( IID_IDBAsynchNotify ),
  50. _WatchConnectionPoint ( IID_IRowsetWatchNotify ),
  51. _fDoWatch (fWatch),
  52. _fPopulationComplete (FALSE),
  53. _cAdvise( 0 ),
  54. _pRowset(pRowset),
  55. _threadNotify(0),
  56. _threadNotifyId( 0 )
  57. {
  58. _AsynchConnectionPoint.SetContrUnk( (IUnknown *)this );
  59. if (_fDoWatch)
  60. {
  61. _WatchConnectionPoint.SetContrUnk( (IUnknown *)this );
  62. }
  63. } //CRowsetAsynchNotification
  64. //+-------------------------------------------------------------------------
  65. //
  66. // Method: CRowsetAsynchNotification::~CRowsetAsynchNotification, public
  67. //
  68. // Synopsis: Destructor for rowset watch notification class
  69. //
  70. // History: 07-Oct-1994 dlee
  71. //
  72. //--------------------------------------------------------------------------
  73. CRowsetAsynchNotification::~CRowsetAsynchNotification()
  74. {
  75. Win4Assert( _cRefs == 0 && _pContainer == 0 );
  76. Win4Assert( _cAdvise == 0 );
  77. if ( 0 != _pContainer )
  78. StopNotifications();
  79. } //~CRowsetAsynchNotification
  80. //+-------------------------------------------------------------------------
  81. //
  82. // Method: CRowsetAsynchNotification::AddRef, public
  83. //
  84. // Synopsis: Increments aggregated object ref. count.
  85. //
  86. // Returns: ULONG
  87. //
  88. // History: 07-Oct-1994 dlee
  89. //
  90. //--------------------------------------------------------------------------
  91. STDMETHODIMP_(ULONG) CRowsetAsynchNotification::AddRef()
  92. {
  93. return InterlockedIncrement( (long *) &_cRefs );
  94. } //AddRef
  95. //+-------------------------------------------------------------------------
  96. //
  97. // Method: CRowsetAsynchNotification::Release, public
  98. //
  99. // Synopsis: Decrements aggregated obj. ref. count, deletes on final release.
  100. //
  101. // Returns: ULONG
  102. //
  103. // History: 07-Oct-1994 dlee
  104. //
  105. //--------------------------------------------------------------------------
  106. STDMETHODIMP_(ULONG) CRowsetAsynchNotification::Release()
  107. {
  108. long cRefs = InterlockedDecrement((long *) &_cRefs);
  109. tbDebugOut(( DEB_NOTIFY, "conpt: release, new crefs: %lx\n", _cRefs ));
  110. // If no references, make sure container doesn't know about me anymore
  111. if ( 0 == cRefs )
  112. {
  113. Win4Assert( 0 == _pContainer );
  114. if ( 0 != _pContainer )
  115. {
  116. // must have gotten here through excess client release
  117. StopNotifications();
  118. }
  119. delete this;
  120. }
  121. return cRefs;
  122. } //Release
  123. //+-------------------------------------------------------------------------
  124. //
  125. // Method: CRowsetAsynchNotification::StopNotifications
  126. //
  127. // Synopsis: Shuts down the notification thread (if any)
  128. //
  129. // History: 07-Oct-1994 dlee
  130. //
  131. //--------------------------------------------------------------------------
  132. void CRowsetAsynchNotification::StopNotifications()
  133. {
  134. if ( GetCurrentThreadId() == _threadNotifyId )
  135. {
  136. Win4Assert( !"Notification thread used illegally" );
  137. return;
  138. }
  139. _EndNotifyThread();
  140. Disconnect();
  141. _AsynchConnectionPoint.Disconnect( );
  142. if (_fDoWatch)
  143. _WatchConnectionPoint.Disconnect( );
  144. }
  145. //+-------------------------------------------------------------------------
  146. //
  147. // Method: CRowsetAsynchNotification::_EndNotifyThread
  148. //
  149. // Synopsis: Shuts down the notification thread (if any)
  150. //
  151. // History: 07-Oct-1994 dlee
  152. //
  153. //--------------------------------------------------------------------------
  154. void CRowsetAsynchNotification::_EndNotifyThread()
  155. {
  156. //
  157. // Is there a thread out there to close?
  158. //
  159. if (0 != _threadNotify)
  160. {
  161. // Signal the thread to die and wait for it to do so, or just kill
  162. // the thread if it looks like it might be lost in client code.
  163. tbDebugOut(( DEB_NOTIFY,
  164. "set notify thread die event %lx\n",
  165. _evtEndNotifyThread.GetHandle() ));
  166. _evtEndNotifyThread.Set();
  167. DWORD dw = WaitForSingleObject( _threadNotify, INFINITE );
  168. BOOL fCloseWorked = CloseHandle( _threadNotify );
  169. Win4Assert( fCloseWorked && "CloseHandle of notify thread failed" );
  170. _threadNotify = 0;
  171. tbDebugOut(( DEB_NOTIFY, "notify thread is history \n"));
  172. }
  173. } //_EndNotifyThread
  174. //+-------------------------------------------------------------------------
  175. //
  176. // Method: CRowsetAsynchNotification::_StartNotifyThread
  177. //
  178. // Synopsis: Starts the notification thread
  179. //
  180. // History: 17 Mar 1998 AlanW
  181. //
  182. //--------------------------------------------------------------------------
  183. inline void CRowsetAsynchNotification::_StartNotifyThread()
  184. {
  185. tbDebugOut(( DEB_NOTIFY, "starting rowset notify thread\n" ));
  186. // First advise, create the thread
  187. Win4Assert(0 == _threadNotify);
  188. _threadNotify = CreateThread( 0, 65536,
  189. (LPTHREAD_START_ROUTINE) _NotifyThread,
  190. this, 0, & _threadNotifyId );
  191. if (0 == _threadNotify)
  192. THROW( CException() );
  193. }
  194. //+-------------------------------------------------------------------------
  195. //
  196. // Method: CRowsetAsynchNotification::_NotifyThread, private
  197. //
  198. // Synopsis: Entry point for notification thread
  199. //
  200. // Arguments: [self] -- a container to call to do the work
  201. //
  202. // Returns: Thread exit code
  203. //
  204. // Notes: this function is "static"
  205. //
  206. // History: 07-Oct-1994 dlee
  207. //
  208. //--------------------------------------------------------------------------
  209. DWORD CRowsetAsynchNotification::_NotifyThread(
  210. CRowsetAsynchNotification *self)
  211. {
  212. TRANSLATE_EXCEPTIONS;
  213. DWORD dw = self->_DoNotifications();
  214. UNTRANSLATE_EXCEPTIONS;
  215. return dw;
  216. } //_NotifyThread
  217. //+-------------------------------------------------------------------------
  218. //
  219. // Method: CRowsetAsynchNotification::_DoNotifications, private
  220. //
  221. // Synopsis: Collects notifications and passes them out to clients.
  222. // Loops sending notifications until event to end thread
  223. // arrives.
  224. //
  225. // Returns: Thread exit code
  226. //
  227. // History: 07-Oct-1994 dlee
  228. //
  229. //--------------------------------------------------------------------------
  230. DWORD CRowsetAsynchNotification::_DoNotifications()
  231. {
  232. BOOL fContinue = TRUE;
  233. do
  234. {
  235. TRY
  236. {
  237. if (fContinue && _AsynchConnectionPoint.GetAdviseCount() )
  238. fContinue = _DoAsynchNotification();
  239. if (fContinue && _fDoWatch &&
  240. _WatchConnectionPoint.GetAdviseCount() )
  241. fContinue = _DoWatchNotification();
  242. if (_fPopulationComplete && !_fDoWatch)
  243. fContinue = FALSE;
  244. }
  245. CATCH( CException, e )
  246. {
  247. // don't want to 'break' out of a catch block, use variable
  248. fContinue = FALSE;
  249. }
  250. END_CATCH;
  251. // Sleep for a bit, but wake up if the thread is to go away,
  252. ULONG x = _evtEndNotifyThread.Wait( defNotificationSleepDuration,
  253. FALSE );
  254. if ( STATUS_WAIT_0 == x )
  255. fContinue = FALSE;
  256. } while ( fContinue );
  257. return 0;
  258. } //_DoNotifications
  259. //+-------------------------------------------------------------------------
  260. //
  261. // Function: IsLowResources
  262. //
  263. // Synopsis: Returns TRUE if it looks like the error is resource related.
  264. //
  265. // Returns: BOOL - TRUE if low on resources
  266. //
  267. // History: 9 May 1999 dlee
  268. //
  269. //--------------------------------------------------------------------------
  270. BOOL IsLowResources( SCODE sc )
  271. {
  272. return E_OUTOFMEMORY == sc ||
  273. STATUS_NO_MEMORY == sc ||
  274. STATUS_COMMITMENT_LIMIT == sc ||
  275. STATUS_INSUFFICIENT_RESOURCES == sc ||
  276. HRESULT_FROM_WIN32( ERROR_COMMITMENT_LIMIT ) == sc ||
  277. HRESULT_FROM_WIN32( ERROR_NO_SYSTEM_RESOURCES ) == sc ||
  278. STG_E_INSUFFICIENTMEMORY == sc;
  279. } //IsLowResources
  280. //+-------------------------------------------------------------------------
  281. //
  282. // Method: CRowsetAsynchNotification::_DoAsynchNotification, private
  283. //
  284. // Synopsis: Collects asynch notifications and passes them out to clients.
  285. // Tracks whether rowset population is completed, after which
  286. // no more notifications are given (except for completion
  287. // notifications for new advises).
  288. //
  289. // Returns: BOOL - TRUE if notification thread should continue
  290. //
  291. // History: 18 Mar 1998 AlanW
  292. //
  293. //--------------------------------------------------------------------------
  294. inline DWORD MapAsynchPhaseAndOp( ULONG op, ULONG phase )
  295. {
  296. Win4Assert( op == 0 && phase < 4 );
  297. return (((op+1) << 4) | (1 << phase));
  298. }
  299. #define ASYNC_PHASE_MASK 0x0F
  300. #define ASYNC_OP_MASK 0xF0
  301. BOOL CRowsetAsynchNotification::_DoAsynchNotification()
  302. {
  303. DBCOUNTITEM ulDenominator = 0, ulNumerator = 0;
  304. DBCOUNTITEM cRows = 0;
  305. BOOL fNewRows = FALSE;
  306. SCODE sc = S_OK;
  307. TRY
  308. {
  309. CNotificationSync Sync( _evtEndNotifyThread.GetHandle() );
  310. sc = _query.RatioFinished( Sync,
  311. _hCursor,
  312. ulDenominator,
  313. ulNumerator,
  314. cRows,
  315. fNewRows );
  316. // Did the main thread tell this thread to go away?
  317. if ( SUCCEEDED( sc ) )
  318. {
  319. Win4Assert( ulDenominator > 0 && ulNumerator <= ulDenominator );
  320. if (fNewRows)
  321. OnRowChange( _pRowset, 0, 0, DBREASON_ROW_ASYNCHINSERT,
  322. DBEVENTPHASE_DIDEVENT, TRUE );
  323. if (!_fPopulationComplete && (ulNumerator == ulDenominator))
  324. {
  325. OnRowsetChange( _pRowset, DBREASON_ROWSET_POPULATIONCOMPLETE,
  326. DBEVENTPHASE_DIDEVENT, TRUE );
  327. _fPopulationComplete = TRUE;
  328. }
  329. }
  330. }
  331. CATCH( CException, e )
  332. {
  333. sc = e.GetErrorCode();
  334. }
  335. END_CATCH;
  336. if ( STATUS_CANCELLED == sc )
  337. return FALSE;
  338. BOOL fLowResources = IsLowResources( sc );
  339. if ( !fLowResources && !SUCCEEDED( sc ) )
  340. return FALSE;
  341. // Give notice to all active advises.
  342. // Need to be careful here to avoid deadlock. The enumerator
  343. // grabs the CPC's mutex. This limits what the client can do.
  344. ULONG ulAsynchPhase = DBASYNCHPHASE_POPULATION;
  345. if (_fPopulationComplete)
  346. ulAsynchPhase = DBASYNCHPHASE_COMPLETE;
  347. ULONG dwOpMask = MapAsynchPhaseAndOp( DBASYNCHOP_OPEN, ulAsynchPhase );
  348. CEnumConnectionsLite Enum( _AsynchConnectionPoint );
  349. CConnectionPointBase::CConnectionContext *pConnCtx = Enum.First();
  350. while ( 0 != pConnCtx )
  351. {
  352. IDBAsynchNotify *pNotifyAsynch = (IDBAsynchNotify *)(pConnCtx->_pIUnk);
  353. tbDebugOut(( DEB_NOTIFY, "rownotfy: pinging asynch client %x\n", pNotifyAsynch ));
  354. SCODE sc = S_OK;
  355. if ( fLowResources )
  356. {
  357. //
  358. // Let the client know we're really wedged and they might not
  359. // get more notifications consistently.
  360. //
  361. sc = pNotifyAsynch->OnLowResource( 0 );
  362. }
  363. else
  364. {
  365. if ( 0 == (pConnCtx->_dwSpare & dwOpMask) )
  366. {
  367. sc = pNotifyAsynch->OnProgress( DB_NULL_HCHAPTER,
  368. DBASYNCHOP_OPEN,
  369. ulNumerator, ulDenominator,
  370. ulAsynchPhase, 0);
  371. }
  372. BOOL fOnStop = FALSE;
  373. if ( _fPopulationComplete && S_OK == sc )
  374. {
  375. sc = DB_S_UNWANTEDPHASE;
  376. fOnStop = TRUE;
  377. }
  378. if (DB_S_UNWANTEDPHASE == sc)
  379. pConnCtx->_dwSpare |= (dwOpMask & ASYNC_PHASE_MASK);
  380. else if (DB_S_UNWANTEDOPERATION == sc)
  381. pConnCtx->_dwSpare |= (dwOpMask & ASYNC_OP_MASK);
  382. else if (E_NOTIMPL == sc)
  383. pConnCtx->_dwSpare |= (ASYNC_PHASE_MASK | ASYNC_OP_MASK);
  384. if ( fOnStop )
  385. pNotifyAsynch->OnStop( DB_NULL_HCHAPTER,
  386. DBASYNCHOP_OPEN,
  387. S_OK,
  388. 0 );
  389. }
  390. tbDebugOut(( DEB_NOTIFY, "rownotfy:(end) pinging asynch client\n" ));
  391. pConnCtx = Enum.Next();
  392. }
  393. // Keep trying to get notifications even if fLowResources is TRUE
  394. return TRUE;
  395. } //_DoAsynchNotification
  396. //+-------------------------------------------------------------------------
  397. //
  398. // Method: CRowsetAsynchNotification::_DoWatchNotification, private
  399. //
  400. // Synopsis: Collects watch notifications and passes them out to clients.
  401. //
  402. // Returns: BOOL - TRUE if notification thread should continue
  403. //
  404. // History: 18 Mar 1998 AlanW
  405. //
  406. //--------------------------------------------------------------------------
  407. BOOL CRowsetAsynchNotification::_DoWatchNotification()
  408. {
  409. // get notification information from the cursor
  410. CNotificationSync Sync( _evtEndNotifyThread.GetHandle() );
  411. DBWATCHNOTIFY changeType;
  412. SCODE sc = _query.GetNotifications( Sync, changeType );
  413. // Did the main thread tell this thread to go away?
  414. if (STATUS_CANCELLED == sc)
  415. return FALSE;
  416. if (!SUCCEEDED(sc))
  417. return FALSE;
  418. // got some, give them to all active advises
  419. // Need to be careful here to avoid deadlock. The enumerator
  420. // grabs the CPC's mutex. We need to break out of the loop
  421. // if the notify thread needs to go away...
  422. tbDebugOut(( DEB_NOTIFY, "rownotfy: watch type %d\n", changeType ));
  423. CEnumConnectionsLite Enum( _WatchConnectionPoint );
  424. CConnectionPointBase::CConnectionContext *pConnCtx = Enum.First();
  425. while ( pConnCtx )
  426. {
  427. IRowsetWatchNotify *pNotifyWatch =
  428. (IRowsetWatchNotify *)(pConnCtx->_pIUnk);
  429. tbDebugOut(( DEB_NOTIFY, "rownotfy: pinging watch client %x\n", pNotifyWatch ));
  430. pNotifyWatch->OnChange( _pRowset, changeType);
  431. tbDebugOut(( DEB_NOTIFY, "rownotfy:(end) pinging watch client\n" ));
  432. pConnCtx = Enum.Next();
  433. }
  434. return TRUE;
  435. } //_DoWatchNotification
  436. //+-------------------------------------------------------------------------
  437. //
  438. // Method: CRowsetAsynchNotification::AdviseHelper, private static
  439. //
  440. // Synopsis: Starts the notification thread if this is the first advise.
  441. //
  442. // Arguments: [pHelperContext] - "this" pointer
  443. // [pConnPt] - the connection point, either Async or Watch CP
  444. // [pConnCtx] - pointer to connection context
  445. //
  446. // Notes: CPC critical section is assumed to be held.
  447. //
  448. // History: 17 Mar 1998 AlanW
  449. //
  450. //--------------------------------------------------------------------------
  451. void CRowsetAsynchNotification::AdviseHelper( PVOID pHelperContext,
  452. CConnectionPointBase * pConnPt,
  453. CConnectionContext * pConnCtx )
  454. {
  455. CRowsetAsynchNotification * pSelf =
  456. (CRowsetAsynchNotification *) pHelperContext;
  457. pSelf->_cAdvise++;
  458. if (1 == pSelf->_cAdvise)
  459. {
  460. // First advise, create the notification thread
  461. pSelf->_StartNotifyThread();
  462. Win4Assert(0 != pSelf->_threadNotify);
  463. }
  464. }
  465. //+-------------------------------------------------------------------------
  466. //
  467. // Method: CRowsetAsynchNotification::UnadviseHelper, private static
  468. //
  469. // Synopsis: Stops the notification thread if this is the last active advise.
  470. //
  471. // Arguments: [pHelperContext] -- "this" pointer
  472. // [pConnPt] -- the connection point, either Async or
  473. // Watch CP
  474. // [pConnCtx] -- pointer to connection context
  475. // [lock] -- CPC lock
  476. //
  477. // Notes: CPC critical section is assumed to be held.
  478. //
  479. // History: 17 Mar 1998 AlanW
  480. //
  481. //--------------------------------------------------------------------------
  482. void CRowsetAsynchNotification::UnadviseHelper(
  483. PVOID pHelperContext,
  484. CConnectionPointBase * pConnPt,
  485. CConnectionContext * pConnCtx,
  486. CReleasableLock & lock )
  487. {
  488. CRowsetAsynchNotification * pSelf =
  489. (CRowsetAsynchNotification *) pHelperContext;
  490. Win4Assert( pSelf->_cAdvise > 0 );
  491. if ( --pSelf->_cAdvise == 0 )
  492. {
  493. // Release the lock so the notify thread has a change to end
  494. // when we tell it to end.
  495. lock.Release();
  496. pSelf->_EndNotifyThread();
  497. }
  498. }
  499. //+-------------------------------------------------------------------------
  500. //
  501. // Function: MapPhaseAndReason, inline local
  502. //
  503. // Synopsis: Encode IRowsetNotify phase and reason into a bit mask
  504. // for supported phases and reasons. Luckily, most supported
  505. // reasons have only a single phase. Multi-phase reasons take
  506. // 5 bits.
  507. //
  508. // Returns: DWORD - bit mask for the reason/phase combination
  509. //
  510. // History: 18 Mar 1998 AlanW
  511. //
  512. //--------------------------------------------------------------------------
  513. const maxSinglePhaseReasons = 8;
  514. const maxMultiPhaseReasons = 4;
  515. const maxPhases = 5;
  516. const bitDidNotifyVote = 0x80000000;
  517. inline DWORD MapPhaseAndReason( ULONG phase, ULONG reason )
  518. {
  519. Win4Assert( reason <= 21 && phase <= 4 );
  520. BOOL fSinglePhase = TRUE;
  521. DWORD dwMappedReason = 0;
  522. switch (reason)
  523. {
  524. // single-phase reasons
  525. case DBREASON_ROW_ASYNCHINSERT:
  526. dwMappedReason = 0; break;
  527. case DBREASON_ROWSET_RELEASE:
  528. dwMappedReason = 1; break;
  529. case DBREASON_ROW_ACTIVATE:
  530. dwMappedReason = 2; break;
  531. case DBREASON_ROW_RELEASE:
  532. dwMappedReason = 3; break;
  533. case DBREASON_ROWSET_POPULATIONCOMPLETE:
  534. dwMappedReason = 4; break;
  535. case DBREASON_ROWSET_POPULATIONSTOPPED:
  536. dwMappedReason = 5; break;
  537. // multi-phase reasons
  538. case DBREASON_ROWSET_FETCHPOSITIONCHANGE:
  539. dwMappedReason = 0; fSinglePhase = FALSE; break;
  540. default:
  541. tbDebugOut(( DEB_ERROR, "MapPhaseAndReason - reason: %d phase: %d\n",
  542. reason, phase ));
  543. Win4Assert( !"MapPhaseAndReason: unhandled reason" );
  544. }
  545. Win4Assert( FALSE == fSinglePhase || phase == DBEVENTPHASE_DIDEVENT );
  546. if (fSinglePhase)
  547. {
  548. Win4Assert( dwMappedReason < maxSinglePhaseReasons );
  549. dwMappedReason = (1 << dwMappedReason);
  550. }
  551. else
  552. {
  553. dwMappedReason = dwMappedReason*maxPhases + phase + maxSinglePhaseReasons;
  554. Win4Assert( dwMappedReason <= 30 );
  555. dwMappedReason = (1 << dwMappedReason);
  556. }
  557. return dwMappedReason;
  558. }
  559. //+-------------------------------------------------------------------------
  560. //
  561. // Function: MapAllPhases, inline local
  562. //
  563. // Synopsis: Return a bit mask that includes all bits for all supported
  564. // phases of the encoded reason.
  565. //
  566. // Arguments: [dwMappedReasonAndPhase] - a bit mask returned by
  567. // MapPhaseAndReason.
  568. //
  569. // Returns: DWORD - bit mask for all phases
  570. //
  571. // History: 18 Mar 1998 AlanW
  572. //
  573. //--------------------------------------------------------------------------
  574. inline DWORD MapAllPhases( DWORD dwMappedReasonAndPhase )
  575. {
  576. Win4Assert( dwMappedReasonAndPhase != 0 );
  577. if (dwMappedReasonAndPhase < (1<<maxSinglePhaseReasons))
  578. return dwMappedReasonAndPhase;
  579. // dwMask == 0x00001F00
  580. DWORD dwMask = (((1<<maxPhases)-1) << maxSinglePhaseReasons);
  581. for (unsigned i=0; i<maxMultiPhaseReasons; i++)
  582. {
  583. if (dwMask & dwMappedReasonAndPhase)
  584. return (dwMappedReasonAndPhase | dwMask);
  585. dwMask = dwMask << maxPhases;
  586. }
  587. Win4Assert(! "MapAllPhases - fell out of loop!" );
  588. return 0;
  589. }
  590. //+-------------------------------------------------------------------------
  591. //
  592. // Method: IRowsetNotify::OnXxxxChange
  593. //
  594. // Synopsis: Dispatches CRowsetNotification notifications to clients.
  595. //
  596. // Returns: SCODE
  597. //
  598. // History: 11 Mar 1998 AlanW
  599. //
  600. //--------------------------------------------------------------------------
  601. SCODE CRowsetNotification::OnFieldChange (
  602. IRowset * pRowset,
  603. HROW hRow,
  604. DBORDINAL cColumns,
  605. DBORDINAL rgColumns[],
  606. DBREASON eReason,
  607. DBEVENTPHASE ePhase,
  608. BOOL fCantDeny )
  609. {
  610. SCODE sc = S_OK;
  611. // NOTE: OnFieldChange is not generated by our rowsets. This code
  612. // doesn't handle unwanted phases or reasons.
  613. CEnumConnectionsLite Enum( *this );
  614. CConnectionPointBase::CConnectionContext *pConnCtx = Enum.First();
  615. while ( pConnCtx )
  616. {
  617. IRowsetNotify *pNotify = (IRowsetNotify *)(pConnCtx->_pIUnk);
  618. tbDebugOut(( DEB_NOTIFY, "rownotfy: pinging notify client %x\n", pNotify ));
  619. SCODE sc1 = pNotify->OnFieldChange( pRowset,
  620. hRow,
  621. cColumns,
  622. rgColumns,
  623. eReason,
  624. ePhase,
  625. fCantDeny );
  626. tbDebugOut(( DEB_NOTIFY, "rownotfy:(end) pinging notify client\n" ));
  627. pConnCtx = Enum.Next();
  628. }
  629. return sc;
  630. }
  631. SCODE CRowsetNotification::OnRowChange (
  632. IRowset * pRowset,
  633. DBCOUNTITEM cRows,
  634. const HROW rghRows[],
  635. DBREASON eReason,
  636. DBEVENTPHASE ePhase,
  637. BOOL fCantDeny )
  638. {
  639. // Note: we don't generate any row notifications which can be denied.
  640. Win4Assert( fCantDeny && DBEVENTPHASE_DIDEVENT == ePhase );
  641. DWORD dwMask = MapPhaseAndReason( ePhase, eReason );
  642. CEnumConnectionsLite Enum( *this );
  643. CConnectionPointBase::CConnectionContext *pConnCtx = Enum.First();
  644. while ( pConnCtx )
  645. {
  646. if (0 == (pConnCtx->_dwSpare & dwMask))
  647. {
  648. IRowsetNotify *pNotify = (IRowsetNotify *)(pConnCtx->_pIUnk);
  649. tbDebugOut(( DEB_NOTIFY, "rownotfy: pinging notify client %x\n", pNotify ));
  650. SCODE sc1 = pNotify->OnRowChange( pRowset,
  651. cRows,
  652. rghRows,
  653. eReason,
  654. ePhase,
  655. fCantDeny );
  656. tbDebugOut(( DEB_NOTIFY, "rownotfy:(end) pinging notify client\n" ));
  657. if (DB_S_UNWANTEDPHASE == sc1)
  658. pConnCtx->_dwSpare |= dwMask;
  659. else if (DB_S_UNWANTEDREASON == sc1)
  660. pConnCtx->_dwSpare |= MapAllPhases(dwMask);
  661. }
  662. pConnCtx = Enum.Next();
  663. }
  664. return S_OK;
  665. }
  666. //+-------------------------------------------------------------------------
  667. //
  668. // Method: CRowsetNotification::DoRowsetChangeCallout, private
  669. //
  670. // Synopsis: Issues an OnRowsetChange notification to all active
  671. // advises. If [fCantDeny] is FALSE, tracks which advises
  672. // were successfully notified and checks the votes.
  673. //
  674. // Arguments: [Enum] - a connection context enumeration
  675. // [pRowset] - the rowset issuing the notification
  676. // [eReason] - the notification reason
  677. // [ePhase] - the notification phase
  678. // [fCantDeny] - if TRUE, notification can't be denied
  679. //
  680. // Notes: CPC critical section is assumed to be held.
  681. //
  682. // History: 08 Apr 1998 AlanW
  683. //
  684. //--------------------------------------------------------------------------
  685. BOOL CRowsetNotification::DoRowsetChangeCallout (
  686. CEnumConnectionsLite & Enum,
  687. IRowset * pRowset,
  688. DBREASON eReason,
  689. DBEVENTPHASE ePhase,
  690. BOOL fCantDeny )
  691. {
  692. DWORD dwMask = MapPhaseAndReason( ePhase, eReason );
  693. CConnectionPointBase::CConnectionContext *pConnCtx;
  694. BOOL fVeto = FALSE;
  695. for ( pConnCtx = Enum.First(); pConnCtx; pConnCtx = Enum.Next() )
  696. {
  697. if ( 0 == (pConnCtx->_dwSpare & dwMask) )
  698. {
  699. IRowsetNotify *pNotify = (IRowsetNotify *)(pConnCtx->_pIUnk);
  700. tbDebugOut(( DEB_NOTIFY, "rownotfy: pinging notify client %x\n", pNotify ));
  701. SCODE sc1 = pNotify->OnRowsetChange( pRowset,
  702. eReason,
  703. ePhase,
  704. fCantDeny );
  705. if ( !fCantDeny )
  706. {
  707. if (S_FALSE == sc1)
  708. {
  709. tbDebugOut(( DEB_NOTIFY, "rownotfy: notify client veto'ed\n" ));
  710. fVeto = TRUE;
  711. break;
  712. }
  713. pConnCtx->_dwSpare |= bitDidNotifyVote;
  714. }
  715. if (DB_S_UNWANTEDPHASE == sc1)
  716. pConnCtx->_dwSpare |= dwMask;
  717. else if (DB_S_UNWANTEDREASON == sc1)
  718. pConnCtx->_dwSpare |= MapAllPhases(dwMask);
  719. tbDebugOut(( DEB_NOTIFY, "rownotfy:(end) pinging notify client\n" ));
  720. }
  721. }
  722. return fVeto;
  723. }
  724. SCODE CRowsetNotification::OnRowsetChange (
  725. IRowset * pRowset,
  726. DBREASON eReason,
  727. DBEVENTPHASE ePhase,
  728. BOOL fCantDeny )
  729. {
  730. // Note: we don't use SYNCHAFTER, and ABOUTTODO is done here after voting
  731. Win4Assert( DBEVENTPHASE_ABOUTTODO != ePhase &&
  732. DBEVENTPHASE_SYNCHAFTER != ePhase );
  733. BOOL fVeto = FALSE;
  734. CEnumConnectionsLite Enum( *this );
  735. if ( DBEVENTPHASE_OKTODO == ePhase )
  736. {
  737. Win4Assert( !fCantDeny );
  738. fVeto = DoRowsetChangeCallout( Enum, pRowset, eReason,
  739. DBEVENTPHASE_OKTODO, FALSE );
  740. if ( ! fVeto )
  741. fVeto = DoRowsetChangeCallout( Enum, pRowset, eReason,
  742. DBEVENTPHASE_ABOUTTODO, FALSE );
  743. DWORD dwMask = MapPhaseAndReason( DBEVENTPHASE_FAILEDTODO, eReason );
  744. CConnectionPointBase::CConnectionContext *pConnCtx;
  745. for ( pConnCtx = Enum.First(); pConnCtx; pConnCtx = Enum.Next() )
  746. {
  747. if (fVeto &&
  748. 0 == (pConnCtx->_dwSpare & dwMask) &&
  749. 0 != (pConnCtx->_dwSpare & bitDidNotifyVote) )
  750. {
  751. IRowsetNotify *pNotify = (IRowsetNotify *)(pConnCtx->_pIUnk);
  752. tbDebugOut(( DEB_NOTIFY, "rownotfy: pinging notify client %x for FAIL\n", pNotify ));
  753. SCODE sc1 = pNotify->OnRowsetChange( pRowset,
  754. eReason,
  755. DBEVENTPHASE_FAILEDTODO,
  756. TRUE );
  757. tbDebugOut(( DEB_NOTIFY, "rownotfy:(end) pinging notify client for FAIL\n" ));
  758. if (DB_S_UNWANTEDPHASE == sc1)
  759. pConnCtx->_dwSpare |= dwMask;
  760. else if (DB_S_UNWANTEDREASON == sc1)
  761. pConnCtx->_dwSpare |= MapAllPhases(dwMask);
  762. }
  763. pConnCtx->_dwSpare &= ~bitDidNotifyVote;
  764. }
  765. }
  766. else
  767. {
  768. // a non-vetoable notification; just send the notifies
  769. Win4Assert( fCantDeny );
  770. DoRowsetChangeCallout( Enum, pRowset, eReason, ePhase, TRUE );
  771. }
  772. return fVeto ? S_FALSE : S_OK;
  773. }