Leaked source code of windows server 2003
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.

812 lines
24 KiB

  1. //////////////////////////////////////////////////////////////////////////////
  2. /*++
  3. Copyright (C) Microsoft Corporation
  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::/sag_ias_rap_node.htm";
  137. const WCHAR szRASDefaultHelpTopic[] = L"ias_ops.chm::/sag_ias_rap_node.htm";
  138. AFX_MANAGE_STATE(AfxGetStaticModuleState());
  139. bool isRasSnapin = false;
  140. CSnapInItem* pItem;
  141. DATA_OBJECT_TYPES type;
  142. HRESULT hr = GetDataClass(lpDataObject, &pItem, &type);
  143. if ( SUCCEEDED(hr) )
  144. {
  145. isRasSnapin = (pItem->m_helpIndex == RAS_HELP_INDEX);
  146. }
  147. CComPtr<IDisplayHelp> spDisplayHelp;
  148. hr = m_spConsole->QueryInterface(
  149. __uuidof(IDisplayHelp),
  150. (LPVOID*) &spDisplayHelp
  151. );
  152. ASSERT (SUCCEEDED (hr));
  153. if ( SUCCEEDED (hr) )
  154. {
  155. if ( isRasSnapin )
  156. {
  157. hr = spDisplayHelp->ShowTopic(W2OLE ((LPWSTR)szRASDefaultHelpTopic));
  158. }
  159. else
  160. {
  161. hr = spDisplayHelp->ShowTopic(W2OLE ((LPWSTR)szIASDefaultHelpTopic));
  162. }
  163. ASSERT (SUCCEEDED (hr));
  164. }
  165. return hr;
  166. }
  167. //////////////////////////////////////////////////////////////////////////////
  168. /*++
  169. CComponent::Notify
  170. Notifies the snap-in of actions taken by the user.
  171. HRESULT Notify(
  172. LPDATAOBJECT lpDataObject, // Pointer to a data object
  173. MMC_NOTIFY_TYPE event, // Action taken by a user
  174. LPARAM arg, // Depends on event
  175. LPARAM param // Depends on event
  176. );
  177. Parameters
  178. lpDataObject
  179. [in] Pointer to the data object of the currently selected item.
  180. event
  181. [in] Identifies an action taken by a user. IComponent::Notify can receive the
  182. following notifications:
  183. MMCN_ACTIVATE
  184. MMCN_ADD_IMAGES
  185. MMCN_BTN_CLICK
  186. MMCN_CLICK
  187. MMCN_DBLCLICK
  188. MMCN_DELETE
  189. MMCN_EXPAND
  190. MMCN_MINIMIZED
  191. MMCN_PROPERTY_CHANGE
  192. MMCN_REMOVE_CHILDREN
  193. MMCN_RENAME
  194. MMCN_SELECT
  195. MMCN_SHOW
  196. MMCN_VIEW_CHANGE
  197. All of which are forwarded to each node's Notify method, as well as:
  198. MMCN_COLUMN_CLICK
  199. MMCN_SNAPINHELP
  200. Which are handled here.
  201. arg
  202. Depends on the notification type.
  203. param
  204. Depends on the notification type.
  205. Return Values
  206. S_OK
  207. Depends on the notification type.
  208. E_UNEXPECTED
  209. An unexpected error occurred.
  210. Remarks
  211. We are overiding the ATLsnap.h implementation of IComponentImpl because
  212. it always returns E_UNEXPECTED when lpDataObject == NULL.
  213. Unfortunately, some valid message (e.g. MMCN_SNAPINHELP and MMCN_COLUMN_CLICK)
  214. pass in lpDataObject = NULL by design.
  215. Also, there seems to be some problem with Sridhar's latest
  216. IComponentImpl::Notify method, because it causes MMC to run-time error.
  217. --*/
  218. //////////////////////////////////////////////////////////////////////////////
  219. STDMETHODIMP CComponent::Notify (
  220. LPDATAOBJECT lpDataObject,
  221. MMC_NOTIFY_TYPE event,
  222. LPARAM arg,
  223. LPARAM param
  224. )
  225. {
  226. TRACE_FUNCTION("CComponent::Notify");
  227. HRESULT hr;
  228. // deal with help
  229. if(event == MMCN_CONTEXTHELP)
  230. {
  231. return OnResultContextHelp(lpDataObject);
  232. }
  233. // lpDataObject should be a pointer to a node object.
  234. // If it is NULL, then we are being notified of an event
  235. // which doesn't pertain to any specific node.
  236. if ( NULL == lpDataObject )
  237. {
  238. // respond to events which have no associated lpDataObject
  239. switch( event )
  240. {
  241. case MMCN_COLUMN_CLICK:
  242. hr = OnColumnClick( arg, param );
  243. break;
  244. case MMCN_PROPERTY_CHANGE:
  245. hr = OnPropertyChange( arg, param );
  246. break;
  247. case MMCN_VIEW_CHANGE:
  248. hr = OnViewChange( arg, param );
  249. break;
  250. default:
  251. ATLTRACE(_T("+NAPMMC+:# CComponent::Notify - called with lpDataObject == NULL and no event handler\n"));
  252. hr = E_NOTIMPL;
  253. break;
  254. }
  255. return hr;
  256. }
  257. // Respond to some notifications where the lpDataObject is not NULL
  258. // but we nevertheless have decided that we want to handle them on a
  259. // per-IComponent basis.
  260. switch( event )
  261. {
  262. case MMCN_ADD_IMAGES:
  263. return OnAddImages( arg, param );
  264. break;
  265. }
  266. // We were passed a LPDATAOBJECT which corresponds to a node.
  267. // We convert this to the ATL ISnapInDataInterface pointer.
  268. // This is done in GetDataClass (a static method of ISnapInDataInterface)
  269. // by asking the dataobject via a supported clipboard format (CCF_GETCOOKIE)
  270. // to write out a pointer to itself on a stream and then
  271. // casting this value to a pointer.
  272. // We then call the Notify method on that object, letting
  273. // the node object deal with the Notify event itself.
  274. CSnapInItem* pData;
  275. DATA_OBJECT_TYPES type;
  276. hr = CSnapInItem::GetDataClass(lpDataObject, &pData, &type);
  277. if (SUCCEEDED(hr))
  278. {
  279. // We need a richer Notify method which has information about the IComponent and IComponentData objects
  280. //hr = pData->Notify(event, arg, param, TRUE, m_spConsole, NULL, NULL);
  281. hr = pData->Notify(event, arg, param, NULL, this, type );
  282. }
  283. return hr;
  284. }
  285. /////////////////////////////////////////////////////////////////////////////
  286. /*++
  287. CComponent::CompareObjects
  288. Needed so that IPropertySheetProvider::FindPropertySheet will work.
  289. FindPropertySheet is used to bring a pre-existing property sheet to the foreground
  290. so that we don't open multiple copies of Properties on the same node.
  291. It requires CompareObjects to be implemented on both IComponent and IComponentData.
  292. --*/
  293. //////////////////////////////////////////////////////////////////////////////
  294. STDMETHODIMP CComponent::CompareObjects(
  295. LPDATAOBJECT lpDataObjectA
  296. , LPDATAOBJECT lpDataObjectB
  297. )
  298. {
  299. TRACE_FUNCTION("CComponent::CompareObjects");
  300. HRESULT hr;
  301. CSnapInItem *pDataA, *pDataB;
  302. DATA_OBJECT_TYPES typeA, typeB;
  303. hr = GetDataClass(lpDataObjectA, &pDataA, &typeA);
  304. if ( FAILED( hr ) )
  305. {
  306. return hr;
  307. }
  308. hr = GetDataClass(lpDataObjectB, &pDataB, &typeB);
  309. if ( FAILED( hr ) )
  310. {
  311. return hr;
  312. }
  313. if( pDataA == pDataB )
  314. {
  315. // They are the same object.
  316. return S_OK;
  317. }
  318. else
  319. {
  320. // They are different.
  321. return S_FALSE;
  322. }
  323. }
  324. /////////////////////////////////////////////////////////////////////////////
  325. /*++
  326. CComponent::OnColumnClick
  327. HRESULT OnColumnClick(
  328. LPARAM arg
  329. , LPARAM param
  330. )
  331. In our implementation, this method gets called when the MMCN_COLUMN_CLICK
  332. Notify message is sent for our IComponent object.
  333. MMC sends this message when the user clicks on a result-list view column header.
  334. Parameters
  335. arg
  336. Column number.
  337. param
  338. Sort option flags. By default, the sort is in ascending order. To specify descending order, use the RSI_DESCENDING (0x0001) flag.
  339. Return Values
  340. Not used.
  341. --*/
  342. //////////////////////////////////////////////////////////////////////////////
  343. HRESULT CComponent::OnColumnClick(
  344. LPARAM arg
  345. , LPARAM param
  346. )
  347. {
  348. TRACE_FUNCTION("CComponent::OnColumnClick -- Not implemented");
  349. m_nLastClickedColumn = arg;
  350. m_dwLastSortOptions = param;
  351. return E_NOTIMPL;
  352. }
  353. //////////////////////////////////////////////////////////////////////////////
  354. /*++
  355. CComponent::OnViewChange
  356. HRESULT OnViewChange(
  357. LPARAM arg
  358. , LPARAM param
  359. )
  360. This is where we respond to an MMCN_VIEW_CHANGE notification which was
  361. set without any reference to a specific node.
  362. In our implementation, this is a signal to refresh the view of the currently
  363. selected node for this IComponent's view.
  364. --*/
  365. //////////////////////////////////////////////////////////////////////////////
  366. HRESULT CComponent::OnViewChange(
  367. LPARAM arg
  368. , LPARAM param
  369. )
  370. {
  371. ATLTRACE(_T("+NAPMMC+:# CComponent::OnViewChange\n"));
  372. HRESULT hr = S_FALSE;
  373. CChangeNotification *pChangeNotification = NULL;
  374. try
  375. {
  376. // If arg here is non-NULL, it should be a pointer to a CChangeNotification object.
  377. if( arg != NULL )
  378. {
  379. pChangeNotification = (CChangeNotification *) arg;
  380. // For now, just call update item on the node.
  381. // ISSUE: Later, we should have a switch on m_dwFlags to see what we should do.
  382. // e.g. in the case of a deletion, we should (maybe?) reselect parent node or something.
  383. switch( pChangeNotification->m_dwFlags )
  384. {
  385. case CHANGE_UPDATE_RESULT_NODE:
  386. {
  387. // We need to update a single node.
  388. CComQIPtr< IResultData, &IID_IResultData > spResultData( m_spConsole );
  389. if( ! spResultData )
  390. {
  391. throw hr;
  392. }
  393. if( pChangeNotification->m_pNode )
  394. {
  395. HRESULTITEM item;
  396. hr = spResultData->FindItemByLParam( (LPARAM) pChangeNotification->m_pNode, &item );
  397. // Note: You can't use the itemID stored in CSnapInItem's RESULTDATAITEM structure
  398. // as this itemID is unique to each view -- so when you add the same item in each
  399. // result pane view, you get a different itemID from each call to InsertItem.
  400. // CSnapInItem's RESULTDATAITEM structure only stores the last one stored.
  401. // This is a flaw in the atlsnap.h architecture, which is why we use
  402. // MMC's FindItemByLParam instead to get the appropriate itemID.
  403. hr = spResultData->UpdateItem( item );
  404. }
  405. }
  406. break;
  407. case CHANGE_UPDATE_CHILDREN_OF_SELECTED_NODE:
  408. {
  409. // reselecting the node was causing too many message to fly around and
  410. // causing a deadlock condition when extending RRAS.
  411. // The correct way to do this is to just add the result pane items if we are selected.
  412. if( m_pSelectedNode )
  413. {
  414. SCOPEDATAITEM *pScopeDataItem;
  415. m_pSelectedNode->GetScopeData( &pScopeDataItem );
  416. CComQIPtr< IResultData, &IID_IResultData > spResultData( m_spConsole );
  417. if( ! spResultData )
  418. {
  419. throw hr;
  420. }
  421. ((CPoliciesNode *) m_pSelectedNode)->UpdateResultPane(spResultData);
  422. }
  423. }
  424. break;
  425. case CHANGE_UPDATE_CHILDREN_OF_THIS_NODE:
  426. {
  427. // We basically tell MMC to simulate reselecting the
  428. // currently selected scope-pane node, which causes it to redraw.
  429. // This will cause MMC to send the MMCN_SHOW notification
  430. // to the selected node.
  431. if( pChangeNotification->m_pNode && m_pSelectedNode && pChangeNotification->m_pNode == m_pSelectedNode )
  432. {
  433. SCOPEDATAITEM *pScopeDataItem;
  434. m_pSelectedNode->GetScopeData( &pScopeDataItem );
  435. hr = m_spConsole->SelectScopeItem( pScopeDataItem->ID );
  436. }
  437. }
  438. case CHANGE_RESORT_PARENT:
  439. {
  440. // We need to swap a node's display with the one above it and make
  441. // sure that the node appears to remain selected.
  442. // We only want to bother here if the currently selected node in this view
  443. // is the parent of the node which needs to be moved up.
  444. if( pChangeNotification->m_pParentNode && m_pSelectedNode && pChangeNotification->m_pParentNode == m_pSelectedNode )
  445. {
  446. CComQIPtr< IResultData, &IID_IResultData > spResultData( m_spConsole );
  447. if( ! spResultData )
  448. {
  449. throw hr;
  450. }
  451. // Our IResultDataCompare implementation returns an answer based on
  452. // each policy's relative merit.
  453. // The first parameter below is the 0-based column,
  454. // in this case the Order column.
  455. hr = spResultData->Sort( m_nLastClickedColumn, m_dwLastSortOptions, NULL );
  456. // This is a bit of a work-around for the fact
  457. // that MMC doesn't seem to update the item's
  458. // toolbar buttons (e.g. Up/Down enabled) unless
  459. // The items are reselected.
  460. if( pChangeNotification->m_pNode )
  461. {
  462. HRESULTITEM item;
  463. HRESULT hrTemp = spResultData->FindItemByLParam( (LPARAM) pChangeNotification->m_pNode, &item );
  464. if( SUCCEEDED(hrTemp) )
  465. {
  466. // De-select and re-select the node.
  467. // This causes the toolbar buttons to be redrawn.
  468. hrTemp = spResultData->ModifyItemState( 0, item, 0, LVIS_SELECTED );
  469. hrTemp = spResultData->ModifyItemState( 0, item, LVIS_SELECTED, 0 );
  470. }
  471. }
  472. }
  473. }
  474. break;
  475. default:
  476. break;
  477. }
  478. }
  479. }
  480. catch(...)
  481. {
  482. // Do nothing -- just need to catch for proper clean-up below.
  483. }
  484. return hr;
  485. }
  486. //////////////////////////////////////////////////////////////////////////////
  487. /*++
  488. CComponent::OnAddImages
  489. HRESULT OnAddImages(
  490. LPARAM arg
  491. , LPARAM param
  492. )
  493. This is where we respond to an MMCN_ADD_IMAGES notification to
  494. this IComponent object.
  495. We add images to the image list used to display result pane
  496. items corresponding to this IComponent's view.
  497. MMC sends this message to the snap-in's IComponent implementation
  498. to add images for the result pane.
  499. Parameters
  500. arg
  501. Pointer to the result pane's image list (IImageList).
  502. param
  503. Specifies the HSCOPEITEM of the item that was selected or deselected.
  504. Return Values
  505. Not used.
  506. Remarks
  507. The primary snap-in should add images for both folders and leaf
  508. items. Extension snap-ins should add only folder images.
  509. --*/
  510. //////////////////////////////////////////////////////////////////////////////
  511. HRESULT CComponent::OnAddImages(
  512. LPARAM arg
  513. , LPARAM param
  514. )
  515. {
  516. ATLTRACE(_T("# CComponent::OnAddImages\n"));
  517. // Check for preconditions:
  518. _ASSERTE( arg != NULL );
  519. HRESULT hr = S_FALSE;
  520. // ISSUE: sburns in localsec does a trick where he combines
  521. // scope and result pane ImageLists into one
  522. // is this necessary?
  523. CComPtr<IImageList> spImageList = reinterpret_cast<IImageList*>(arg);
  524. _ASSERTE( spImageList != NULL );
  525. HBITMAP hBitmap16 = LoadBitmap( _Module.GetResourceInstance(), MAKEINTRESOURCE( IDB_NAPSNAPIN_16 ) );
  526. HBITMAP hBitmap32 = LoadBitmap( _Module.GetResourceInstance(), MAKEINTRESOURCE( IDB_NAPSNAPIN_32 ) );
  527. if( hBitmap16 != NULL && hBitmap32 != NULL )
  528. {
  529. hr = spImageList->ImageListSetStrip( (LONG_PTR*) hBitmap16, (LONG_PTR*) hBitmap32, 0, RGB(255, 0, 255) );
  530. if( FAILED( hr ) )
  531. {
  532. ATLTRACE(_T("# *** CSnapinNode::OnAddImages -- Failed to add images.\n"));
  533. }
  534. }
  535. if ( hBitmap16 != NULL )
  536. {
  537. DeleteObject(hBitmap16);
  538. }
  539. if ( hBitmap32 != NULL )
  540. {
  541. DeleteObject(hBitmap32);
  542. }
  543. return hr;
  544. }
  545. //////////////////////////////////////////////////////////////////////////////
  546. STDMETHODIMP CComponent::GetWatermarks(
  547. LPDATAOBJECT lpIDataObject,
  548. HBITMAP *lphWatermark,
  549. HBITMAP *lphHeader,
  550. HPALETTE *lphPalette,
  551. BOOL *bStretch
  552. )
  553. {
  554. if(!lphWatermark || !lphHeader || !lphPalette || !bStretch)
  555. return E_INVALIDARG;
  556. *lphWatermark = LoadBitmap(
  557. _Module.GetResourceInstance(),
  558. MAKEINTRESOURCE(IDB_RAP_WATERMARK)
  559. );
  560. *lphHeader = LoadBitmap(
  561. _Module.GetResourceInstance(),
  562. MAKEINTRESOURCE(IDB_RAP_HEADER)
  563. );
  564. *lphPalette = NULL;
  565. *bStretch = FALSE;
  566. return S_OK;
  567. }
  568. //////////////////////////////////////////////////////////////////////////////
  569. /*++
  570. CComponent::OnPropertyChange
  571. HRESULT OnPropertyChange(
  572. LPARAM arg
  573. , LPARAM param
  574. )
  575. This is where we respond to an MMCN_PROPERTY_CHANGE notification.
  576. This notification is sent when we call MMCPropertyChangeNotify.
  577. We call this in our property pages when changes are made to the data
  578. they contain and we may need to update of view of the data.
  579. --*/
  580. //////////////////////////////////////////////////////////////////////////////
  581. HRESULT CComponent::OnPropertyChange(
  582. LPARAM lArg
  583. , LPARAM lParam
  584. )
  585. {
  586. ATLTRACE(_T("# CNodeWithResultChildrenList::OnPropertyChange\n"));
  587. // Check for preconditions:
  588. _ASSERTE( m_spConsole != NULL );
  589. HRESULT hr = S_FALSE;
  590. if( lParam )
  591. {
  592. // We were passed a pointer to a CChangeNotification in the param argument.
  593. CChangeNotification * pChangeNotification = (CChangeNotification *) lParam;
  594. // We call notify on the node specified, passing it our own custom event type
  595. // so that it knows that it must refresh its data.
  596. // Call notify on this node with the MMCN_PROPERTY_CHANGE notification.
  597. // We had to use this trick because of the fact that we are using template
  598. // classes and so we have no common object among all our nodes
  599. // other than CSnapInItem. But we can't change CSnapInItem
  600. // so instead we use the notify method it already has with a new
  601. // notification.
  602. // Note: We are trying to deal gracefully here with the fact that the
  603. // MMCN_PROPERTY_CHANGE notification doesn't pass us an lpDataObject
  604. // so we have to have our own protocol for picking out which node
  605. // needs to update itself.
  606. hr = pChangeNotification->m_pNode->Notify( MMCN_PROPERTY_CHANGE
  607. , NULL
  608. , NULL
  609. , NULL
  610. , NULL
  611. , (DATA_OBJECT_TYPES) 0
  612. );
  613. // We want to make sure all views with this node select also get updated.
  614. // Pass it the CChangeNotification pointer we were passed in param.
  615. hr = m_pComponentData->m_spConsole->UpdateAllViews( NULL, lParam, 0);
  616. pChangeNotification->Release();
  617. }
  618. return hr;
  619. }