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.

823 lines
23 KiB

  1. //////////////////////////////////////////////////////////////////////////////
  2. /*++
  3. Copyright (C) Microsoft Corporation, 1997 - 1999
  4. Module Name:
  5. Component.cpp
  6. Abstract:
  7. Implementation file for the CComponent class.
  8. The CComponent class implements several interfaces which MMC uses:
  9. The IComponent interface is basically how MMC talks to the snap-in
  10. to get it to implement a right-hand-side "scope" pane. There can be several
  11. objects implementing this interface instantiated at once. These are best
  12. thought of as "views" on the single object implementing the IComponentData
  13. "document" (see ComponentData.cpp).
  14. The IExtendPropertySheet interface is how the snap-in adds property sheets
  15. for any of the items a user might click on.
  16. The IExtendContextMenu interface what we do to add custom entries
  17. to the menu which appears when a user right-clicks on a node.
  18. The IExtendControlBar interface allows us to support a custom
  19. iconic toolbar.
  20. The IResultDataCompare interface allows us to support a custom
  21. sorting algorithm for result pane items
  22. Note:
  23. Much of the functionality of this class is implemented in atlsnap.h
  24. by IComponentDataImpl. We are mostly overriding here.
  25. Revision History:
  26. mmaguire 11/6/97 - created using MMC snap-in wizard
  27. mmaguire 11/24/97 - hurricaned for better project structure
  28. --*/
  29. //////////////////////////////////////////////////////////////////////////////
  30. //////////////////////////////////////////////////////////////////////////////
  31. // BEGIN INCLUDES
  32. //
  33. // standard includes:
  34. //
  35. #include "Precompiled.h"
  36. //
  37. // where we can find declaration for main class in this file:
  38. //
  39. #include "proxyres.h"
  40. #include "Component.h"
  41. //
  42. // where we can find declarations needed in this file:
  43. //
  44. #include "MachineNode.h"
  45. #include "PoliciesNode.h"
  46. #include "PolicyNode.h"
  47. #include "ChangeNotification.h"
  48. #include "globals.h"
  49. //
  50. //
  51. // END INCLUDES
  52. //////////////////////////////////////////////////////////////////////////////
  53. //////////////////////////////////////////////////////////////////////////////
  54. /*++
  55. CComponent::CComponent
  56. Constructor
  57. --*/
  58. //////////////////////////////////////////////////////////////////////////////
  59. CComponent::CComponent()
  60. :m_pSelectedNode(NULL),
  61. m_nLastClickedColumn(1),
  62. m_dwLastSortOptions(0)
  63. {
  64. TRACE_FUNCTION("CComponent::CComponent");
  65. }
  66. //////////////////////////////////////////////////////////////////////////////
  67. /*++
  68. CComponent::~CComponent
  69. Destructor
  70. --*/
  71. //////////////////////////////////////////////////////////////////////////////
  72. CComponent::~CComponent()
  73. {
  74. TRACE_FUNCTION("CComponent::~CComponent");
  75. }
  76. //+---------------------------------------------------------------------------
  77. //
  78. // Function: Compare
  79. //
  80. // Class: CComponent (inherited from IResultDataCompare)
  81. //
  82. // Synopsis: customized sorting algorithm
  83. // This method will be called whenever MMC console needs to
  84. // compare two result pane data items, for example, when user
  85. // clicks on the column header
  86. //
  87. // Arguments:
  88. // LPARAM lUserParam, User-provided information
  89. // MMC_COOKIE cookieA, Unique identifier of first object
  90. // MMC_COOKIE cookieB, Unique identifier of second object
  91. // int * pnResult Column being sorted//
  92. //
  93. // Returns: STDMETHODIMP -
  94. //
  95. // History: Created byao 2/5/98 4:19:10 PM
  96. //
  97. //+---------------------------------------------------------------------------
  98. STDMETHODIMP CComponent::Compare(LPARAM lUserParam,
  99. MMC_COOKIE cookieA,
  100. MMC_COOKIE cookieB,
  101. int *pnResult)
  102. {
  103. TRACE_FUNCTION("CComponent::Compare");
  104. //
  105. // sort policies node according to their merit value
  106. //
  107. CPolicyNode *pA = (CPolicyNode*)cookieA;
  108. CPolicyNode *pB = (CPolicyNode*)cookieB;
  109. ATLASSERT(pA != NULL);
  110. ATLASSERT(pB != NULL);
  111. int nCol = *pnResult;
  112. switch(nCol)
  113. {
  114. case 0:
  115. *pnResult = wcscmp(pA->GetResultPaneColInfo(0), pB->GetResultPaneColInfo(0));
  116. break;
  117. case 1:
  118. *pnResult = pA->GetMerit() - pB->GetMerit();
  119. if(*pnResult)
  120. *pnResult /= abs(*pnResult);
  121. break;
  122. }
  123. return S_OK;
  124. }
  125. /*!--------------------------------------------------------------------------
  126. CComponent::OnResultContextHelp
  127. Implementation of OnResultContextHelp
  128. Author: EricDav
  129. ---------------------------------------------------------------------------*/
  130. HRESULT CComponent::OnResultContextHelp(LPDATAOBJECT lpDataObject)
  131. {
  132. // Need to find if the context is running in the Remote Access snap-in
  133. // or in the IAS snapin.
  134. // path if in IAS: ias_ops.chm::/sag_ias_rap_node.htm
  135. // path in RAS snapin: rrasconcepts.chm::/sag_rap_node.htm
  136. const WCHAR szIASDefaultHelpTopic[] = L"ias_ops.chm::" \
  137. L"/sag_ias_rap_node.htm";
  138. const WCHAR szRASDefaultHelpTopic[] = L"RRASconcepts.chm::" \
  139. L"/sag_rap_node.htm";
  140. AFX_MANAGE_STATE(AfxGetStaticModuleState());
  141. bool isRasSnapin = false;
  142. CSnapInItem* pItem;
  143. DATA_OBJECT_TYPES type;
  144. HRESULT hr = GetDataClass(lpDataObject, &pItem, &type);
  145. if ( SUCCEEDED(hr) )
  146. {
  147. isRasSnapin = (pItem->m_helpIndex == RAS_HELP_INDEX);
  148. }
  149. CComPtr<IDisplayHelp> spDisplayHelp;
  150. hr = m_spConsole->QueryInterface(
  151. __uuidof(IDisplayHelp),
  152. (LPVOID*) &spDisplayHelp
  153. );
  154. ASSERT (SUCCEEDED (hr));
  155. if ( SUCCEEDED (hr) )
  156. {
  157. if ( isRasSnapin )
  158. {
  159. hr = spDisplayHelp->ShowTopic(W2OLE ((LPWSTR)szRASDefaultHelpTopic));
  160. }
  161. else
  162. {
  163. hr = spDisplayHelp->ShowTopic(W2OLE ((LPWSTR)szIASDefaultHelpTopic));
  164. }
  165. ASSERT (SUCCEEDED (hr));
  166. }
  167. return hr;
  168. }
  169. //////////////////////////////////////////////////////////////////////////////
  170. /*++
  171. CComponent::Notify
  172. Notifies the snap-in of actions taken by the user.
  173. HRESULT Notify(
  174. LPDATAOBJECT lpDataObject, // Pointer to a data object
  175. MMC_NOTIFY_TYPE event, // Action taken by a user
  176. LPARAM arg, // Depends on event
  177. LPARAM param // Depends on event
  178. );
  179. Parameters
  180. lpDataObject
  181. [in] Pointer to the data object of the currently selected item.
  182. event
  183. [in] Identifies an action taken by a user. IComponent::Notify can receive the
  184. following notifications:
  185. MMCN_ACTIVATE
  186. MMCN_ADD_IMAGES
  187. MMCN_BTN_CLICK
  188. MMCN_CLICK
  189. MMCN_DBLCLICK
  190. MMCN_DELETE
  191. MMCN_EXPAND
  192. MMCN_MINIMIZED
  193. MMCN_PROPERTY_CHANGE
  194. MMCN_REMOVE_CHILDREN
  195. MMCN_RENAME
  196. MMCN_SELECT
  197. MMCN_SHOW
  198. MMCN_VIEW_CHANGE
  199. All of which are forwarded to each node's Notify method, as well as:
  200. MMCN_COLUMN_CLICK
  201. MMCN_SNAPINHELP
  202. Which are handled here.
  203. arg
  204. Depends on the notification type.
  205. param
  206. Depends on the notification type.
  207. Return Values
  208. S_OK
  209. Depends on the notification type.
  210. E_UNEXPECTED
  211. An unexpected error occurred.
  212. Remarks
  213. We are overiding the ATLsnap.h implementation of IComponentImpl because
  214. it always returns E_UNEXPECTED when lpDataObject == NULL.
  215. Unfortunately, some valid message (e.g. MMCN_SNAPINHELP and MMCN_COLUMN_CLICK)
  216. pass in lpDataObject = NULL by design.
  217. Also, there seems to be some problem with Sridhar's latest
  218. IComponentImpl::Notify method, because it causes MMC to run-time error.
  219. --*/
  220. //////////////////////////////////////////////////////////////////////////////
  221. STDMETHODIMP CComponent::Notify (
  222. LPDATAOBJECT lpDataObject,
  223. MMC_NOTIFY_TYPE event,
  224. LPARAM arg,
  225. LPARAM param
  226. )
  227. {
  228. TRACE_FUNCTION("CComponent::Notify");
  229. HRESULT hr;
  230. // deal with help
  231. if(event == MMCN_CONTEXTHELP)
  232. {
  233. return OnResultContextHelp(lpDataObject);
  234. }
  235. // lpDataObject should be a pointer to a node object.
  236. // If it is NULL, then we are being notified of an event
  237. // which doesn't pertain to any specific node.
  238. if ( NULL == lpDataObject )
  239. {
  240. // respond to events which have no associated lpDataObject
  241. switch( event )
  242. {
  243. case MMCN_COLUMN_CLICK:
  244. hr = OnColumnClick( arg, param );
  245. break;
  246. case MMCN_PROPERTY_CHANGE:
  247. hr = OnPropertyChange( arg, param );
  248. break;
  249. case MMCN_VIEW_CHANGE:
  250. hr = OnViewChange( arg, param );
  251. break;
  252. default:
  253. ATLTRACE(_T("+NAPMMC+:# CComponent::Notify - called with lpDataObject == NULL and no event handler\n"));
  254. hr = E_NOTIMPL;
  255. break;
  256. }
  257. return hr;
  258. }
  259. // Respond to some notifications where the lpDataObject is not NULL
  260. // but we nevertheless have decided that we want to handle them on a
  261. // per-IComponent basis.
  262. switch( event )
  263. {
  264. case MMCN_ADD_IMAGES:
  265. return OnAddImages( arg, param );
  266. break;
  267. }
  268. // We were passed a LPDATAOBJECT which corresponds to a node.
  269. // We convert this to the ATL ISnapInDataInterface pointer.
  270. // This is done in GetDataClass (a static method of ISnapInDataInterface)
  271. // by asking the dataobject via a supported clipboard format (CCF_GETCOOKIE)
  272. // to write out a pointer to itself on a stream and then
  273. // casting this value to a pointer.
  274. // We then call the Notify method on that object, letting
  275. // the node object deal with the Notify event itself.
  276. CSnapInItem* pData;
  277. DATA_OBJECT_TYPES type;
  278. hr = CSnapInItem::GetDataClass(lpDataObject, &pData, &type);
  279. if (SUCCEEDED(hr))
  280. {
  281. // We need a richer Notify method which has information about the IComponent and IComponentData objects
  282. //hr = pData->Notify(event, arg, param, TRUE, m_spConsole, NULL, NULL);
  283. hr = pData->Notify(event, arg, param, NULL, this, type );
  284. }
  285. return hr;
  286. }
  287. /////////////////////////////////////////////////////////////////////////////
  288. /*++
  289. CComponent::CompareObjects
  290. Needed so that IPropertySheetProvider::FindPropertySheet will work.
  291. FindPropertySheet is used to bring a pre-existing property sheet to the foreground
  292. so that we don't open multiple copies of Properties on the same node.
  293. It requires CompareObjects to be implemented on both IComponent and IComponentData.
  294. --*/
  295. //////////////////////////////////////////////////////////////////////////////
  296. STDMETHODIMP CComponent::CompareObjects(
  297. LPDATAOBJECT lpDataObjectA
  298. , LPDATAOBJECT lpDataObjectB
  299. )
  300. {
  301. TRACE_FUNCTION("CComponent::CompareObjects");
  302. HRESULT hr;
  303. CSnapInItem *pDataA, *pDataB;
  304. DATA_OBJECT_TYPES typeA, typeB;
  305. hr = GetDataClass(lpDataObjectA, &pDataA, &typeA);
  306. if ( FAILED( hr ) )
  307. {
  308. return hr;
  309. }
  310. hr = GetDataClass(lpDataObjectB, &pDataB, &typeB);
  311. if ( FAILED( hr ) )
  312. {
  313. return hr;
  314. }
  315. if( pDataA == pDataB )
  316. {
  317. // They are the same object.
  318. return S_OK;
  319. }
  320. else
  321. {
  322. // They are different.
  323. return S_FALSE;
  324. }
  325. }
  326. /////////////////////////////////////////////////////////////////////////////
  327. /*++
  328. CComponent::OnColumnClick
  329. HRESULT OnColumnClick(
  330. LPARAM arg
  331. , LPARAM param
  332. )
  333. In our implementation, this method gets called when the MMCN_COLUMN_CLICK
  334. Notify message is sent for our IComponent object.
  335. MMC sends this message when the user clicks on a result-list view column header.
  336. Parameters
  337. arg
  338. Column number.
  339. param
  340. Sort option flags. By default, the sort is in ascending order. To specify descending order, use the RSI_DESCENDING (0x0001) flag.
  341. Return Values
  342. Not used.
  343. --*/
  344. //////////////////////////////////////////////////////////////////////////////
  345. HRESULT CComponent::OnColumnClick(
  346. LPARAM arg
  347. , LPARAM param
  348. )
  349. {
  350. TRACE_FUNCTION("CComponent::OnColumnClick -- Not implemented");
  351. m_nLastClickedColumn = arg;
  352. m_dwLastSortOptions = param;
  353. return E_NOTIMPL;
  354. }
  355. //////////////////////////////////////////////////////////////////////////////
  356. /*++
  357. CComponent::OnViewChange
  358. HRESULT OnViewChange(
  359. LPARAM arg
  360. , LPARAM param
  361. )
  362. This is where we respond to an MMCN_VIEW_CHANGE notification which was
  363. set without any reference to a specific node.
  364. In our implementation, this is a signal to refresh the view of the currently
  365. selected node for this IComponent's view.
  366. --*/
  367. //////////////////////////////////////////////////////////////////////////////
  368. HRESULT CComponent::OnViewChange(
  369. LPARAM arg
  370. , LPARAM param
  371. )
  372. {
  373. ATLTRACE(_T("+NAPMMC+:# CComponent::OnViewChange\n"));
  374. HRESULT hr = S_FALSE;
  375. CChangeNotification *pChangeNotification = NULL;
  376. try
  377. {
  378. // If arg here is non-NULL, it should be a pointer to a CChangeNotification object.
  379. if( arg != NULL )
  380. {
  381. pChangeNotification = (CChangeNotification *) arg;
  382. // For now, just call update item on the node.
  383. // ISSUE: Later, we should have a switch on m_dwFlags to see what we should do.
  384. // e.g. in the case of a deletion, we should (maybe?) reselect parent node or something.
  385. switch( pChangeNotification->m_dwFlags )
  386. {
  387. case CHANGE_UPDATE_RESULT_NODE:
  388. {
  389. // We need to update a single node.
  390. CComQIPtr< IResultData, &IID_IResultData > spResultData( m_spConsole );
  391. if( ! spResultData )
  392. {
  393. throw hr;
  394. }
  395. if( pChangeNotification->m_pNode )
  396. {
  397. HRESULTITEM item;
  398. hr = spResultData->FindItemByLParam( (LPARAM) pChangeNotification->m_pNode, &item );
  399. // Note: You can't use the itemID stored in CSnapInItem's RESULTDATAITEM structure
  400. // as this itemID is unique to each view -- so when you add the same item in each
  401. // result pane view, you get a different itemID from each call to InsertItem.
  402. // CSnapInItem's RESULTDATAITEM structure only stores the last one stored.
  403. // This is a flaw in the atlsnap.h architecture, which is why we use
  404. // MMC's FindItemByLParam instead to get the appropriate itemID.
  405. hr = spResultData->UpdateItem( item );
  406. }
  407. }
  408. break;
  409. case CHANGE_UPDATE_CHILDREN_OF_SELECTED_NODE:
  410. {
  411. // reselecting the node was causing too many message to fly around and
  412. // causing a deadlock condition when extending RRAS.
  413. // The correct way to do this is to just add the result pane items if we are selected.
  414. if( m_pSelectedNode )
  415. {
  416. SCOPEDATAITEM *pScopeDataItem;
  417. m_pSelectedNode->GetScopeData( &pScopeDataItem );
  418. CComQIPtr< IResultData, &IID_IResultData > spResultData( m_spConsole );
  419. if( ! spResultData )
  420. {
  421. throw hr;
  422. }
  423. ((CPoliciesNode *) m_pSelectedNode)->UpdateResultPane(spResultData);
  424. }
  425. }
  426. break;
  427. case CHANGE_UPDATE_CHILDREN_OF_THIS_NODE:
  428. {
  429. // We basically tell MMC to simulate reselecting the
  430. // currently selected scope-pane node, which causes it to redraw.
  431. // This will cause MMC to send the MMCN_SHOW notification
  432. // to the selected node.
  433. if( pChangeNotification->m_pNode && m_pSelectedNode && pChangeNotification->m_pNode == m_pSelectedNode )
  434. {
  435. SCOPEDATAITEM *pScopeDataItem;
  436. m_pSelectedNode->GetScopeData( &pScopeDataItem );
  437. hr = m_spConsole->SelectScopeItem( pScopeDataItem->ID );
  438. }
  439. }
  440. case CHANGE_RESORT_PARENT:
  441. {
  442. // We need to swap a node's display with the one above it and make
  443. // sure that the node appears to remain selected.
  444. // We only want to bother here if the currently selected node in this view
  445. // is the parent of the node which needs to be moved up.
  446. if( pChangeNotification->m_pParentNode && m_pSelectedNode && pChangeNotification->m_pParentNode == m_pSelectedNode )
  447. {
  448. CComQIPtr< IResultData, &IID_IResultData > spResultData( m_spConsole );
  449. if( ! spResultData )
  450. {
  451. throw hr;
  452. }
  453. // Our IResultDataCompare implementation returns an answer based on
  454. // each policy's relative merit.
  455. // The first parameter below is the 0-based column,
  456. // in this case the Order column.
  457. hr = spResultData->Sort( m_nLastClickedColumn, m_dwLastSortOptions, NULL );
  458. // This is a bit of a work-around for the fact
  459. // that MMC doesn't seem to update the item's
  460. // toolbar buttons (e.g. Up/Down enabled) unless
  461. // The items are reselected.
  462. if( pChangeNotification->m_pNode )
  463. {
  464. HRESULTITEM item;
  465. HRESULT hrTemp = spResultData->FindItemByLParam( (LPARAM) pChangeNotification->m_pNode, &item );
  466. if( SUCCEEDED(hrTemp) )
  467. {
  468. // See whether the item is selected.
  469. RESULTDATAITEM rdi;
  470. ZeroMemory( &rdi, sizeof(rdi) );
  471. rdi.itemID = item;
  472. rdi.mask = RDI_STATE;
  473. hrTemp = spResultData->GetItem( &rdi );
  474. if( SUCCEEDED( hrTemp ) && (rdi.nState & LVIS_SELECTED) )
  475. {
  476. // De-select and re-select the node.
  477. // This causes the toolbar buttons to be redrawn.
  478. hrTemp = spResultData->ModifyItemState( 0, item, 0, LVIS_SELECTED );
  479. hrTemp = spResultData->ModifyItemState( 0, item, LVIS_SELECTED, 0 );
  480. }
  481. }
  482. }
  483. }
  484. }
  485. break;
  486. default:
  487. break;
  488. }
  489. }
  490. }
  491. catch(...)
  492. {
  493. // Do nothing -- just need to catch for proper clean-up below.
  494. }
  495. return hr;
  496. }
  497. //////////////////////////////////////////////////////////////////////////////
  498. /*++
  499. CComponent::OnAddImages
  500. HRESULT OnAddImages(
  501. LPARAM arg
  502. , LPARAM param
  503. )
  504. This is where we respond to an MMCN_ADD_IMAGES notification to
  505. this IComponent object.
  506. We add images to the image list used to display result pane
  507. items corresponding to this IComponent's view.
  508. MMC sends this message to the snap-in's IComponent implementation
  509. to add images for the result pane.
  510. Parameters
  511. arg
  512. Pointer to the result pane's image list (IImageList).
  513. param
  514. Specifies the HSCOPEITEM of the item that was selected or deselected.
  515. Return Values
  516. Not used.
  517. Remarks
  518. The primary snap-in should add images for both folders and leaf
  519. items. Extension snap-ins should add only folder images.
  520. --*/
  521. //////////////////////////////////////////////////////////////////////////////
  522. HRESULT CComponent::OnAddImages(
  523. LPARAM arg
  524. , LPARAM param
  525. )
  526. {
  527. ATLTRACE(_T("# CComponent::OnAddImages\n"));
  528. // Check for preconditions:
  529. _ASSERTE( arg != NULL );
  530. HRESULT hr = S_FALSE;
  531. // ISSUE: sburns in localsec does a trick where he combines
  532. // scope and result pane ImageLists into one
  533. // is this necessary?
  534. CComPtr<IImageList> spImageList = reinterpret_cast<IImageList*>(arg);
  535. _ASSERTE( spImageList != NULL );
  536. HBITMAP hBitmap16 = LoadBitmap( _Module.GetResourceInstance(), MAKEINTRESOURCE( IDB_NAPSNAPIN_16 ) );
  537. HBITMAP hBitmap32 = LoadBitmap( _Module.GetResourceInstance(), MAKEINTRESOURCE( IDB_NAPSNAPIN_32 ) );
  538. if( hBitmap16 != NULL && hBitmap32 != NULL )
  539. {
  540. hr = spImageList->ImageListSetStrip( (LONG_PTR*) hBitmap16, (LONG_PTR*) hBitmap32, 0, RGB(255, 0, 255) );
  541. if( FAILED( hr ) )
  542. {
  543. ATLTRACE(_T("# *** CSnapinNode::OnAddImages -- Failed to add images.\n"));
  544. }
  545. }
  546. if ( hBitmap16 != NULL )
  547. {
  548. DeleteObject(hBitmap16);
  549. }
  550. if ( hBitmap32 != NULL )
  551. {
  552. DeleteObject(hBitmap32);
  553. }
  554. return hr;
  555. }
  556. //////////////////////////////////////////////////////////////////////////////
  557. STDMETHODIMP CComponent::GetWatermarks(
  558. LPDATAOBJECT lpIDataObject,
  559. HBITMAP *lphWatermark,
  560. HBITMAP *lphHeader,
  561. HPALETTE *lphPalette,
  562. BOOL *bStretch
  563. )
  564. {
  565. if(!lphWatermark || !lphHeader || !lphPalette || !bStretch)
  566. return E_INVALIDARG;
  567. *lphWatermark = LoadBitmap(
  568. _Module.GetResourceInstance(),
  569. MAKEINTRESOURCE(IDB_RAP_WATERMARK)
  570. );
  571. *lphHeader = LoadBitmap(
  572. _Module.GetResourceInstance(),
  573. MAKEINTRESOURCE(IDB_RAP_HEADER)
  574. );
  575. *lphPalette = NULL;
  576. *bStretch = FALSE;
  577. return S_OK;
  578. }
  579. //////////////////////////////////////////////////////////////////////////////
  580. /*++
  581. CComponent::OnPropertyChange
  582. HRESULT OnPropertyChange(
  583. LPARAM arg
  584. , LPARAM param
  585. )
  586. This is where we respond to an MMCN_PROPERTY_CHANGE notification.
  587. This notification is sent when we call MMCPropertyChangeNotify.
  588. We call this in our property pages when changes are made to the data
  589. they contain and we may need to update of view of the data.
  590. --*/
  591. //////////////////////////////////////////////////////////////////////////////
  592. HRESULT CComponent::OnPropertyChange(
  593. LPARAM lArg
  594. , LPARAM lParam
  595. )
  596. {
  597. ATLTRACE(_T("# CNodeWithResultChildrenList::OnPropertyChange\n"));
  598. // Check for preconditions:
  599. _ASSERTE( m_spConsole != NULL );
  600. HRESULT hr = S_FALSE;
  601. if( lParam )
  602. {
  603. // We were passed a pointer to a CChangeNotification in the param argument.
  604. CChangeNotification * pChangeNotification = (CChangeNotification *) lParam;
  605. // We call notify on the node specified, passing it our own custom event type
  606. // so that it knows that it must refresh its data.
  607. // Call notify on this node with the MMCN_PROPERTY_CHANGE notification.
  608. // We had to use this trick because of the fact that we are using template
  609. // classes and so we have no common object among all our nodes
  610. // other than CSnapInItem. But we can't change CSnapInItem
  611. // so instead we use the notify method it already has with a new
  612. // notification.
  613. // Note: We are trying to deal gracefully here with the fact that the
  614. // MMCN_PROPERTY_CHANGE notification doesn't pass us an lpDataObject
  615. // so we have to have our own protocol for picking out which node
  616. // needs to update itself.
  617. hr = pChangeNotification->m_pNode->Notify( MMCN_PROPERTY_CHANGE
  618. , NULL
  619. , NULL
  620. , NULL
  621. , NULL
  622. , (DATA_OBJECT_TYPES) 0
  623. );
  624. // We want to make sure all views with this node select also get updated.
  625. // Pass it the CChangeNotification pointer we were passed in param.
  626. hr = m_spConsole->UpdateAllViews( NULL, lParam, 0);
  627. pChangeNotification->Release();
  628. }
  629. return hr;
  630. }