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.

2140 lines
60 KiB

  1. /////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright (c) 1996-2000 Microsoft Corporation
  4. //
  5. // Module Name:
  6. // ClusProp.cpp
  7. //
  8. // Abstract:
  9. // Implementation of the cluster property sheet and pages.
  10. //
  11. // Author:
  12. // David Potter (davidp) May 13, 1996
  13. //
  14. // Revision History:
  15. //
  16. // Notes:
  17. //
  18. /////////////////////////////////////////////////////////////////////////////
  19. #include "stdafx.h"
  20. #include "ClusProp.h"
  21. #include "Cluster.h"
  22. #include "Res.h"
  23. #include "ClusDoc.h"
  24. #include "ClusItem.inl"
  25. //#include "EditAcl.h"
  26. #include "DDxDDv.h"
  27. #include "ExcOper.h"
  28. #include "HelpData.h" // g_rghelpmapClusterGeneral
  29. #include "WaitDlg.h"
  30. #ifdef _DEBUG
  31. #define new DEBUG_NEW
  32. #undef THIS_FILE
  33. static char THIS_FILE[] = __FILE__;
  34. #endif
  35. /////////////////////////////////////////////////////////////////////////////
  36. // CClusterPropSheet
  37. /////////////////////////////////////////////////////////////////////////////
  38. IMPLEMENT_DYNAMIC(CClusterPropSheet, CBasePropertySheet)
  39. /////////////////////////////////////////////////////////////////////////////
  40. // Message Maps
  41. BEGIN_MESSAGE_MAP(CClusterPropSheet, CBasePropertySheet)
  42. //{{AFX_MSG_MAP(CClusterPropSheet)
  43. //}}AFX_MSG_MAP
  44. END_MESSAGE_MAP()
  45. /////////////////////////////////////////////////////////////////////////////
  46. //++
  47. //
  48. // CClusterPropSheet::CClusterPropSheet
  49. //
  50. // Routine Description:
  51. // Constructor.
  52. //
  53. // Arguments:
  54. // pci [IN OUT] Cluster item whose properties are to be displayed.
  55. // pParentWnd [IN OUT] Parent window for this property sheet.
  56. // iSelectPage [IN] Page to show first.
  57. //
  58. // Return Value:
  59. // None.
  60. //
  61. //--
  62. /////////////////////////////////////////////////////////////////////////////
  63. CClusterPropSheet::CClusterPropSheet(
  64. IN OUT CWnd * pParentWnd,
  65. IN UINT iSelectPage
  66. )
  67. : CBasePropertySheet(pParentWnd, iSelectPage)
  68. {
  69. m_rgpages[0] = &PageGeneral();
  70. m_rgpages[1] = &PageQuorum();
  71. m_rgpages[2] = &PageNetPriority();
  72. } //*** CClusterPropSheet::CClusterPropSheet()
  73. /////////////////////////////////////////////////////////////////////////////
  74. //++
  75. //
  76. // CClusterPropSheet::BInit
  77. //
  78. // Routine Description:
  79. // Initialize the property sheet.
  80. //
  81. // Arguments:
  82. // pci [IN OUT] Cluster item whose properties are to be displayed.
  83. // iimgIcon [IN] Index in the large image list for the image to use
  84. // as the icon on each page.
  85. //
  86. // Return Value:
  87. // TRUE Property sheet initialized successfully.
  88. // FALSE Error initializing property sheet.
  89. //
  90. //--
  91. /////////////////////////////////////////////////////////////////////////////
  92. BOOL CClusterPropSheet::BInit(
  93. IN OUT CClusterItem * pci,
  94. IN IIMG iimgIcon
  95. )
  96. {
  97. // Call the base class method.
  98. if (!CBasePropertySheet::BInit(pci, iimgIcon))
  99. return FALSE;
  100. // Set the read-only flag if the handles are invalid.
  101. if ((PciCluster()->Hcluster() == NULL)
  102. || (PciCluster()->Hkey() == NULL))
  103. m_bReadOnly = TRUE;
  104. return TRUE;
  105. } //*** CClusterPropSheet::BInit()
  106. /////////////////////////////////////////////////////////////////////////////
  107. //++
  108. //
  109. // CClusterPropSheet::Ppages
  110. //
  111. // Routine Description:
  112. // Returns the array of pages to add to the property sheet.
  113. //
  114. // Arguments:
  115. // None.
  116. //
  117. // Return Value:
  118. // Page array.
  119. //
  120. //--
  121. /////////////////////////////////////////////////////////////////////////////
  122. CBasePropertyPage ** CClusterPropSheet::Ppages(void)
  123. {
  124. return m_rgpages;
  125. } //*** CClusterPropSheet::Ppages()
  126. /////////////////////////////////////////////////////////////////////////////
  127. //++
  128. //
  129. // CClusterPropSheet::Cpages
  130. //
  131. // Routine Description:
  132. // Returns the count of pages in the array.
  133. //
  134. // Arguments:
  135. // None.
  136. //
  137. // Return Value:
  138. // Count of pages in the array.
  139. //
  140. //--
  141. /////////////////////////////////////////////////////////////////////////////
  142. int CClusterPropSheet::Cpages(void)
  143. {
  144. return sizeof(m_rgpages) / sizeof(CBasePropertyPage *);
  145. } //*** CClusterPropSheet::Cpages()
  146. //*************************************************************************//
  147. /////////////////////////////////////////////////////////////////////////////
  148. // CClusterGeneralPage property page
  149. /////////////////////////////////////////////////////////////////////////////
  150. IMPLEMENT_DYNCREATE(CClusterGeneralPage, CBasePropertyPage)
  151. /////////////////////////////////////////////////////////////////////////////
  152. // Message Maps
  153. BEGIN_MESSAGE_MAP(CClusterGeneralPage, CBasePropertyPage)
  154. //{{AFX_MSG_MAP(CClusterGeneralPage)
  155. // ON_BN_CLICKED(IDC_PP_CLUS_PERMISSIONS, OnBnClickedPermissions)
  156. //}}AFX_MSG_MAP
  157. ON_EN_CHANGE(IDC_PP_CLUS_NAME, CBasePropertyPage::OnChangeCtrl)
  158. ON_EN_CHANGE(IDC_PP_CLUS_DESC, CBasePropertyPage::OnChangeCtrl)
  159. END_MESSAGE_MAP()
  160. /////////////////////////////////////////////////////////////////////////////
  161. //++
  162. //
  163. // CClusterGeneralPage::CClusterGeneralPage
  164. //
  165. // Routine Description:
  166. // Constructor.
  167. //
  168. // Arguments:
  169. // None.
  170. //
  171. // Return Value:
  172. // None.
  173. //
  174. //--
  175. /////////////////////////////////////////////////////////////////////////////
  176. CClusterGeneralPage::CClusterGeneralPage(void)
  177. : CBasePropertyPage(IDD, g_aHelpIDs_IDD_PP_CLUSTER_GENERAL)
  178. {
  179. //{{AFX_DATA_INIT(CClusterGeneralPage)
  180. m_strName = _T("");
  181. m_strDesc = _T("");
  182. m_strVendorID = _T("");
  183. m_strVersion = _T("");
  184. //}}AFX_DATA_INIT
  185. // m_bSecurityChanged = FALSE;
  186. } //*** CClusterGeneralPage::CClusterGeneralPage()
  187. /////////////////////////////////////////////////////////////////////////////
  188. //++
  189. //
  190. // CClusterGeneralPage::~CClusterGeneralPage
  191. //
  192. // Routine Description:
  193. // Destructor.
  194. //
  195. // Arguments:
  196. // None.
  197. //
  198. // Return Value:
  199. // None.
  200. //
  201. //--
  202. /////////////////////////////////////////////////////////////////////////////
  203. CClusterGeneralPage::~CClusterGeneralPage(void)
  204. {
  205. } //*** CClusterGeneralPage::~CClusterGeneralPage()
  206. /////////////////////////////////////////////////////////////////////////////
  207. //++
  208. //
  209. // CClusterGeneralPage::BInit
  210. //
  211. // Routine Description:
  212. // Initialize the page.
  213. //
  214. // Arguments:
  215. // psht [IN OUT] Property sheet to which this page belongs.
  216. //
  217. // Return Value:
  218. // TRUE Page initialized successfully.
  219. // FALSE Page failed to initialize.
  220. //
  221. //--
  222. /////////////////////////////////////////////////////////////////////////////
  223. BOOL CClusterGeneralPage::BInit(IN OUT CBaseSheet * psht)
  224. {
  225. BOOL bSuccess;
  226. CWaitCursor wc;
  227. ASSERT_KINDOF(CClusterPropSheet, psht);
  228. bSuccess = CBasePropertyPage::BInit(psht);
  229. try
  230. {
  231. m_strName = PciCluster()->StrName();
  232. m_strDesc = PciCluster()->StrDescription();
  233. m_strVendorID = PciCluster()->Cvi().szVendorId;
  234. m_strVersion.Format( IDS_OP_VERSION_NUMBER_FORMAT, PciCluster()->Cvi().MajorVersion );
  235. } // try
  236. catch (CException * pe)
  237. {
  238. pe->ReportError();
  239. pe->Delete();
  240. m_bReadOnly = TRUE;
  241. } // catch: CException
  242. return bSuccess;
  243. } //*** CClusterGeneralPage::BInit()
  244. /////////////////////////////////////////////////////////////////////////////
  245. //++
  246. //
  247. // CClusterGeneralPage::DoDataExchange
  248. //
  249. // Routine Description:
  250. // Do data exchange between the dialog and the class.
  251. //
  252. // Arguments:
  253. // pDX [IN OUT] Data exchange object
  254. //
  255. // Return Value:
  256. // None.
  257. //
  258. //--
  259. /////////////////////////////////////////////////////////////////////////////
  260. void CClusterGeneralPage::DoDataExchange(CDataExchange * pDX)
  261. {
  262. CWaitCursor wc;
  263. CString strClusName;
  264. CBasePropertyPage::DoDataExchange(pDX);
  265. //{{AFX_DATA_MAP(CClusterGeneralPage)
  266. DDX_Control(pDX, IDC_PP_CLUS_NAME, m_editName);
  267. DDX_Control(pDX, IDC_PP_CLUS_DESC, m_editDesc);
  268. DDX_Text(pDX, IDC_PP_CLUS_DESC, m_strDesc);
  269. DDX_Text(pDX, IDC_PP_CLUS_VENDOR_ID, m_strVendorID);
  270. DDX_Text(pDX, IDC_PP_CLUS_VERSION, m_strVersion);
  271. //}}AFX_DATA_MAP
  272. if (pDX->m_bSaveAndValidate)
  273. {
  274. CLRTL_NAME_STATUS cnStatus;
  275. //
  276. // get the name from the control into a temp variable
  277. //
  278. DDX_Text(pDX, IDC_PP_CLUS_NAME, strClusName);
  279. if ( (strClusName != m_strName)
  280. && !ClRtlIsNetNameValid(strClusName, &cnStatus, FALSE /*CheckIfExists*/))
  281. {
  282. CString strMsg;
  283. UINT idsError;
  284. switch (cnStatus)
  285. {
  286. case NetNameTooLong:
  287. idsError = IDS_INVALID_CLUSTER_NAME_TOO_LONG;
  288. break;
  289. case NetNameInvalidChars:
  290. idsError = IDS_INVALID_CLUSTER_NAME_INVALID_CHARS;
  291. break;
  292. case NetNameInUse:
  293. idsError = IDS_INVALID_CLUSTER_NAME_IN_USE;
  294. break;
  295. case NetNameDNSNonRFCChars:
  296. idsError = IDS_INVALID_CLUSTER_NAME_INVALID_DNS_CHARS;
  297. break;
  298. case NetNameSystemError:
  299. {
  300. DWORD scError = GetLastError();
  301. CNTException nte(scError, IDS_ERROR_VALIDATING_NETWORK_NAME, (LPCWSTR) strClusName);
  302. nte.ReportError();
  303. pDX->Fail();
  304. }
  305. default:
  306. idsError = IDS_INVALID_CLUSTER_NAME;
  307. break;
  308. } // switch: cnStatus
  309. strMsg.LoadString(idsError);
  310. if ( idsError == IDS_INVALID_CLUSTER_NAME_INVALID_DNS_CHARS )
  311. {
  312. int id = AfxMessageBox(strMsg, MB_YESNO | MB_DEFBUTTON2 | MB_ICONEXCLAMATION );
  313. if ( id == IDNO )
  314. {
  315. strMsg.Empty();
  316. pDX->Fail();
  317. }
  318. }
  319. else
  320. {
  321. AfxMessageBox(strMsg, MB_ICONEXCLAMATION);
  322. strMsg.Empty(); // exception prep
  323. pDX->Fail();
  324. }
  325. m_strName = strClusName;
  326. } // if: cluster name has changed and an invalid network name was specified
  327. } // if: getting data from the dialog
  328. else
  329. {
  330. //
  331. // populate the control with data from the member variable
  332. //
  333. DDX_Text(pDX, IDC_PP_CLUS_NAME, m_strName);
  334. } // else: setting data to the dialog
  335. } //*** CClusterGeneralPage::DoDataExchange()
  336. /////////////////////////////////////////////////////////////////////////////
  337. //++
  338. //
  339. // CClusterGeneralPage::OnInitDialog
  340. //
  341. // Routine Description:
  342. // Handler for the WM_INITDIALOG message.
  343. //
  344. // Arguments:
  345. // None.
  346. //
  347. // Return Value:
  348. // TRUE Focus not set yet.
  349. // FALSE Focus already set.
  350. //
  351. //--
  352. /////////////////////////////////////////////////////////////////////////////
  353. BOOL CClusterGeneralPage::OnInitDialog(void)
  354. {
  355. // Call the base class method.
  356. CBasePropertyPage::OnInitDialog();
  357. // Set limits on the edit controls.
  358. m_editName.SetLimitText(MAX_CLUSTERNAME_LENGTH);
  359. // If read-only, set all controls to be either disabled or read-only.
  360. if (BReadOnly())
  361. {
  362. m_editName.SetReadOnly(TRUE);
  363. m_editDesc.SetReadOnly(TRUE);
  364. } // if: sheet is read-only
  365. return FALSE; // return TRUE unless you set the focus to a control
  366. // EXCEPTION: OCX Property Pages should return FALSE
  367. } //*** CClusterGeneralPage::OnInitDialog()
  368. /////////////////////////////////////////////////////////////////////////////
  369. //++
  370. //
  371. // CClusterGeneralPage::OnSetActive
  372. //
  373. // Routine Description:
  374. // Handler for the PSN_SETACTIVE message.
  375. //
  376. // Arguments:
  377. // None.
  378. //
  379. // Return Value:
  380. // TRUE Page successfully initialized.
  381. // FALSE Page not initialized.
  382. //
  383. //--
  384. /////////////////////////////////////////////////////////////////////////////
  385. BOOL CClusterGeneralPage::OnSetActive(void)
  386. {
  387. return CBasePropertyPage::OnSetActive();
  388. } //*** CClusterGeneralPage::OnSetActive()
  389. /////////////////////////////////////////////////////////////////////////////
  390. //++
  391. //
  392. // CClusterGeneralPage::OnKillActive
  393. //
  394. // Routine Description:
  395. // Handler for the PSN_KILLACTIVE message.
  396. //
  397. // Arguments:
  398. // None.
  399. //
  400. // Return Value:
  401. // TRUE Page focus successfully killed.
  402. // FALSE Error killing page focus.
  403. //
  404. //--
  405. /////////////////////////////////////////////////////////////////////////////
  406. BOOL CClusterGeneralPage::OnKillActive(void)
  407. {
  408. return CBasePropertyPage::OnKillActive();
  409. } //*** CClusterGeneralPage::OnKillActive()
  410. /////////////////////////////////////////////////////////////////////////////
  411. //++
  412. //
  413. // CClusterGeneralPage::OnApply
  414. //
  415. // Routine Description:
  416. // Handler for the PSN_APPLY message.
  417. //
  418. // Arguments:
  419. // None.
  420. //
  421. // Return Value:
  422. // TRUE Page successfully applied.
  423. // FALSE Error applying page.
  424. //
  425. //--
  426. /////////////////////////////////////////////////////////////////////////////
  427. BOOL CClusterGeneralPage::OnApply(void)
  428. {
  429. // Set the data from the page in the cluster item.
  430. try
  431. {
  432. CWaitCursor wc;
  433. PciCluster()->SetDescription(m_strDesc);
  434. PciCluster()->SetName(m_strName);
  435. } // try
  436. catch (CException * pe)
  437. {
  438. pe->ReportError();
  439. pe->Delete();
  440. return FALSE;
  441. } // catch: CException
  442. return CBasePropertyPage::OnApply();
  443. } //*** CClusterGeneralPage::OnApply()
  444. /*
  445. /////////////////////////////////////////////////////////////////////////////
  446. //++
  447. //
  448. // CClusterGeneralPage::OnBnClickedPermissions
  449. //
  450. // Routine Description:
  451. // Handler for the BN_CLICKED message on the Permissions push button.
  452. //
  453. // Arguments:
  454. // None.
  455. //
  456. // Return Value:
  457. // None.
  458. //
  459. //--
  460. /////////////////////////////////////////////////////////////////////////////
  461. void CClusterGeneralPage::OnBnClickedPermissions(void)
  462. {
  463. LONG lResult;
  464. BOOL bSecDescModified;
  465. PSECURITY_DESCRIPTOR psec = NULL;
  466. CString strServer;
  467. CResource * pciRes = NULL;
  468. CWaitCursor wc;
  469. // Find the cluster name resource.
  470. {
  471. POSITION pos;
  472. pos = PciCluster()->Pdoc()->LpciResources().GetHeadPosition();
  473. while (pos != NULL)
  474. {
  475. pciRes = (CResource *) PciCluster()->Pdoc()->LpciResources().GetNext(pos);
  476. ASSERT_VALID(pciRes);
  477. if ( (pciRes->StrRealResourceType().CompareNoCase(CLUS_RESTYPE_NAME_NETNAME))
  478. && pciRes->BCore() )
  479. break;
  480. pciRes = NULL;
  481. } // while: more resources in the list
  482. ASSERT(pciRes != NULL);
  483. } // Find the cluster name resource
  484. strServer.Format(_T("\\\\%s"), PciCluster()->StrName());
  485. lResult = EditClusterAcl(
  486. m_hWnd,
  487. strServer,
  488. PciCluster()->StrName(),
  489. pciRes->StrOwner(),
  490. m_psec,
  491. &bSecDescModified,
  492. &psec
  493. );
  494. if (bSecDescModified)
  495. {
  496. delete [] m_psec;
  497. m_psec = psec;
  498. m_bSecurityChanged = TRUE;
  499. SetModified(TRUE);
  500. } // if: data changed
  501. } //*** CClusterGeneralPage::OnBnClickedPermissions()
  502. */
  503. //*************************************************************************//
  504. /////////////////////////////////////////////////////////////////////////////
  505. // CClusterQuorumPage property page
  506. /////////////////////////////////////////////////////////////////////////////
  507. IMPLEMENT_DYNCREATE(CClusterQuorumPage, CBasePropertyPage)
  508. /////////////////////////////////////////////////////////////////////////////
  509. // Message Maps
  510. BEGIN_MESSAGE_MAP(CClusterQuorumPage, CBasePropertyPage)
  511. //{{AFX_MSG_MAP(CClusterQuorumPage)
  512. ON_CBN_DBLCLK(IDC_PP_CLUS_QUORUM_RESOURCE, OnDblClkQuorumResource)
  513. ON_CBN_SELCHANGE(IDC_PP_CLUS_QUORUM_RESOURCE, OnChangeQuorumResource)
  514. ON_WM_DESTROY()
  515. //}}AFX_MSG_MAP
  516. ON_CBN_SELCHANGE(IDC_PP_CLUS_QUORUM_PARTITION, CBasePropertyPage::OnChangeCtrl)
  517. ON_EN_CHANGE(IDC_PP_CLUS_QUORUM_ROOT_PATH, CBasePropertyPage::OnChangeCtrl)
  518. ON_EN_CHANGE(IDC_PP_CLUS_QUORUM_MAX_LOG_SIZE, CBasePropertyPage::OnChangeCtrl)
  519. END_MESSAGE_MAP()
  520. /////////////////////////////////////////////////////////////////////////////
  521. //++
  522. //
  523. // CClusterQuorumPage::CClusterQuorumPage
  524. //
  525. // Routine Description:
  526. // Constructor.
  527. //
  528. // Arguments:
  529. // None.
  530. //
  531. // Return Value:
  532. // None.
  533. //
  534. //--
  535. /////////////////////////////////////////////////////////////////////////////
  536. CClusterQuorumPage::CClusterQuorumPage(void)
  537. : CBasePropertyPage(IDD, g_aHelpIDs_IDD_PP_CLUSTER_QUORUM)
  538. {
  539. //{{AFX_DATA_INIT(CClusterQuorumPage)
  540. m_strQuorumResource = _T("");
  541. m_strPartition = _T("");
  542. m_strRootPath = _T("");
  543. m_nMaxLogSize = 0;
  544. //}}AFX_DATA_INIT
  545. m_pbDiskInfo = NULL;
  546. m_cbDiskInfo = 0;
  547. m_bControlsInitialized = FALSE;
  548. m_ppid = NULL;
  549. } //*** CClusterQuorumPage::CClusterQuorumPage()
  550. /////////////////////////////////////////////////////////////////////////////
  551. //++
  552. //
  553. // CClusterQuorumPage::~CClusterQuorumPage
  554. //
  555. // Routine Description:
  556. // Destructor.
  557. //
  558. // Arguments:
  559. // None.
  560. //
  561. // Return Value:
  562. // None.
  563. //
  564. //--
  565. /////////////////////////////////////////////////////////////////////////////
  566. CClusterQuorumPage::~CClusterQuorumPage(void)
  567. {
  568. delete [] m_pbDiskInfo;
  569. } //*** CClusterQuorumPage::~CClusterQuorumPage()
  570. /////////////////////////////////////////////////////////////////////////////
  571. //++
  572. //
  573. // CClusterQuorumPage::OnDestroy
  574. //
  575. // Routine Description:
  576. // Handler for the WM_DESTROY message.
  577. //
  578. // Arguments:
  579. // None.
  580. //
  581. // Return Value:
  582. // None.
  583. //
  584. //--
  585. /////////////////////////////////////////////////////////////////////////////
  586. void CClusterQuorumPage::OnDestroy(void)
  587. {
  588. // If the controls have been initialized, clear the resource combobox.
  589. if ( BControlsInitialized() )
  590. {
  591. ClearResourceList();
  592. ClearPartitionList();
  593. }
  594. delete [] m_pbDiskInfo;
  595. m_pbDiskInfo = NULL;
  596. m_cbDiskInfo = 0;
  597. // Call the base class method.
  598. CBasePropertyPage::OnDestroy();
  599. } //*** CClusterQuorumPage::OnDestroy()
  600. /////////////////////////////////////////////////////////////////////////////
  601. //++
  602. //
  603. // CClusterQuorumPage::BInit
  604. //
  605. // Routine Description:
  606. // Initialize the page.
  607. //
  608. // Arguments:
  609. // psht [IN OUT] Property sheet to which this page belongs.
  610. //
  611. // Return Value:
  612. // TRUE Page initialized successfully.
  613. // FALSE Page failed to initialize.
  614. //
  615. //--
  616. /////////////////////////////////////////////////////////////////////////////
  617. BOOL CClusterQuorumPage::BInit(IN OUT CBaseSheet * psht)
  618. {
  619. BOOL bSuccess;
  620. CWaitCursor wc;
  621. ASSERT_KINDOF(CClusterPropSheet, psht);
  622. bSuccess = CBasePropertyPage::BInit(psht);
  623. try
  624. {
  625. // Get the current quorum resource.
  626. m_strQuorumResource = PciCluster()->StrQuorumResource();
  627. SplitDeviceName(
  628. PciCluster()->StrQuorumPath(),
  629. m_strPartition.GetBuffer( _MAX_PATH ),
  630. m_strRootPath.GetBuffer( _MAX_PATH )
  631. );
  632. m_strPartition.ReleaseBuffer();
  633. m_strRootPath.ReleaseBuffer();
  634. m_nMaxLogSize = (PciCluster()->NMaxQuorumLogSize() + 1023) / 1024;
  635. m_strCurrentPartition = m_strPartition;
  636. m_strCurrentRootPath = m_strRootPath;
  637. } // try
  638. catch (CException * pe)
  639. {
  640. pe->ReportError();
  641. pe->Delete();
  642. m_bReadOnly = TRUE;
  643. } // catch: CException
  644. return bSuccess;
  645. } //*** CClusterQuorumPage::BInit()
  646. /////////////////////////////////////////////////////////////////////////////
  647. //++
  648. //
  649. // CClusterQuorumPage::DoDataExchange
  650. //
  651. // Routine Description:
  652. // Do data exchange between the dialog and the class.
  653. //
  654. // Arguments:
  655. // pDX [IN OUT] Data exchange object
  656. //
  657. // Return Value:
  658. // None.
  659. //
  660. //--
  661. /////////////////////////////////////////////////////////////////////////////
  662. void CClusterQuorumPage::DoDataExchange(CDataExchange * pDX)
  663. {
  664. CWaitCursor wc;
  665. CBasePropertyPage::DoDataExchange(pDX);
  666. //{{AFX_DATA_MAP(CClusterQuorumPage)
  667. DDX_Control(pDX, IDC_PP_CLUS_QUORUM_MAX_LOG_SIZE, m_editMaxLogSize);
  668. DDX_Control(pDX, IDC_PP_CLUS_QUORUM_ROOT_PATH, m_editRootPath);
  669. DDX_Control(pDX, IDC_PP_CLUS_QUORUM_PARTITION, m_cboxPartition);
  670. DDX_Control(pDX, IDC_PP_CLUS_QUORUM_RESOURCE, m_cboxQuorumResource);
  671. DDX_CBString(pDX, IDC_PP_CLUS_QUORUM_RESOURCE, m_strQuorumResource);
  672. DDX_CBString(pDX, IDC_PP_CLUS_QUORUM_PARTITION, m_strPartition);
  673. DDX_Text(pDX, IDC_PP_CLUS_QUORUM_ROOT_PATH, m_strRootPath);
  674. DDX_Text(pDX, IDC_PP_CLUS_QUORUM_MAX_LOG_SIZE, m_nMaxLogSize);
  675. //}}AFX_DATA_MAP
  676. if ( ! pDX->m_bSaveAndValidate )
  677. {
  678. // Fill the quorum resource combobox with resource names.
  679. FillResourceList();
  680. } // if: setting data to the dialog
  681. m_bControlsInitialized = TRUE;
  682. if (pDX->m_bSaveAndValidate || !BReadOnly())
  683. DDX_Number(pDX, IDC_PP_CLUS_QUORUM_MAX_LOG_SIZE, m_nMaxLogSize, 1, 0xffffffff / 1024);
  684. if (pDX->m_bSaveAndValidate)
  685. {
  686. DDV_RequiredText(pDX, IDC_PP_CLUS_QUORUM_RESOURCE, IDC_PP_CLUS_QUORUM_RESOURCE_LABEL, m_strQuorumResource);
  687. DDV_RequiredText(pDX, IDC_PP_CLUS_QUORUM_PARTITION, IDC_PP_CLUS_QUORUM_PARTITION_LABEL, m_strPartition);
  688. DDV_RequiredText(pDX, IDC_PP_CLUS_QUORUM_ROOT_PATH, IDC_PP_CLUS_QUORUM_ROOT_PATH_LABEL, m_strRootPath);
  689. } // if: getting data from the dialog
  690. } //*** CClusterQuorumPage::DoDataExchange()
  691. /////////////////////////////////////////////////////////////////////////////
  692. //++
  693. //
  694. // CClusterQuorumPage::OnInitDialog
  695. //
  696. // Routine Description:
  697. // Handler for the WM_INITDIALOG message.
  698. //
  699. // Arguments:
  700. // None.
  701. //
  702. // Return Value:
  703. // TRUE Focus not set yet.
  704. // FALSE Focus already set.
  705. //
  706. //--
  707. /////////////////////////////////////////////////////////////////////////////
  708. BOOL CClusterQuorumPage::OnInitDialog(void)
  709. {
  710. // Call the base class method.
  711. CBasePropertyPage::OnInitDialog();
  712. // If read-only, set all controls to be either disabled or read-only.
  713. if (BReadOnly())
  714. {
  715. m_cboxQuorumResource.EnableWindow(FALSE);
  716. m_cboxPartition.EnableWindow(FALSE);
  717. m_editRootPath.SetReadOnly();
  718. m_editMaxLogSize.SetReadOnly();
  719. } // if: sheet is read-only
  720. else
  721. {
  722. m_editRootPath.SetLimitText(MAX_PATH);
  723. }
  724. return FALSE; // return TRUE unless you set the focus to a control
  725. // EXCEPTION: OCX Property Pages should return FALSE
  726. } //*** CClusterQuorumPage::OnInitDialog()
  727. /////////////////////////////////////////////////////////////////////////////
  728. //++
  729. //
  730. // CClusterQuorumPage::OnApply
  731. //
  732. // Routine Description:
  733. // Handler for the PSN_APPLY message.
  734. //
  735. // Arguments:
  736. // None.
  737. //
  738. // Return Value:
  739. // TRUE Page successfully applied.
  740. // FALSE Error applying page.
  741. //
  742. //--
  743. /////////////////////////////////////////////////////////////////////////////
  744. BOOL CClusterQuorumPage::OnApply(void)
  745. {
  746. CWaitCursor wc;
  747. CString strQuorumPath;
  748. CString strTemp;
  749. DWORD nLen;
  750. CResource * pciRes = NULL;
  751. int nSelected;
  752. int nCount;
  753. CLUSTER_RESOURCE_STATE crs = ClusterResourceStateUnknown;
  754. //
  755. // Get the currently selected resource from the combo box.
  756. //
  757. nSelected = m_cboxQuorumResource.GetCurSel();
  758. nCount = m_cboxQuorumResource.GetCount();
  759. if ( CB_ERR != nSelected && 0 < nCount )
  760. {
  761. pciRes = (CResource *) m_cboxQuorumResource.GetItemDataPtr(nSelected);
  762. }
  763. //
  764. // If we successfully retrieved a resource make sure it's online.
  765. //
  766. if ( NULL != pciRes )
  767. {
  768. crs = pciRes->Crs();
  769. if ( ClusterResourceOnline != crs )
  770. {
  771. //
  772. // Prompt the user whether or not they'd like to online the resource.
  773. //
  774. strTemp.FormatMessage( IDS_ONLINE_QUORUM_RESOURCE_PROMPT, pciRes->StrName() );
  775. if ( IDYES == AfxMessageBox( strTemp, MB_YESNO | MB_ICONQUESTION ) )
  776. {
  777. CWaitForResourceOnlineDlg dlg( pciRes, AfxGetMainWnd() );
  778. pciRes->OnCmdBringOnline();
  779. dlg.DoModal();
  780. crs = pciRes->Crs();
  781. }
  782. else
  783. {
  784. return FALSE;
  785. }
  786. } // if: resource !online
  787. } // if: pciRes !NULL
  788. else
  789. {
  790. // No resource was selected - this should never happen.
  791. AfxMessageBox( IDS_SELECT_QUORUM_RESOURCE_ERROR, MB_OK | MB_ICONEXCLAMATION );
  792. return FALSE;
  793. }
  794. // Set the data from the page in the cluster item.
  795. if ( ClusterResourceOnline == crs )
  796. {
  797. try {
  798. strTemp = _T("");
  799. if( m_ppid != NULL && !m_strRootPath.IsEmpty() )
  800. {
  801. nLen = _tcslen( m_ppid->szDeviceName );
  802. //
  803. // Concatenate the strings before calling SetQuorumResource, but make sure that
  804. // there is only one backslash between them.
  805. //
  806. strTemp = m_ppid->szDeviceName;
  807. if( ( m_ppid->szDeviceName[nLen-1] != _T('\\') ) && ( m_strRootPath[0] != _T('\\') ) )
  808. {
  809. strTemp += _T('\\');
  810. }
  811. else if( ( m_ppid->szDeviceName[nLen-1] == _T('\\') ) && ( m_strRootPath[0] == _T('\\') ) )
  812. {
  813. strTemp.SetAt( nLen-1, _T('\0') );
  814. }
  815. } // if: neither string is empty
  816. strQuorumPath.Format( _T("%s%s"), strTemp, m_strRootPath );
  817. PciCluster()->SetQuorumResource(
  818. m_strQuorumResource,
  819. strQuorumPath,
  820. (m_nMaxLogSize * 1024)
  821. );
  822. m_strCurrentPartition = m_strPartition;
  823. m_strCurrentRootPath = m_strRootPath;
  824. } // try
  825. catch (CException * pe)
  826. {
  827. pe->ReportError();
  828. pe->Delete();
  829. strQuorumPath.Empty();
  830. return FALSE;
  831. } // catch: CException
  832. } // if: the resource is online
  833. return CBasePropertyPage::OnApply();
  834. } //*** CClusterQuorumPage::OnApply()
  835. /////////////////////////////////////////////////////////////////////////////
  836. //++
  837. //
  838. // CClusterQuorumPage::OnDblClkQuorumResource
  839. //
  840. // Routine Description:
  841. // Handler for the CBN_DBLCLK message on the Quorum Resource combo box.
  842. //
  843. // Arguments:
  844. // None.
  845. //
  846. // Return Value:
  847. // None.
  848. //
  849. //--
  850. /////////////////////////////////////////////////////////////////////////////
  851. void CClusterQuorumPage::OnDblClkQuorumResource(void)
  852. {
  853. int nSelected;
  854. CResource * pciRes;
  855. // Get the selected resource.
  856. nSelected = m_cboxQuorumResource.GetCurSel();
  857. ASSERT(nSelected != CB_ERR);
  858. // Get the resource object.
  859. pciRes = (CResource *) m_cboxQuorumResource.GetItemDataPtr(nSelected);
  860. ASSERT_VALID(pciRes);
  861. ASSERT_KINDOF(CResource, pciRes);
  862. // Display properties for the resource.
  863. pciRes->OnCmdProperties();
  864. } //*** CClusterQuorumPage::OnDblClkQuorumResource()
  865. /////////////////////////////////////////////////////////////////////////////
  866. //++
  867. //
  868. // CClusterQuorumPage::OnChangeQuorumResource
  869. //
  870. // Routine Description:
  871. // Handler for the CBN_SELCHANGE message on the Quorum Resource combobox.
  872. //
  873. // Arguments:
  874. // None.
  875. //
  876. // Return Value:
  877. // None.
  878. //
  879. //--
  880. /////////////////////////////////////////////////////////////////////////////
  881. void CClusterQuorumPage::OnChangeQuorumResource(void)
  882. {
  883. int nSelected;
  884. CResource * pciRes;
  885. OnChangeCtrl();
  886. // Get the selected resource.
  887. nSelected = m_cboxQuorumResource.GetCurSel();
  888. ASSERT(nSelected != CB_ERR);
  889. // Get the resource object.
  890. pciRes = (CResource *) m_cboxQuorumResource.GetItemDataPtr(nSelected);
  891. ASSERT_VALID(pciRes);
  892. ASSERT_KINDOF(CResource, pciRes);
  893. // If the resource really did change and is the same as the currently
  894. // saved resource, set the partition to the currently saved partition.
  895. // Otherwise, clear the partition setting and use the first one.
  896. if (pciRes->StrName() != m_strQuorumResource)
  897. {
  898. if (pciRes->StrName() == PciCluster()->StrQuorumResource())
  899. {
  900. m_strPartition = m_strCurrentPartition;
  901. m_strRootPath = m_strCurrentRootPath;
  902. } // if: changed to previously set quorum resource
  903. else
  904. {
  905. m_strPartition = _T("");
  906. //m_strRootPath = _T("\\") CLUS_NAME_DEFAULT_FILESPATH;
  907. } // else: changed to new quorum resource
  908. m_strQuorumResource = pciRes->StrName();
  909. } // if: resource really did change
  910. UpdateData(FALSE /*bSaveAndValidate*/);
  911. } //*** CClusterQuorumPage::OnChangeQuorumResource()
  912. /////////////////////////////////////////////////////////////////////////////
  913. //++
  914. //
  915. // CClusterQuorumPage::ClearResourceList
  916. //
  917. // Routine Description:
  918. // Clear the resource list and release references to pointers.
  919. //
  920. // Arguments:
  921. // None.
  922. //
  923. // Return Value:
  924. // None.
  925. //
  926. //--
  927. /////////////////////////////////////////////////////////////////////////////
  928. void CClusterQuorumPage::ClearResourceList(void)
  929. {
  930. int cItems;
  931. int iItem;
  932. CResource * pciRes;
  933. cItems = m_cboxQuorumResource.GetCount();
  934. for (iItem = 0 ; iItem < cItems ; iItem++)
  935. {
  936. pciRes = (CResource *) m_cboxQuorumResource.GetItemDataPtr(iItem);
  937. ASSERT_VALID(pciRes);
  938. ASSERT_KINDOF(CResource, pciRes);
  939. pciRes->Release();
  940. } // for: each item in the list
  941. m_cboxQuorumResource.ResetContent();
  942. } //*** CClusterQuorumPage::ClearResourceList()
  943. /////////////////////////////////////////////////////////////////////////////
  944. //++
  945. //
  946. // CClusterQuorumPage::FillResourceList
  947. //
  948. // Routine Description:
  949. // Fill the quorum resource combobox with all resources and select
  950. // the quorum resource.
  951. //
  952. // Arguments:
  953. // None.
  954. //
  955. // Return Value:
  956. // None.
  957. //
  958. //--
  959. /////////////////////////////////////////////////////////////////////////////
  960. void CClusterQuorumPage::FillResourceList(void)
  961. {
  962. POSITION pos;
  963. int nIndex;
  964. CResource * pciRes;
  965. CResource * pciSelected = NULL;
  966. CWaitCursor wc;
  967. // Clear the list.
  968. ClearResourceList();
  969. pos = PciCluster()->Pdoc()->LpciResources().GetHeadPosition();
  970. while (pos != NULL)
  971. {
  972. // Get the next resource.
  973. pciRes = (CResource *) PciCluster()->Pdoc()->LpciResources().GetNext(pos);
  974. ASSERT_VALID(pciRes);
  975. ASSERT_KINDOF(CResource, pciRes);
  976. // If it is quorum capable, add it to the list.
  977. try
  978. {
  979. if (pciRes->BQuorumCapable())
  980. {
  981. nIndex = m_cboxQuorumResource.AddString(pciRes->StrName());
  982. ASSERT(nIndex != CB_ERR);
  983. m_cboxQuorumResource.SetItemDataPtr(nIndex, pciRes);
  984. pciRes->AddRef();
  985. if (m_strQuorumResource == pciRes->StrName())
  986. pciSelected = pciRes;
  987. } // if: resource can be a quorum resource
  988. } // try
  989. catch (...)
  990. {
  991. // Ignore all errors because there is really nothing we can do.
  992. // Displaying a message isn't really very useful.
  993. } // catch: Anything
  994. } // while: more items in the list
  995. // Select the current quorum resource in the list.
  996. {
  997. int istr;
  998. istr = m_cboxQuorumResource.FindStringExact(-1, m_strQuorumResource);
  999. ASSERT(istr != CB_ERR);
  1000. VERIFY(m_cboxQuorumResource.SetCurSel(istr) != CB_ERR);
  1001. } // Select the current quorum resource in the list
  1002. // Check if the current quorum device is in the list of quorum capable resources
  1003. if (pciSelected != NULL)
  1004. {
  1005. // Fill the partition list.
  1006. FillPartitionList(pciSelected);
  1007. }
  1008. else
  1009. {
  1010. // There is nothing we can do in this case. There is something seriously wrong
  1011. // with the cluster.
  1012. CNTException nte(
  1013. ERROR_QUORUM_DISK_NOT_FOUND,
  1014. IDS_GET_QUORUM_DEVICES_ERROR
  1015. );
  1016. nte.ReportError();
  1017. } // else: the current quorum device is not in the list of quorum capable resources
  1018. } //*** CClusterQuorumPage::FillResourceList()
  1019. /////////////////////////////////////////////////////////////////////////////
  1020. //++
  1021. //
  1022. // CClusterQuorumPage::ClearPartitionList
  1023. //
  1024. // Routine Description:
  1025. // Clear the partition list and release references to pointers.
  1026. //
  1027. // Arguments:
  1028. // None.
  1029. //
  1030. // Return Value:
  1031. // None.
  1032. //
  1033. //--
  1034. /////////////////////////////////////////////////////////////////////////////
  1035. void CClusterQuorumPage::ClearPartitionList(void)
  1036. {
  1037. int cItems;
  1038. int iItem;
  1039. SPartitionItemData * ppid = NULL;
  1040. cItems = m_cboxPartition.GetCount();
  1041. for ( iItem = 0 ; iItem < cItems ; iItem++ )
  1042. {
  1043. ppid = (SPartitionItemData *) m_cboxPartition.GetItemDataPtr( iItem );
  1044. delete ppid;
  1045. } // for: each item in the list
  1046. m_cboxPartition.ResetContent();
  1047. } //*** CClusterQuorumPage::ClearPartitionList()
  1048. /////////////////////////////////////////////////////////////////////////////
  1049. //++
  1050. //
  1051. // CClusterQuorumPage::FillPartitionList
  1052. //
  1053. // Routine Description:
  1054. // Fill the partition combobox with all partitions available on the
  1055. // currently selected quorum resource.
  1056. //
  1057. // Arguments:
  1058. // pciRes [IN OUT] Currently selected quorum resource.
  1059. //
  1060. // Return Value:
  1061. // None.
  1062. //
  1063. //--
  1064. /////////////////////////////////////////////////////////////////////////////
  1065. void CClusterQuorumPage::FillPartitionList(IN OUT CResource * pciRes)
  1066. {
  1067. CString strPartitionInfo;
  1068. CLUSPROP_BUFFER_HELPER buf;
  1069. DWORD cbData;
  1070. DWORD cbBuf;
  1071. int nIndex;
  1072. SPartitionItemData * ppid = NULL;
  1073. CWaitCursor wc;
  1074. ASSERT_VALID(pciRes);
  1075. // Clear the list.
  1076. ClearPartitionList();
  1077. // Get disk info for this resource.
  1078. if (BGetDiskInfo(*pciRes))
  1079. {
  1080. buf.pb = m_pbDiskInfo;
  1081. cbBuf = m_cbDiskInfo;
  1082. while (buf.pSyntax->dw != CLUSPROP_SYNTAX_ENDMARK)
  1083. {
  1084. // Calculate the size of the value.
  1085. cbData = sizeof(*buf.pValue) + ALIGN_CLUSPROP(buf.pValue->cbLength);
  1086. ASSERT(cbData <= cbBuf);
  1087. // Parse the value.
  1088. if (buf.pSyntax->dw == CLUSPROP_SYNTAX_PARTITION_INFO)
  1089. {
  1090. // Add the partition to the combobox if it is a usable partition
  1091. // and it hasn't been added already.
  1092. if ( (buf.pPartitionInfoValue->dwFlags & CLUSPROP_PIFLAG_USABLE)
  1093. && (m_cboxPartition.FindString(-1, buf.pPartitionInfoValue->szDeviceName) == CB_ERR))
  1094. {
  1095. try
  1096. {
  1097. // Construct the name to display to the user
  1098. // and add the item to the combobox.
  1099. strPartitionInfo.Format(
  1100. ( buf.pPartitionInfoValue->szVolumeLabel[ 0 ] ? _T("%ls (%ls) ") : _T("%ls ") ),
  1101. buf.pPartitionInfoValue->szDeviceName,
  1102. buf.pPartitionInfoValue->szVolumeLabel
  1103. );
  1104. nIndex = m_cboxPartition.AddString( strPartitionInfo );
  1105. ASSERT( nIndex != CB_ERR );
  1106. // Construct a partition item data structure,
  1107. // which consists of the device name, a partition
  1108. // name, and a root path name.
  1109. ppid = new SPartitionItemData;
  1110. if ( ppid == NULL )
  1111. {
  1112. AfxThrowMemoryException();
  1113. }
  1114. ASSERT( ( _tcslen( buf.pPartitionInfoValue->szDeviceName ) + 1 ) * sizeof( TCHAR ) <= sizeof( ppid->szDeviceName ) );
  1115. _tcscpy( ppid->szDeviceName, buf.pPartitionInfoValue->szDeviceName );
  1116. SplitDeviceName(
  1117. buf.pPartitionInfoValue->szDeviceName,
  1118. ppid->szPartitionName,
  1119. ppid->szBaseRootPath
  1120. );
  1121. m_cboxPartition.SetItemDataPtr( nIndex, ppid );
  1122. } // try
  1123. catch (...)
  1124. {
  1125. // Ignore all errors because there is really nothing we can do.
  1126. // Displaying a message isn't really very useful.
  1127. } // catch: Anything
  1128. } // if: partition not added yet
  1129. } // if: partition info
  1130. // Advance the buffer pointer
  1131. buf.pb += cbData;
  1132. cbBuf -= cbData;
  1133. } // while: more values
  1134. } // if: got disk info successfully
  1135. // Select the current partition in the list.
  1136. if (m_strPartition.GetLength() > 0)
  1137. {
  1138. nIndex = m_cboxPartition.SelectString( -1, m_strPartition );
  1139. } // if: there is a current partition
  1140. else
  1141. {
  1142. if (m_cboxPartition.GetCount() > 0)
  1143. {
  1144. nIndex = m_cboxPartition.SetCurSel( 0 );
  1145. } // if: combobox has any entries
  1146. else
  1147. {
  1148. nIndex = -1;
  1149. } // else: no entries in the list
  1150. } // else: no current selection
  1151. if ( nIndex != -1 )
  1152. {
  1153. size_t cchBaseRootPath;
  1154. LPTSTR pszRootPath;
  1155. // Save the item data.
  1156. // If the base root path is contained in the root path,
  1157. // remove the base root path from the root path.
  1158. m_ppid = (SPartitionItemData *) m_cboxPartition.GetItemDataPtr( nIndex );
  1159. if ( m_ppid != NULL )
  1160. {
  1161. m_strPartition = m_ppid->szPartitionName;
  1162. cchBaseRootPath = _tcslen( m_ppid->szBaseRootPath );
  1163. if ( ( cchBaseRootPath > 0 )
  1164. && ( _tcsncmp( m_strRootPath, m_ppid->szBaseRootPath, cchBaseRootPath ) == 0 ) )
  1165. {
  1166. pszRootPath = m_strRootPath.GetBuffer( 1 );
  1167. _tcscpy( pszRootPath, &pszRootPath[ cchBaseRootPath ] );
  1168. m_strRootPath.ReleaseBuffer();
  1169. } // if: root path contains base root path
  1170. } // if: ppid retrieved successfully
  1171. } // if: an item is selected
  1172. else if ( m_cboxPartition.GetCount() > 0 )
  1173. {
  1174. ASSERT( nIndex != CB_ERR );
  1175. } // else if: combobox is not empty
  1176. } //*** CClusterQuorumPage::FillPartitionList()
  1177. /////////////////////////////////////////////////////////////////////////////
  1178. //++
  1179. //
  1180. // CClusterQuorumPage::SplitDeviceName
  1181. //
  1182. // Routine Description:
  1183. // Split a device name into a partition name and a root path. It is
  1184. // expected that the output buffers are at least of size _MAX_PATH.
  1185. //
  1186. // Arguments:
  1187. // pszDeviceNameIn Device name to split.
  1188. // pszPartitionNameOut Partition name buffer to fill.
  1189. // pszRootPathOut Root path buffer to fill.
  1190. //
  1191. // Return Value:
  1192. // None.
  1193. //
  1194. //--
  1195. /////////////////////////////////////////////////////////////////////////////
  1196. void
  1197. CClusterQuorumPage::SplitDeviceName(
  1198. LPCTSTR pszDeviceNameIn,
  1199. LPTSTR pszPartitionNameOut,
  1200. LPTSTR pszRootPathOut
  1201. )
  1202. {
  1203. //
  1204. // Construct the partition name and base root path from the device name.
  1205. //
  1206. //
  1207. // If we have a UNC path it will end up looking like \\<X>\<Y>\. If
  1208. // it begins with "\\?\UNC\" we do a special case and pull out "\?\UNC".
  1209. // We then search for the second '\' beyond that to end the partition name,
  1210. // at which point we start grabbing the root path.
  1211. //
  1212. // If we have it pointing to a directory the partition should look like "c:"
  1213. // and the root path should look like "winnt\cluster\MSCS"
  1214. //
  1215. if ( ( pszDeviceNameIn[ 0 ] == _T('\\') )
  1216. && ( pszDeviceNameIn[ 1 ] == _T('\\') ) )
  1217. {
  1218. int cSlashes = 0;
  1219. //
  1220. // pszDeviceNameIn contains a UNC path. Skip over any \\?\UNC\ prefix
  1221. //
  1222. if ( _tcsnicmp( pszDeviceNameIn, _T("\\\\?\\UNC\\"), 8 ) == 0 )
  1223. {
  1224. pszDeviceNameIn += 8;
  1225. }
  1226. else
  1227. {
  1228. pszDeviceNameIn += 2;
  1229. }
  1230. //
  1231. // Make sure out partition looks like a UNC path on output.
  1232. //
  1233. _sntprintf( pszPartitionNameOut, 2, _T("\\\\") );
  1234. pszPartitionNameOut += 2;
  1235. // Copy the server and share name to the partition string.
  1236. for ( ; *pszDeviceNameIn != _T('\0') ; pszPartitionNameOut++, pszDeviceNameIn++ )
  1237. {
  1238. if ( *pszDeviceNameIn == _T('\\') )
  1239. {
  1240. cSlashes++;
  1241. if ( cSlashes == 2 )
  1242. {
  1243. break;
  1244. } // if: found the slash after the share name
  1245. } // if: found a slash
  1246. *pszPartitionNameOut = *pszDeviceNameIn;
  1247. } // for: each character in the device name
  1248. // Copy the rest of the path to the root path string.
  1249. if ( *pszDeviceNameIn == _T('\0') )
  1250. {
  1251. _tcscpy( pszRootPathOut, _T("\\") );
  1252. } // if: no root path
  1253. else
  1254. {
  1255. pszDeviceNameIn++;
  1256. _tcscpy( pszRootPathOut, pszDeviceNameIn );
  1257. } // else: root path specified
  1258. } // if: UNC path
  1259. else
  1260. {
  1261. _tsplitpath( pszDeviceNameIn, pszPartitionNameOut, pszRootPathOut, NULL, NULL );
  1262. } // else: not a UNC path
  1263. } //*** CClusterQuorumPage::FillPartitionItemData()
  1264. /////////////////////////////////////////////////////////////////////////////
  1265. //++
  1266. //
  1267. // CClusterQuorumPage::BGetDiskInfo
  1268. //
  1269. // Routine Description:
  1270. // Get information about the currently selected disk.
  1271. //
  1272. // Arguments:
  1273. // rpciRes [IN OUT] Disk resource to get info about.
  1274. //
  1275. // Return Value:
  1276. // TRUE The operation was successful.
  1277. // FALSE The operation failed.
  1278. //
  1279. //--
  1280. /////////////////////////////////////////////////////////////////////////////
  1281. BOOL CClusterQuorumPage::BGetDiskInfo( IN OUT CResource & rpciRes )
  1282. {
  1283. DWORD dwStatus = ERROR_SUCCESS;
  1284. DWORD cbDiskInfo = sizeof( CLUSPROP_DWORD )
  1285. + sizeof( CLUSPROP_SCSI_ADDRESS )
  1286. + sizeof( CLUSPROP_DISK_NUMBER )
  1287. + sizeof( CLUSPROP_PARTITION_INFO )
  1288. + sizeof( CLUSPROP_SYNTAX );
  1289. PBYTE pbDiskInfo = NULL;
  1290. try
  1291. {
  1292. // Get disk info.
  1293. pbDiskInfo = new BYTE[ cbDiskInfo ];
  1294. if ( pbDiskInfo == NULL )
  1295. {
  1296. AfxThrowMemoryException();
  1297. } // if: error allocating memory
  1298. dwStatus = ClusterResourceControl(
  1299. rpciRes.Hresource(),
  1300. NULL,
  1301. CLUSCTL_RESOURCE_STORAGE_GET_DISK_INFO,
  1302. NULL,
  1303. 0,
  1304. pbDiskInfo,
  1305. cbDiskInfo,
  1306. &cbDiskInfo
  1307. );
  1308. if ( dwStatus == ERROR_MORE_DATA )
  1309. {
  1310. delete [] pbDiskInfo;
  1311. pbDiskInfo = new BYTE[ cbDiskInfo] ;
  1312. if ( pbDiskInfo == NULL )
  1313. {
  1314. AfxThrowMemoryException();
  1315. } // if: error allocating memory
  1316. dwStatus = ClusterResourceControl(
  1317. rpciRes.Hresource(),
  1318. NULL,
  1319. CLUSCTL_RESOURCE_STORAGE_GET_DISK_INFO,
  1320. NULL,
  1321. 0,
  1322. pbDiskInfo,
  1323. cbDiskInfo,
  1324. &cbDiskInfo
  1325. );
  1326. } // if: buffer is too small
  1327. } // try
  1328. catch ( CMemoryException * pme )
  1329. {
  1330. pme->Delete();
  1331. dwStatus = ERROR_NOT_ENOUGH_MEMORY;
  1332. } // catch: CMemoryException
  1333. if ( dwStatus != ERROR_SUCCESS )
  1334. {
  1335. CNTException nte(
  1336. dwStatus,
  1337. IDS_GET_DISK_INFO_ERROR,
  1338. rpciRes.StrName(),
  1339. NULL,
  1340. FALSE /*bAutoDelete*/
  1341. );
  1342. delete [] pbDiskInfo;
  1343. nte.ReportError();
  1344. nte.Delete();
  1345. return FALSE;
  1346. } // if: error getting disk info
  1347. delete [] m_pbDiskInfo;
  1348. m_pbDiskInfo = pbDiskInfo;
  1349. m_cbDiskInfo = cbDiskInfo;
  1350. return TRUE;
  1351. } //*** CClusterQuorumPage::BGetDiskInfo()
  1352. //*************************************************************************//
  1353. /////////////////////////////////////////////////////////////////////////////
  1354. // CClusterNetPriorityPage property page
  1355. /////////////////////////////////////////////////////////////////////////////
  1356. IMPLEMENT_DYNCREATE(CClusterNetPriorityPage, CBasePropertyPage)
  1357. /////////////////////////////////////////////////////////////////////////////
  1358. // Message Maps
  1359. BEGIN_MESSAGE_MAP(CClusterNetPriorityPage, CBasePropertyPage)
  1360. //{{AFX_MSG_MAP(CClusterNetPriorityPage)
  1361. ON_LBN_SELCHANGE(IDC_PP_CLUS_PRIORITY_LIST, OnSelChangeList)
  1362. ON_BN_CLICKED(IDC_PP_CLUS_PRIORITY_UP, OnUp)
  1363. ON_BN_CLICKED(IDC_PP_CLUS_PRIORITY_DOWN, OnDown)
  1364. ON_BN_CLICKED(IDC_PP_CLUS_PRIORITY_PROPERTIES, OnProperties)
  1365. ON_WM_DESTROY()
  1366. ON_WM_CONTEXTMENU()
  1367. ON_LBN_DBLCLK(IDC_PP_CLUS_PRIORITY_LIST, OnDblClkList)
  1368. //}}AFX_MSG_MAP
  1369. ON_COMMAND(ID_FILE_PROPERTIES, OnProperties)
  1370. END_MESSAGE_MAP()
  1371. /////////////////////////////////////////////////////////////////////////////
  1372. //++
  1373. //
  1374. // CClusterNetPriorityPage::CClusterNetPriorityPage
  1375. //
  1376. // Routine Description:
  1377. // Constructor.
  1378. //
  1379. // Arguments:
  1380. // None.
  1381. //
  1382. // Return Value:
  1383. // None.
  1384. //
  1385. //--
  1386. /////////////////////////////////////////////////////////////////////////////
  1387. CClusterNetPriorityPage::CClusterNetPriorityPage(void)
  1388. : CBasePropertyPage(IDD, g_aHelpIDs_IDD_PP_CLUSTER_NET_PRIORITY)
  1389. {
  1390. //{{AFX_DATA_INIT(CClusterNetPriorityPage)
  1391. //}}AFX_DATA_INIT
  1392. m_bControlsInitialized = FALSE;
  1393. } //*** CClusterNetPriorityPage::CClusterNetPriorityPage()
  1394. /////////////////////////////////////////////////////////////////////////////
  1395. //++
  1396. //
  1397. // CClusterNetPriorityPage::OnDestroy
  1398. //
  1399. // Routine Description:
  1400. // Handler for the WM_DESTROY message.
  1401. //
  1402. // Arguments:
  1403. // None.
  1404. //
  1405. // Return Value:
  1406. // None.
  1407. //
  1408. //--
  1409. /////////////////////////////////////////////////////////////////////////////
  1410. void CClusterNetPriorityPage::OnDestroy(void)
  1411. {
  1412. // If the controls have been initialized, clear the list box.
  1413. if (BControlsInitialized())
  1414. ClearNetworkList();
  1415. // Call the base class method.
  1416. CBasePropertyPage::OnDestroy();
  1417. } //*** CClusterNetPriorityPage::OnDestroy()
  1418. /////////////////////////////////////////////////////////////////////////////
  1419. //++
  1420. //
  1421. // CClusterNetPriorityPage::DoDataExchange
  1422. //
  1423. // Routine Description:
  1424. // Do data exchange between the dialog and the class.
  1425. //
  1426. // Arguments:
  1427. // pDX [IN OUT] Data exchange object
  1428. //
  1429. // Return Value:
  1430. // None.
  1431. //
  1432. //--
  1433. /////////////////////////////////////////////////////////////////////////////
  1434. void CClusterNetPriorityPage::DoDataExchange(CDataExchange* pDX)
  1435. {
  1436. CBasePropertyPage::DoDataExchange(pDX);
  1437. //{{AFX_DATA_MAP(CClusterNetPriorityPage)
  1438. DDX_Control(pDX, IDC_PP_CLUS_PRIORITY_PROPERTIES, m_pbProperties);
  1439. DDX_Control(pDX, IDC_PP_CLUS_PRIORITY_DOWN, m_pbDown);
  1440. DDX_Control(pDX, IDC_PP_CLUS_PRIORITY_UP, m_pbUp);
  1441. DDX_Control(pDX, IDC_PP_CLUS_PRIORITY_LIST, m_lbList);
  1442. //}}AFX_DATA_MAP
  1443. m_bControlsInitialized = TRUE;
  1444. if (pDX->m_bSaveAndValidate)
  1445. {
  1446. int nIndex;
  1447. int cItems;
  1448. CNetwork * pciNet;
  1449. ASSERT(!BReadOnly());
  1450. // Save the list.
  1451. LpciNetworkPriority().RemoveAll();
  1452. cItems = m_lbList.GetCount();
  1453. for (nIndex = 0 ; nIndex < cItems ; nIndex++)
  1454. {
  1455. pciNet = (CNetwork *) m_lbList.GetItemDataPtr(nIndex);
  1456. ASSERT_VALID(pciNet);
  1457. LpciNetworkPriority().AddTail(pciNet);
  1458. } // for: each item in the list box
  1459. } // if: saving data from the dialog
  1460. } //*** CClusterNetPriorityPage::DoDataExchange()
  1461. /////////////////////////////////////////////////////////////////////////////
  1462. //++
  1463. //
  1464. // CClusterNetPriorityPage::OnInitDialog
  1465. //
  1466. // Routine Description:
  1467. // Handler for the WM_INITDIALOG message.
  1468. //
  1469. // Arguments:
  1470. // None.
  1471. //
  1472. // Return Value:
  1473. // TRUE Focus not set yet.
  1474. // FALSE Focus already set.
  1475. //
  1476. //--
  1477. /////////////////////////////////////////////////////////////////////////////
  1478. BOOL CClusterNetPriorityPage::OnInitDialog(void)
  1479. {
  1480. CBasePropertyPage::OnInitDialog();
  1481. if (BReadOnly())
  1482. {
  1483. m_lbList.EnableWindow(FALSE);
  1484. m_pbUp.EnableWindow(FALSE);
  1485. m_pbDown.EnableWindow(FALSE);
  1486. } // if: object is read only
  1487. try
  1488. {
  1489. // Duplicate the network priority list.
  1490. {
  1491. POSITION pos;
  1492. CNetwork * pciNet;
  1493. pos = PciCluster()->LpciNetworkPriority().GetHeadPosition();
  1494. while (pos != NULL)
  1495. {
  1496. pciNet = (CNetwork *) PciCluster()->LpciNetworkPriority().GetNext(pos);
  1497. ASSERT_VALID(pciNet);
  1498. m_lpciNetworkPriority.AddTail(pciNet);
  1499. } // while: more networks in the list
  1500. } // Duplicate the network priority list
  1501. } // try
  1502. catch (CException * pe)
  1503. {
  1504. pe->ReportError();
  1505. pe->Delete();
  1506. } // catch: CException
  1507. // Fill the list.
  1508. FillList();
  1509. // Set button states.
  1510. OnSelChangeList();
  1511. return TRUE; // return TRUE unless you set the focus to a control
  1512. // EXCEPTION: OCX Property Pages should return FALSE
  1513. } //*** CClusterNetPriorityPage::OnInitDialog()
  1514. /////////////////////////////////////////////////////////////////////////////
  1515. //++
  1516. //
  1517. // CClusterNetPriorityPage::OnApply
  1518. //
  1519. // Routine Description:
  1520. // Handler for the PSN_APPLY message.
  1521. //
  1522. // Arguments:
  1523. // None.
  1524. //
  1525. // Return Value:
  1526. // TRUE Page successfully applied.
  1527. // FALSE Error applying page.
  1528. //
  1529. //--
  1530. /////////////////////////////////////////////////////////////////////////////
  1531. BOOL CClusterNetPriorityPage::OnApply(void)
  1532. {
  1533. ASSERT(!BReadOnly());
  1534. try
  1535. {
  1536. PciCluster()->SetNetworkPriority(LpciNetworkPriority());
  1537. } // try
  1538. catch (CException * pe)
  1539. {
  1540. pe->ReportError();
  1541. pe->Delete();
  1542. return FALSE;
  1543. } // catch: CException
  1544. return CPropertyPage::OnApply();
  1545. } //*** CClusterNetPriorityPage::OnApply()
  1546. /////////////////////////////////////////////////////////////////////////////
  1547. //++
  1548. //
  1549. // CClusterNetPriorityPage::OnSelChangeList
  1550. //
  1551. // Routine Description:
  1552. // Handler for the LBN_SELCHANGE message on the list box.
  1553. //
  1554. // Arguments:
  1555. // None.
  1556. //
  1557. // Return Value:
  1558. // None.
  1559. //
  1560. //--
  1561. /////////////////////////////////////////////////////////////////////////////
  1562. void CClusterNetPriorityPage::OnSelChangeList(void)
  1563. {
  1564. BOOL bEnableUp;
  1565. BOOL bEnableDown;
  1566. BOOL bEnableProperties;
  1567. int isel;
  1568. int cItems;
  1569. isel = m_lbList.GetCurSel();
  1570. cItems = m_lbList.GetCount();
  1571. // Enable buttons only if there is a selection and there
  1572. // is more than one item in the list.
  1573. if (BReadOnly() || (isel == LB_ERR) || (cItems <= 1))
  1574. {
  1575. bEnableUp = FALSE;
  1576. bEnableDown = FALSE;
  1577. } // if: no selection or only 0 or 1 items in the list
  1578. else
  1579. {
  1580. bEnableUp = (isel > 0);
  1581. bEnableDown = (isel < cItems - 1);
  1582. } // else: buttons allowed to be enabled
  1583. bEnableProperties = (isel != LB_ERR);
  1584. m_pbUp.EnableWindow(bEnableUp);
  1585. m_pbDown.EnableWindow(bEnableDown);
  1586. m_pbProperties.EnableWindow(bEnableProperties);
  1587. } //*** CClusterNetPriorityPage::OnSelChangeList()
  1588. /////////////////////////////////////////////////////////////////////////////
  1589. //++
  1590. //
  1591. // CClusterNetPriorityPage::OnUp
  1592. //
  1593. // Routine Description:
  1594. // Handler for the BN_CLICKED message on the Up push button.
  1595. //
  1596. // Arguments:
  1597. // None.
  1598. //
  1599. // Return Value:
  1600. // None.
  1601. //
  1602. //--
  1603. /////////////////////////////////////////////////////////////////////////////
  1604. void CClusterNetPriorityPage::OnUp(void)
  1605. {
  1606. int isel;
  1607. CNetwork * pciNet;
  1608. isel = m_lbList.GetCurSel();
  1609. ASSERT(isel != LB_ERR);
  1610. pciNet = (CNetwork *) m_lbList.GetItemDataPtr(isel);
  1611. ASSERT_VALID(pciNet);
  1612. VERIFY(m_lbList.DeleteString(isel) != LB_ERR);
  1613. isel = m_lbList.InsertString(isel - 1, pciNet->StrName());
  1614. ASSERT(isel != LB_ERR);
  1615. VERIFY(m_lbList.SetItemDataPtr(isel, pciNet) != LB_ERR);
  1616. VERIFY(m_lbList.SetCurSel(isel) != LB_ERR);
  1617. OnSelChangeList();
  1618. m_lbList.SetFocus();
  1619. SetModified(TRUE);
  1620. } //*** CClusterNetPriorityPage::OnUp()
  1621. /////////////////////////////////////////////////////////////////////////////
  1622. //++
  1623. //
  1624. // CClusterNetPriorityPage::OnDown
  1625. //
  1626. // Routine Description:
  1627. // Handler for the BN_CLICKED message on the Down push button.
  1628. //
  1629. // Arguments:
  1630. // None.
  1631. //
  1632. // Return Value:
  1633. // None.
  1634. //
  1635. //--
  1636. /////////////////////////////////////////////////////////////////////////////
  1637. void CClusterNetPriorityPage::OnDown(void)
  1638. {
  1639. int isel;
  1640. CNetwork * pciNet;
  1641. isel = m_lbList.GetCurSel();
  1642. ASSERT(isel != LB_ERR);
  1643. pciNet = (CNetwork *) m_lbList.GetItemDataPtr(isel);
  1644. ASSERT_VALID(pciNet);
  1645. VERIFY(m_lbList.DeleteString(isel) != LB_ERR);
  1646. isel = m_lbList.InsertString(isel + 1, pciNet->StrName());
  1647. ASSERT(isel != LB_ERR);
  1648. VERIFY(m_lbList.SetItemDataPtr(isel, pciNet) != LB_ERR);
  1649. VERIFY(m_lbList.SetCurSel(isel) != LB_ERR);
  1650. OnSelChangeList();
  1651. m_lbList.SetFocus();
  1652. SetModified(TRUE);
  1653. } //*** CClusterNetPriorityPage::OnDown()
  1654. /////////////////////////////////////////////////////////////////////////////
  1655. //++
  1656. //
  1657. // CClusterNetPriorityPage::OnProperties
  1658. //
  1659. // Routine Description:
  1660. // Handler for the BN_CLICKED message on the Properties push button.
  1661. //
  1662. // Arguments:
  1663. // None.
  1664. //
  1665. // Return Value:
  1666. // None.
  1667. //
  1668. //--
  1669. /////////////////////////////////////////////////////////////////////////////
  1670. void CClusterNetPriorityPage::OnProperties(void)
  1671. {
  1672. DisplayProperties();
  1673. } //*** CClusterNetPriorityPage::OnProperties()
  1674. /////////////////////////////////////////////////////////////////////////////
  1675. //++
  1676. //
  1677. // CClusterNetPriorityPage::FillList
  1678. //
  1679. // Routine Description:
  1680. // Fill the network list.
  1681. //
  1682. // Arguments:
  1683. // None.
  1684. //
  1685. // Return Value:
  1686. // None.
  1687. //
  1688. //--
  1689. /////////////////////////////////////////////////////////////////////////////
  1690. void CClusterNetPriorityPage::FillList(void)
  1691. {
  1692. int nIndex;
  1693. POSITION pos;
  1694. CNetwork * pciNet;
  1695. CWaitCursor wc;
  1696. ClearNetworkList();
  1697. PciCluster()->CollectNetworkPriority(NULL);
  1698. pos = LpciNetworkPriority().GetHeadPosition();
  1699. while (pos != NULL)
  1700. {
  1701. pciNet = (CNetwork *) LpciNetworkPriority().GetNext(pos);
  1702. ASSERT_VALID(pciNet);
  1703. try
  1704. {
  1705. nIndex = m_lbList.AddString(pciNet->StrName());
  1706. ASSERT(nIndex != LB_ERR);
  1707. m_lbList.SetItemDataPtr(nIndex, pciNet);
  1708. pciNet->AddRef();
  1709. } // try
  1710. catch (...)
  1711. {
  1712. // Ignore all errors because there is really nothing we can do.
  1713. // Displaying a message isn't really very useful.
  1714. } // catch: Anything
  1715. } // while: more items in the list
  1716. } // CClusterNetPriorityPage::FillList
  1717. /////////////////////////////////////////////////////////////////////////////
  1718. //++
  1719. //
  1720. // CClusterNetPriorityPage::ClearNetworkList
  1721. //
  1722. // Routine Description:
  1723. // Clear the network list and release references to pointers.
  1724. //
  1725. // Arguments:
  1726. // None.
  1727. //
  1728. // Return Value:
  1729. // None.
  1730. //
  1731. //--
  1732. /////////////////////////////////////////////////////////////////////////////
  1733. void CClusterNetPriorityPage::ClearNetworkList(void)
  1734. {
  1735. int cItems;
  1736. int iItem;
  1737. CNetwork * pciNet;
  1738. cItems = m_lbList.GetCount();
  1739. for (iItem = 0 ; iItem < cItems ; iItem++)
  1740. {
  1741. pciNet = (CNetwork *) m_lbList.GetItemDataPtr(iItem);
  1742. ASSERT_VALID(pciNet);
  1743. ASSERT_KINDOF(CNetwork, pciNet);
  1744. pciNet->Release();
  1745. } // for: each item in the list
  1746. m_lbList.ResetContent();
  1747. } //*** CClusterNetPriorityPage::ClearNetworkList()
  1748. /////////////////////////////////////////////////////////////////////////////
  1749. //++
  1750. //
  1751. // CResourceDependsPage::DisplayProperties
  1752. //
  1753. // Routine Description:
  1754. // Display properties of the item with the focus.
  1755. //
  1756. // Arguments:
  1757. // None.
  1758. //
  1759. // Return Value:
  1760. // None.
  1761. //
  1762. //--
  1763. /////////////////////////////////////////////////////////////////////////////
  1764. void CClusterNetPriorityPage::DisplayProperties()
  1765. {
  1766. int isel;
  1767. CNetwork * pciNet;
  1768. isel = m_lbList.GetCurSel();
  1769. ASSERT(isel != LB_ERR);
  1770. if (isel != LB_ERR)
  1771. {
  1772. // Get the network pointer.
  1773. pciNet = (CNetwork *) m_lbList.GetItemDataPtr(isel);
  1774. ASSERT_VALID(pciNet);
  1775. // Set properties of that item.
  1776. if (pciNet->BDisplayProperties())
  1777. {
  1778. // Remove the item. If it is still used for internal cluster
  1779. // communications, add it back in.
  1780. VERIFY(m_lbList.DeleteString(isel) != LB_ERR);
  1781. if (pciNet->Cnr() & ClusterNetworkRoleInternalUse)
  1782. {
  1783. isel = m_lbList.InsertString(isel, pciNet->StrName());
  1784. ASSERT(isel != LB_ERR);
  1785. VERIFY(m_lbList.SetItemDataPtr(isel, pciNet) != LB_ERR);
  1786. VERIFY(m_lbList.SetCurSel(isel) != LB_ERR);
  1787. } // if: still used for internal cluster communications
  1788. else
  1789. pciNet->Release();
  1790. // Make sure only appropriate buttons are enabled.
  1791. OnSelChangeList();
  1792. } // if: properties changed
  1793. m_lbList.SetFocus();
  1794. } // if: found an item with focus
  1795. } //*** CClusterNetPriorityPage::DisplayProperties()
  1796. /////////////////////////////////////////////////////////////////////////////
  1797. //++
  1798. //
  1799. // CClusterNetPriorityPage::OnContextMenu
  1800. //
  1801. // Routine Description:
  1802. // Handler for the WM_CONTEXTMENU method.
  1803. //
  1804. // Arguments:
  1805. // pWnd Window in which the user right clicked the mouse.
  1806. // point Position of the cursor, in screen coordinates.
  1807. //
  1808. // Return Value:
  1809. // None.
  1810. //
  1811. //--
  1812. /////////////////////////////////////////////////////////////////////////////
  1813. void CClusterNetPriorityPage::OnContextMenu( CWnd * pWnd, CPoint point )
  1814. {
  1815. BOOL bHandled = FALSE;
  1816. CMenu * pmenu = NULL;
  1817. CListBox * pListBox = (CListBox *) pWnd;
  1818. CString strMenuName;
  1819. CWaitCursor wc;
  1820. // If focus is not in the list box, don't handle the message.
  1821. if ( pWnd == &m_lbList )
  1822. {
  1823. // Create the menu to display.
  1824. try
  1825. {
  1826. pmenu = new CMenu;
  1827. if ( pmenu == NULL )
  1828. {
  1829. AfxThrowMemoryException();
  1830. } // if: error allocating memory
  1831. if ( pmenu->CreatePopupMenu() )
  1832. {
  1833. UINT nFlags = MF_STRING;
  1834. // If there are no items in the list, disable the menu item.
  1835. if ( pListBox->GetCount() == 0 )
  1836. {
  1837. nFlags |= MF_GRAYED;
  1838. } // if: no items in the list
  1839. // Add the Properties item to the menu.
  1840. strMenuName.LoadString( IDS_MENU_PROPERTIES );
  1841. if ( pmenu->AppendMenu( nFlags, ID_FILE_PROPERTIES, strMenuName ) )
  1842. {
  1843. bHandled = TRUE;
  1844. } // if: successfully added menu item
  1845. } // if: menu created successfully
  1846. } // try
  1847. catch ( CException * pe )
  1848. {
  1849. pe->ReportError();
  1850. pe->Delete();
  1851. } // catch: CException
  1852. } // if: focus is on the list control
  1853. if ( bHandled )
  1854. {
  1855. // Display the menu.
  1856. if ( ! pmenu->TrackPopupMenu(
  1857. TPM_LEFTALIGN | TPM_RIGHTBUTTON,
  1858. point.x,
  1859. point.y,
  1860. this
  1861. ) )
  1862. {
  1863. } // if: unsuccessfully displayed the menu
  1864. } // if: there is a menu to display
  1865. else
  1866. {
  1867. CBasePropertyPage::OnContextMenu( pWnd, point );
  1868. } // else: no menu to display
  1869. delete pmenu;
  1870. } //*** CClusterNetPriorityPage::OnContextMenu()
  1871. /////////////////////////////////////////////////////////////////////////////
  1872. //++
  1873. //
  1874. // CClusterNetPriorityPage::OnDblClkList
  1875. //
  1876. // Routine Description:
  1877. // Handler method for the NM_DBLCLK message.
  1878. //
  1879. // Arguments:
  1880. // None.
  1881. //
  1882. // Return Value:
  1883. // None.
  1884. //
  1885. //--
  1886. /////////////////////////////////////////////////////////////////////////////
  1887. void CClusterNetPriorityPage::OnDblClkList()
  1888. {
  1889. DisplayProperties();
  1890. } //*** CClusterNetPriorityPage::OnDblClkList()