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.

1920 lines
53 KiB

  1. /*++
  2. Module Name:
  3. CusTop.cpp
  4. Abstract:
  5. This module contains the Implementation of CCustomTopology.
  6. This class displays the Customize Topology Dialog.
  7. */
  8. #include "stdafx.h"
  9. #include "CusTop.h"
  10. #include "frsAdv.h"
  11. #include "utils.h"
  12. #include "dfshelp.h"
  13. #include "ldaputils.h"
  14. int g_FRS_CUSTOP_Last_SortColumn = 1;
  15. #define NUM_OF_FRS_CUSTOP_COLUMNS 5
  16. RSTOPOLOGYPREF_STRING g_TopologyPref[] = {
  17. {FRS_RSTOPOLOGYPREF_RING, IDS_FRSPROP_RING},
  18. {FRS_RSTOPOLOGYPREF_HUBSPOKE, IDS_FRSPROP_HUBSPOKE},
  19. {FRS_RSTOPOLOGYPREF_FULLMESH, IDS_FRSPROP_FULLMESH},
  20. {FRS_RSTOPOLOGYPREF_CUSTOM, IDS_FRSPROP_CUSTOM}
  21. };
  22. // sort member list based on m_bstrSite
  23. struct CusTopMemberCompareSite : greater<CCusTopMember*>
  24. {
  25. bool operator()(const CCusTopMember *pMem1, const CCusTopMember *pMem2) const
  26. { return (lstrcmpi(pMem1->m_bstrSite, pMem2->m_bstrSite) > 0); }
  27. };
  28. /////////////////////////////////////////////////////////////////////////////
  29. //
  30. // CCustomTopology
  31. //
  32. CCustomTopology::CCustomTopology()
  33. {
  34. }
  35. CCustomTopology::~CCustomTopology()
  36. {
  37. }
  38. HRESULT CCustomTopology::_GetMemberList()
  39. {
  40. FreeCusTopMembers(&m_MemberList);
  41. RETURN_INVALIDARG_IF_NULL((IReplicaSet *)m_piReplicaSet);
  42. VARIANT var;
  43. VariantInit(&var);
  44. HRESULT hr = m_piReplicaSet->GetMemberListEx(&var);
  45. RETURN_IF_FAILED(hr);
  46. if (V_VT(&var) != (VT_ARRAY | VT_VARIANT))
  47. return E_INVALIDARG;
  48. SAFEARRAY *psa_1 = V_ARRAY(&var);
  49. if (!psa_1) // no member at all
  50. return hr;
  51. long lLowerBound_1 = 0;
  52. long lUpperBound_1 = 0;
  53. long lCount_1 = 0;
  54. SafeArrayGetLBound(psa_1, 1, &lLowerBound_1);
  55. SafeArrayGetUBound(psa_1, 1, &lUpperBound_1);
  56. lCount_1 = lUpperBound_1 - lLowerBound_1 + 1;
  57. VARIANT HUGEP *pArray_1;
  58. SafeArrayAccessData(psa_1, (void HUGEP **) &pArray_1);
  59. for (long i = 0; i < lCount_1; i++)
  60. {
  61. if (V_VT(&(pArray_1[i])) != (VT_ARRAY | VT_VARIANT))
  62. {
  63. hr = E_INVALIDARG;
  64. break;
  65. }
  66. SAFEARRAY *psa_0 = V_ARRAY(&(pArray_1[i]));
  67. if (!psa_0)
  68. {
  69. hr = E_INVALIDARG;
  70. break;
  71. }
  72. long lLowerBound_0 = 0;
  73. long lUpperBound_0 = 0;
  74. long lCount_0 = 0;
  75. SafeArrayGetLBound(psa_0, 1, &lLowerBound_0);
  76. SafeArrayGetUBound(psa_0, 1, &lUpperBound_0);
  77. lCount_0 = lUpperBound_0 - lLowerBound_0 + 1;
  78. if (NUM_OF_FRSMEMBER_ATTRS != lCount_0)
  79. {
  80. hr = E_INVALIDARG;
  81. break;
  82. }
  83. VARIANT HUGEP *pArray_0;
  84. SafeArrayAccessData(psa_0, (void HUGEP **) &pArray_0);
  85. do {
  86. CCusTopMember* pNew = new CCusTopMember;
  87. BREAK_OUTOFMEMORY_IF_NULL(pNew, &hr);
  88. hr = pNew->Init(
  89. pArray_0[2].bstrVal, //bstrMemberDN,
  90. pArray_0[4].bstrVal, //bstrServer,
  91. pArray_0[6].bstrVal //bstrSite
  92. );
  93. if (SUCCEEDED(hr))
  94. m_MemberList.push_back(pNew);
  95. else
  96. delete pNew;
  97. } while (0);
  98. SafeArrayUnaccessData(psa_0);
  99. }
  100. SafeArrayUnaccessData(psa_1);
  101. if (SUCCEEDED(hr))
  102. {
  103. //
  104. // sort member list on m_bstrServer alphabetically
  105. //
  106. hr = _SortMemberList();
  107. }
  108. if (FAILED(hr))
  109. FreeCusTopMembers(&m_MemberList);
  110. SafeArrayDestroy(psa_1); // it should free psa_0 as well
  111. return hr;
  112. }
  113. HRESULT CCustomTopology::_GetConnectionList()
  114. {
  115. FreeCusTopConnections(&m_ConnectionList);
  116. RETURN_INVALIDARG_IF_NULL((IReplicaSet *)m_piReplicaSet);
  117. VARIANT var;
  118. VariantInit(&var);
  119. HRESULT hr = m_piReplicaSet->GetConnectionListEx(&var);
  120. RETURN_IF_FAILED(hr);
  121. if (V_VT(&var) != (VT_ARRAY | VT_VARIANT))
  122. return E_INVALIDARG;
  123. SAFEARRAY *psa_1 = V_ARRAY(&var);
  124. if (!psa_1) // no connection at all
  125. return hr;
  126. long lLowerBound_1 = 0;
  127. long lUpperBound_1 = 0;
  128. long lCount_1 = 0;
  129. SafeArrayGetLBound(psa_1, 1, &lLowerBound_1);
  130. SafeArrayGetUBound(psa_1, 1, &lUpperBound_1);
  131. lCount_1 = lUpperBound_1 - lLowerBound_1 + 1;
  132. VARIANT HUGEP *pArray_1;
  133. SafeArrayAccessData(psa_1, (void HUGEP **) &pArray_1);
  134. for (long i = 0; i < lCount_1; i++)
  135. {
  136. if (V_VT(&(pArray_1[i])) != (VT_ARRAY | VT_VARIANT))
  137. {
  138. hr = E_INVALIDARG;
  139. break;
  140. }
  141. SAFEARRAY *psa_0 = V_ARRAY(&(pArray_1[i]));
  142. if (!psa_0)
  143. {
  144. hr = E_INVALIDARG;
  145. break;
  146. }
  147. long lLowerBound_0 = 0;
  148. long lUpperBound_0 = 0;
  149. long lCount_0 = 0;
  150. SafeArrayGetLBound(psa_0, 1, &lLowerBound_0);
  151. SafeArrayGetUBound(psa_0, 1, &lUpperBound_0);
  152. lCount_0 = lUpperBound_0 - lLowerBound_0 + 1;
  153. if (NUM_OF_FRSCONNECTION_ATTRS != lCount_0)
  154. {
  155. hr = E_INVALIDARG;
  156. break;
  157. }
  158. VARIANT HUGEP *pArray_0;
  159. SafeArrayAccessData(psa_0, (void HUGEP **) &pArray_0);
  160. do {
  161. CComBSTR bstrFromServer;
  162. CComBSTR bstrFromSite;
  163. hr = _GetMemberDNInfo(pArray_0[1].bstrVal, &bstrFromServer, &bstrFromSite);
  164. BREAK_IF_FAILED(hr);
  165. CComBSTR bstrToServer;
  166. CComBSTR bstrToSite;
  167. hr = _GetMemberDNInfo(pArray_0[2].bstrVal, &bstrToServer, &bstrToSite);
  168. BREAK_IF_FAILED(hr);
  169. BOOL bSyncImmediately = (BOOL)(NTDSCONN_IGNORE_SCHEDULE(pArray_0[4].lVal));
  170. Connection_Priority nPriority = PRIORITY_LOW;
  171. switch ((pArray_0[4].lVal & FRSCONN_PRIORITY_MASK) >> 28)
  172. {
  173. case 1:
  174. case 2:
  175. nPriority = PRIORITY_HIGH;
  176. break;
  177. case 3:
  178. case 4:
  179. nPriority = PRIORITY_MEDIUM;
  180. break;
  181. default:
  182. nPriority = PRIORITY_LOW;
  183. break;
  184. }
  185. CCusTopConnection* pNew = new CCusTopConnection;
  186. BREAK_OUTOFMEMORY_IF_NULL(pNew, &hr);
  187. hr = pNew->Init(
  188. pArray_0[1].bstrVal, //bstrFromDN,
  189. bstrFromServer,
  190. bstrFromSite,
  191. pArray_0[2].bstrVal, //bstrToDN,
  192. bstrToServer,
  193. bstrToSite,
  194. (BOOL)(pArray_0[3].lVal), //bEnable
  195. CONNECTION_OPTYPE_OTHERS,
  196. bSyncImmediately,
  197. nPriority
  198. );
  199. if (SUCCEEDED(hr))
  200. m_ConnectionList.push_back(pNew);
  201. else
  202. delete pNew;
  203. } while (0);
  204. SafeArrayUnaccessData(psa_0);
  205. }
  206. SafeArrayUnaccessData(psa_1);
  207. if (FAILED(hr))
  208. FreeCusTopConnections(&m_ConnectionList);
  209. SafeArrayDestroy(psa_1);
  210. return hr;
  211. }
  212. void CCustomTopology::_Reset()
  213. {
  214. m_bstrTopologyPref.Empty();
  215. m_bstrHubMemberDN.Empty();
  216. FreeCusTopMembers(&m_MemberList);
  217. FreeCusTopConnections(&m_ConnectionList);
  218. m_piReplicaSet = NULL;
  219. }
  220. HRESULT CCustomTopology::put_ReplicaSet
  221. (
  222. IReplicaSet* i_piReplicaSet
  223. )
  224. {
  225. RETURN_INVALIDARG_IF_NULL(i_piReplicaSet);
  226. _Reset();
  227. m_piReplicaSet = i_piReplicaSet;
  228. HRESULT hr = S_OK;
  229. do {
  230. hr = m_piReplicaSet->get_TopologyPref(&m_bstrTopologyPref);
  231. BREAK_IF_FAILED(hr);
  232. hr = m_piReplicaSet->get_HubMemberDN(&m_bstrHubMemberDN);
  233. BREAK_IF_FAILED(hr);
  234. hr = _GetMemberList();
  235. BREAK_IF_FAILED(hr);
  236. hr = _GetConnectionList();
  237. BREAK_IF_FAILED(hr);
  238. } while (0);
  239. if (FAILED(hr))
  240. _Reset();
  241. return hr;
  242. }
  243. int __cdecl CusTopMemberCompareServer(const void *arg1, const void *arg2 )
  244. {
  245. return lstrcmpi(
  246. (*(CCusTopMember**)arg1)->m_bstrServer,
  247. (*(CCusTopMember**)arg2)->m_bstrServer
  248. );
  249. }
  250. HRESULT CCustomTopology::_SortMemberList()
  251. {
  252. HRESULT hr = S_OK;
  253. int cMembers = m_MemberList.size();
  254. if (2 > cMembers)
  255. return hr;
  256. CCusTopMember** ppMember = (CCusTopMember **)calloc(cMembers, sizeof(CCusTopMember *));
  257. RETURN_OUTOFMEMORY_IF_NULL(ppMember);
  258. int i = 0;
  259. for (CCusTopMemberList::iterator it = m_MemberList.begin(); it != m_MemberList.end(); it++, i++)
  260. {
  261. ppMember[i] = (*it);
  262. }
  263. qsort((void *)ppMember, cMembers, sizeof(CCusTopMember *), CusTopMemberCompareServer);
  264. m_MemberList.clear(); // without deleting the object
  265. for (i = 0; i < cMembers; i++)
  266. {
  267. m_MemberList.push_back(ppMember[i]);
  268. }
  269. free((void *)ppMember);
  270. return hr;
  271. }
  272. HRESULT CCustomTopology::_GetMemberDNInfo(
  273. IN BSTR i_bstrMemberDN,
  274. OUT BSTR* o_pbstrServer,
  275. OUT BSTR* o_pbstrSite
  276. )
  277. {
  278. RETURN_INVALIDARG_IF_NULL(i_bstrMemberDN);
  279. RETURN_INVALIDARG_IF_NULL(o_pbstrServer);
  280. RETURN_INVALIDARG_IF_NULL(o_pbstrSite);
  281. for (CCusTopMemberList::iterator i = m_MemberList.begin(); i != m_MemberList.end(); i++)
  282. {
  283. if (!lstrcmpi(i_bstrMemberDN, (*i)->m_bstrMemberDN))
  284. break;
  285. }
  286. if (i == m_MemberList.end())
  287. return S_FALSE;
  288. *o_pbstrServer = (*i)->m_bstrServer.Copy();
  289. RETURN_OUTOFMEMORY_IF_NULL(*o_pbstrServer);
  290. *o_pbstrSite = (*i)->m_bstrSite.Copy();
  291. if (!*o_pbstrSite)
  292. {
  293. SysFreeString(*o_pbstrServer);
  294. return E_OUTOFMEMORY;
  295. }
  296. return S_OK;
  297. }
  298. HRESULT CCustomTopology::_GetHubMember(
  299. OUT CCusTopMember** o_ppHubMember
  300. )
  301. {
  302. RETURN_INVALIDARG_IF_NULL(o_ppHubMember);
  303. int index = SendDlgItemMessage(IDC_FRS_CUSTOP_HUBSERVER, CB_GETCURSEL, 0, 0);
  304. int len = SendDlgItemMessage(IDC_FRS_CUSTOP_HUBSERVER, CB_GETLBTEXTLEN, index, 0);
  305. PTSTR pszServer = (PTSTR)calloc(len + 1, sizeof(TCHAR));
  306. RETURN_OUTOFMEMORY_IF_NULL(pszServer);
  307. SendDlgItemMessage(IDC_FRS_CUSTOP_HUBSERVER, CB_GETLBTEXT, index, (LPARAM)pszServer);
  308. CCusTopMemberList::iterator i;
  309. for (i = m_MemberList.begin(); i != m_MemberList.end(); i++)
  310. {
  311. if (!lstrcmpi(pszServer, (*i)->m_bstrServer))
  312. break;
  313. }
  314. free(pszServer);
  315. if (i == m_MemberList.end())
  316. return E_INVALIDARG;
  317. *o_ppHubMember = (*i);
  318. return S_OK;
  319. }
  320. LRESULT CCustomTopology::OnInitDialog
  321. (
  322. UINT uMsg,
  323. WPARAM wParam,
  324. LPARAM lParam,
  325. BOOL& bHandled
  326. )
  327. {
  328. int i = 0;
  329. int nControlID = 0;
  330. //
  331. // set IDC_FRS_CUSTOP_TOPOLOGYPREF
  332. //
  333. nControlID = IDC_FRS_CUSTOP_TOPOLOGYPREF;
  334. for (i = 0; i < 4; i++)
  335. {
  336. CComBSTR bstrTopologyPref;
  337. LoadStringFromResource(g_TopologyPref[i].nStringID, &bstrTopologyPref);
  338. SendDlgItemMessage(nControlID, CB_INSERTSTRING, i, (LPARAM)(BSTR)bstrTopologyPref);
  339. if (!lstrcmpi(m_bstrTopologyPref, g_TopologyPref[i].pszTopologyPref))
  340. {
  341. SendDlgItemMessage(nControlID, CB_SETCURSEL, i, 0);
  342. ::EnableWindow(GetDlgItem(IDC_FRS_CUSTOP_REBUILD), (0 != lstrcmpi(m_bstrTopologyPref, FRS_RSTOPOLOGYPREF_CUSTOM)));
  343. }
  344. }
  345. //
  346. // set IDC_FRS_CUSTOP_HUBSERVER
  347. //
  348. nControlID = IDC_FRS_CUSTOP_HUBSERVER;
  349. int index = 0;
  350. CCusTopMemberList::iterator itMem;
  351. for (i = 0, itMem = m_MemberList.begin(); itMem != m_MemberList.end(); i++, itMem++)
  352. {
  353. SendDlgItemMessage(nControlID, CB_INSERTSTRING, i, (LPARAM)(BSTR)(*itMem)->m_bstrServer);
  354. if (!lstrcmpi(m_bstrHubMemberDN, (*itMem)->m_bstrMemberDN))
  355. index = i;
  356. }
  357. SendDlgItemMessage(nControlID, CB_SETCURSEL, index, 0);
  358. if (lstrcmpi(m_bstrTopologyPref, FRS_RSTOPOLOGYPREF_HUBSPOKE))
  359. {
  360. MyShowWindow(GetDlgItem(IDC_FRS_CUSTOP_HUBSERVER_LABEL), FALSE);
  361. MyShowWindow(GetDlgItem(IDC_FRS_CUSTOP_HUBSERVER), FALSE);
  362. }
  363. //
  364. // set IDC_FRS_CUSTOP_CONNECTIONS
  365. //
  366. nControlID = IDC_FRS_CUSTOP_CONNECTIONS;
  367. HWND hwndControl = GetDlgItem(nControlID);
  368. AddLVColumns(hwndControl, IDS_FRS_CUSTOP_COL_ENABLE, NUM_OF_FRS_CUSTOP_COLUMNS);
  369. ListView_SetExtendedListViewStyle(hwndControl, LVS_EX_FULLROWSELECT | LVS_EX_CHECKBOXES);
  370. CCusTopConnectionList::iterator itConn;
  371. for (itConn = m_ConnectionList.begin(); itConn != m_ConnectionList.end(); itConn++)
  372. _InsertConnection(*itConn);
  373. ListView_SortItems( hwndControl,
  374. ConnectionsListCompareProc,
  375. (LPARAM)g_FRS_CUSTOP_Last_SortColumn);
  376. _EnableButtonsForConnectionList();
  377. return TRUE; // Let the system set the focus
  378. }
  379. void CCustomTopology::_EnableButtonsForConnectionList()
  380. {
  381. //
  382. // enable New/Delete/Schedule/Advanced buttons accordingly
  383. //
  384. HWND hwndList = GetDlgItem(IDC_FRS_CUSTOP_CONNECTIONS);
  385. ::EnableWindow(GetDlgItem(IDC_FRS_CUSTOP_ADVANCED), (ListView_GetItemCount(hwndList) >= 1));
  386. int nCount = ListView_GetSelectedCount(hwndList);
  387. ::EnableWindow(GetDlgItem(IDC_FRS_CUSTOP_SCHEDULE), (nCount >= 1));
  388. int index = SendDlgItemMessage(IDC_FRS_CUSTOP_TOPOLOGYPREF, CB_GETCURSEL, 0, 0);
  389. if (3 == index) // bCustomTopology
  390. {
  391. ::EnableWindow(GetDlgItem(IDC_FRS_CUSTOP_CONNECTIONS_NEW), TRUE);
  392. ::EnableWindow(GetDlgItem(IDC_FRS_CUSTOP_CONNECTIONS_DELETE), (nCount >= 1));
  393. } else
  394. {
  395. ::EnableWindow(GetDlgItem(IDC_FRS_CUSTOP_CONNECTIONS_NEW), FALSE);
  396. ::EnableWindow(GetDlgItem(IDC_FRS_CUSTOP_CONNECTIONS_DELETE), FALSE);
  397. }
  398. }
  399. /*++
  400. This function is called when a user clicks the ? in the top right of a property sheet
  401. and then clciks a control, or when they hit F1 in a control.
  402. --*/
  403. LRESULT CCustomTopology::OnCtxHelp(
  404. IN UINT i_uMsg,
  405. IN WPARAM i_wParam,
  406. IN LPARAM i_lParam,
  407. IN OUT BOOL& io_bHandled
  408. )
  409. {
  410. LPHELPINFO lphi = (LPHELPINFO) i_lParam;
  411. if (!lphi || lphi->iContextType != HELPINFO_WINDOW || lphi->iCtrlId < 0)
  412. return FALSE;
  413. ::WinHelp((HWND)(lphi->hItemHandle),
  414. DFS_CTX_HELP_FILE,
  415. HELP_WM_HELP,
  416. (DWORD_PTR)(PVOID)g_aHelpIDs_IDD_FRS_CUSTOP);
  417. return TRUE;
  418. }
  419. /*++
  420. This function handles "What's This" help when a user right clicks the control
  421. --*/
  422. LRESULT CCustomTopology::OnCtxMenuHelp(
  423. IN UINT i_uMsg,
  424. IN WPARAM i_wParam,
  425. IN LPARAM i_lParam,
  426. IN OUT BOOL& io_bHandled
  427. )
  428. {
  429. ::WinHelp((HWND)i_wParam,
  430. DFS_CTX_HELP_FILE,
  431. HELP_CONTEXTMENU,
  432. (DWORD_PTR)(PVOID)g_aHelpIDs_IDD_FRS_CUSTOP);
  433. return TRUE;
  434. }
  435. BOOL CCustomTopology::_EnableRebuild()
  436. {
  437. BOOL bSameTopologyPref = FALSE;
  438. int index = SendDlgItemMessage(IDC_FRS_CUSTOP_TOPOLOGYPREF, CB_GETCURSEL, 0, 0);
  439. if (lstrcmpi(FRS_RSTOPOLOGYPREF_CUSTOM, g_TopologyPref[index].pszTopologyPref) &&
  440. !lstrcmpi(m_bstrTopologyPref, g_TopologyPref[index].pszTopologyPref))
  441. {
  442. bSameTopologyPref = TRUE;
  443. }
  444. if (!bSameTopologyPref || 0 != lstrcmpi(m_bstrTopologyPref, FRS_RSTOPOLOGYPREF_HUBSPOKE))
  445. return bSameTopologyPref;
  446. BOOL bSameHub = FALSE;
  447. CCusTopMember* pHubMember = NULL;
  448. HRESULT hr = _GetHubMember(&pHubMember);
  449. if (SUCCEEDED(hr))
  450. bSameHub = (!lstrcmpi(m_bstrHubMemberDN, pHubMember->m_bstrMemberDN));
  451. return bSameHub;
  452. }
  453. LRESULT CCustomTopology::OnTopologyPref
  454. (
  455. WORD wNotifyCode,
  456. WORD wID,
  457. HWND hWndCtl,
  458. BOOL& bHandled
  459. )
  460. {
  461. HRESULT hr = S_OK;
  462. if (CBN_SELCHANGE == wNotifyCode)
  463. {
  464. int index = SendDlgItemMessage(wID, CB_GETCURSEL, 0, 0);
  465. BOOL bCmdShow = (1 == index);
  466. MyShowWindow(GetDlgItem(IDC_FRS_CUSTOP_HUBSERVER_LABEL), bCmdShow);
  467. MyShowWindow(GetDlgItem(IDC_FRS_CUSTOP_HUBSERVER), bCmdShow);
  468. if (1 == index)
  469. {
  470. CCusTopMember* pHubMember = NULL;
  471. hr = _GetHubMember(&pHubMember);
  472. if (SUCCEEDED(hr))
  473. hr = _RebuildConnections(FRS_RSTOPOLOGYPREF_HUBSPOKE, pHubMember);
  474. } else
  475. hr = _RebuildConnections(g_TopologyPref[index].pszTopologyPref, NULL);
  476. _EnableButtonsForConnectionList();
  477. BOOL bSameTopology = _EnableRebuild();
  478. ::EnableWindow(GetDlgItem(IDC_FRS_CUSTOP_REBUILD), bSameTopology);
  479. }
  480. return (SUCCEEDED(hr));
  481. }
  482. LRESULT CCustomTopology::OnHubServer
  483. (
  484. WORD wNotifyCode,
  485. WORD wID,
  486. HWND hWndCtl,
  487. BOOL& bHandled
  488. )
  489. {
  490. HRESULT hr = S_OK;
  491. if (CBN_SELCHANGE == wNotifyCode)
  492. {
  493. CCusTopMember* pHubMember = NULL;
  494. hr = _GetHubMember(&pHubMember);
  495. if (SUCCEEDED(hr))
  496. hr = _RebuildConnections(FRS_RSTOPOLOGYPREF_HUBSPOKE, pHubMember);
  497. BOOL bSameTopology = _EnableRebuild();
  498. ::EnableWindow(GetDlgItem(IDC_FRS_CUSTOP_REBUILD), bSameTopology);
  499. }
  500. return (SUCCEEDED(hr));
  501. }
  502. LRESULT CCustomTopology::OnRebuild
  503. (
  504. WORD wNotifyCode,
  505. WORD wID,
  506. HWND hWndCtl,
  507. BOOL& bHandled
  508. )
  509. {
  510. HRESULT hr = S_OK;
  511. if (!lstrcmpi(m_bstrTopologyPref, FRS_RSTOPOLOGYPREF_HUBSPOKE))
  512. {
  513. CCusTopMember* pHubMember = NULL;
  514. hr = _GetHubMember(&pHubMember);
  515. if (SUCCEEDED(hr))
  516. hr = _RebuildConnections(m_bstrTopologyPref, pHubMember);
  517. } else
  518. hr = _RebuildConnections(m_bstrTopologyPref, NULL);
  519. return (SUCCEEDED(hr));
  520. }
  521. HRESULT CCustomTopology::_RebuildConnections(
  522. IN BSTR i_bstrTopologyPref,
  523. IN CCusTopMember* i_pHubMember)
  524. {
  525. HRESULT hr = S_OK;
  526. //
  527. // delete all existing connections from list and view
  528. //
  529. CCusTopConnectionList::iterator it = m_ConnectionList.begin();
  530. while (it != m_ConnectionList.end())
  531. {
  532. CCusTopConnectionList::iterator itConn = it++;
  533. if (CONNECTION_OPTYPE_ADD == (*itConn)->m_opType)
  534. {
  535. delete (*itConn);
  536. m_ConnectionList.erase(itConn);
  537. } else
  538. {
  539. (*itConn)->m_opType = CONNECTION_OPTYPE_DEL;
  540. }
  541. }
  542. ListView_DeleteAllItems(GetDlgItem(IDC_FRS_CUSTOP_CONNECTIONS));
  543. //
  544. // re-create the connections as specified
  545. //
  546. if (m_MemberList.size() == 1)
  547. return hr;
  548. CCusTopMemberList::iterator n1;
  549. CCusTopMemberList::iterator n2;
  550. CCusTopConnection Conn;
  551. if (!lstrcmpi(i_bstrTopologyPref, FRS_RSTOPOLOGYPREF_RING))
  552. {
  553. //
  554. // sort member list, such that members on the same site will be neighbors
  555. //
  556. m_MemberList.sort(CusTopMemberCompareSite());
  557. CCusTopMemberList::iterator head;
  558. head = n1 = m_MemberList.begin();
  559. while (n1 != m_MemberList.end())
  560. {
  561. n2 = n1++;
  562. if (n1 == m_MemberList.end())
  563. {
  564. if (m_MemberList.size() == 2)
  565. break;
  566. n1 = head;
  567. }
  568. hr = Conn.Init((*n1)->m_bstrMemberDN, (*n1)->m_bstrServer, (*n1)->m_bstrSite,
  569. (*n2)->m_bstrMemberDN, (*n2)->m_bstrServer, (*n2)->m_bstrSite,
  570. TRUE, CONNECTION_OPTYPE_ADD);
  571. BREAK_IF_FAILED(hr);
  572. hr = _AddToConnectionListAndView(&Conn);
  573. BREAK_IF_FAILED(hr);
  574. hr = Conn.Init((*n2)->m_bstrMemberDN, (*n2)->m_bstrServer, (*n2)->m_bstrSite,
  575. (*n1)->m_bstrMemberDN, (*n1)->m_bstrServer, (*n1)->m_bstrSite,
  576. TRUE, CONNECTION_OPTYPE_ADD);
  577. BREAK_IF_FAILED(hr);
  578. hr = _AddToConnectionListAndView(&Conn);
  579. BREAK_IF_FAILED(hr);
  580. if (n1 == head)
  581. break;
  582. }
  583. } else if (!lstrcmpi(i_bstrTopologyPref, FRS_RSTOPOLOGYPREF_HUBSPOKE))
  584. {
  585. for (n1 = m_MemberList.begin(); n1 != m_MemberList.end(); n1++)
  586. {
  587. if (!lstrcmpi((*n1)->m_bstrMemberDN, i_pHubMember->m_bstrMemberDN))
  588. continue;
  589. hr = Conn.Init((*n1)->m_bstrMemberDN, (*n1)->m_bstrServer, (*n1)->m_bstrSite,
  590. i_pHubMember->m_bstrMemberDN, i_pHubMember->m_bstrServer, i_pHubMember->m_bstrSite,
  591. TRUE, CONNECTION_OPTYPE_ADD);
  592. BREAK_IF_FAILED(hr);
  593. hr = _AddToConnectionListAndView(&Conn);
  594. BREAK_IF_FAILED(hr);
  595. hr = Conn.Init(i_pHubMember->m_bstrMemberDN, i_pHubMember->m_bstrServer, i_pHubMember->m_bstrSite,
  596. (*n1)->m_bstrMemberDN, (*n1)->m_bstrServer, (*n1)->m_bstrSite,
  597. TRUE, CONNECTION_OPTYPE_ADD);
  598. BREAK_IF_FAILED(hr);
  599. hr = _AddToConnectionListAndView(&Conn);
  600. BREAK_IF_FAILED(hr);
  601. }
  602. } else if (!lstrcmpi(i_bstrTopologyPref, FRS_RSTOPOLOGYPREF_FULLMESH))
  603. {
  604. for (n1 = m_MemberList.begin(); n1 != m_MemberList.end(); n1++)
  605. {
  606. for (n2 = m_MemberList.begin(); n2 != m_MemberList.end(); n2++)
  607. {
  608. if (!lstrcmpi((*n1)->m_bstrMemberDN, (*n2)->m_bstrMemberDN))
  609. continue;
  610. hr = Conn.Init((*n1)->m_bstrMemberDN, (*n1)->m_bstrServer, (*n1)->m_bstrSite,
  611. (*n2)->m_bstrMemberDN, (*n2)->m_bstrServer, (*n2)->m_bstrSite,
  612. TRUE, CONNECTION_OPTYPE_ADD);
  613. BREAK_IF_FAILED(hr);
  614. hr = _AddToConnectionListAndView(&Conn);
  615. BREAK_IF_FAILED(hr);
  616. }
  617. BREAK_IF_FAILED(hr);
  618. }
  619. }
  620. return hr;
  621. }
  622. HRESULT CCustomTopology::_SetConnectionState(CCusTopConnection *pConn)
  623. {
  624. HWND hwnd = GetDlgItem(IDC_FRS_CUSTOP_CONNECTIONS);
  625. int nIndex = ListView_GetNextItem(hwnd, -1, LVNI_ALL);
  626. while (-1 != nIndex)
  627. {
  628. if (pConn == (CCusTopConnection *)GetListViewItemData(hwnd, nIndex))
  629. break;
  630. nIndex = ListView_GetNextItem(hwnd, nIndex, LVNI_ALL);
  631. }
  632. if (-1 != nIndex)
  633. {
  634. ListView_SetCheckState(hwnd, nIndex, pConn->m_bStateNew);
  635. ListView_Update(hwnd, nIndex);
  636. }
  637. return S_OK;
  638. }
  639. HRESULT CCustomTopology::_InsertConnection(CCusTopConnection *pConn)
  640. {
  641. RETURN_INVALIDARG_IF_NULL(pConn);
  642. HWND hwndControl = GetDlgItem(IDC_FRS_CUSTOP_CONNECTIONS);
  643. LVITEM lvItem;
  644. lvItem.mask = LVIF_TEXT | LVIF_PARAM;
  645. lvItem.lParam = (LPARAM)pConn;
  646. lvItem.pszText = _T("");
  647. lvItem.iSubItem = 0;
  648. int iItemIndex = ListView_InsertItem(hwndControl, &lvItem);
  649. lvItem.mask = LVIF_TEXT;
  650. lvItem.iItem = iItemIndex;
  651. lvItem.pszText = pConn->m_bstrFromServer;
  652. lvItem.iSubItem = 1;
  653. ListView_SetItem(hwndControl, &lvItem);
  654. lvItem.mask = LVIF_TEXT;
  655. lvItem.iItem = iItemIndex;
  656. lvItem.pszText = pConn->m_bstrToServer;
  657. lvItem.iSubItem = 2;
  658. ListView_SetItem(hwndControl, &lvItem);
  659. lvItem.mask = LVIF_TEXT;
  660. lvItem.iItem = iItemIndex;
  661. lvItem.pszText = pConn->m_bstrFromSite;
  662. lvItem.iSubItem = 3;
  663. ListView_SetItem(hwndControl, &lvItem);
  664. lvItem.mask = LVIF_TEXT;
  665. lvItem.iItem = iItemIndex;
  666. lvItem.pszText = pConn->m_bstrToSite;
  667. lvItem.iSubItem = 4;
  668. ListView_SetItem(hwndControl, &lvItem);
  669. ListView_SetCheckState(hwndControl, iItemIndex, pConn->m_bStateNew);
  670. ListView_Update(hwndControl, iItemIndex);
  671. return S_OK;
  672. }
  673. LRESULT CCustomTopology::OnConnectionsNew
  674. (
  675. WORD wNotifyCode,
  676. WORD wID,
  677. HWND hWndCtl,
  678. BOOL& bHandled
  679. )
  680. {
  681. HRESULT hr = S_OK;
  682. CNewConnections NewConnDlg;
  683. // Building the RING topology might have sorted the members on sites,
  684. // we need to re-sort it on server alphabetically before we give it to the New Connections dialog
  685. _SortMemberList();
  686. hr = NewConnDlg.Initialize(&m_MemberList);
  687. if (SUCCEEDED(hr))
  688. {
  689. hr = NewConnDlg.DoModal();
  690. if (S_OK == hr)
  691. {
  692. CCusTopConnectionList* pNewConnectionList = NULL;
  693. hr = NewConnDlg.get_NewConnections(&pNewConnectionList);
  694. if (SUCCEEDED(hr))
  695. {
  696. HWND hwnd = GetDlgItem(IDC_FRS_CUSTOP_CONNECTIONS);
  697. int nCount = ListView_GetItemCount(hwnd);
  698. CCusTopConnectionList::iterator it;
  699. for (it = pNewConnectionList->begin(); it != pNewConnectionList->end(); it++)
  700. {
  701. hr = _AddToConnectionListAndView(*it);
  702. BREAK_IF_FAILED(hr);
  703. }
  704. if (ListView_GetItemCount(hwnd) > nCount)
  705. ListView_SortItems(hwnd, ConnectionsListCompareProc, (LPARAM)g_FRS_CUSTOP_Last_SortColumn);
  706. }
  707. }
  708. }
  709. // if FAILED, display msg?
  710. return (SUCCEEDED(hr));
  711. }
  712. LRESULT CCustomTopology::OnConnectionsDelete
  713. (
  714. WORD wNotifyCode,
  715. WORD wID,
  716. HWND hWndCtl,
  717. BOOL& bHandled
  718. )
  719. {
  720. CCusTopConnection* pConn;
  721. HWND hwnd = GetDlgItem(IDC_FRS_CUSTOP_CONNECTIONS);
  722. int nIndex = ListView_GetNextItem(hwnd, -1, LVNI_ALL | LVNI_SELECTED);
  723. while (-1 != nIndex &&
  724. (pConn = (CCusTopConnection *)GetListViewItemData(hwnd, nIndex)))
  725. {
  726. _RemoveFromConnectionList(pConn);
  727. ListView_DeleteItem(hwnd, nIndex);
  728. nIndex = ListView_GetNextItem(hwnd, -1, LVNI_ALL | LVNI_SELECTED);
  729. }
  730. return TRUE;
  731. }
  732. HRESULT CCustomTopology::_InitScheduleOnSelectedConnections()
  733. {
  734. HRESULT hr = S_OK;
  735. int nIndex = -1;
  736. CCusTopConnection* pConn = NULL;
  737. HWND hwnd = GetDlgItem(IDC_FRS_CUSTOP_CONNECTIONS);
  738. while ( -1 != (nIndex = ListView_GetNextItem(hwnd, nIndex, LVNI_ALL | LVNI_SELECTED)) &&
  739. NULL != (pConn = (CCusTopConnection *)GetListViewItemData(hwnd, nIndex)))
  740. {
  741. if (!pConn->m_pScheduleNew)
  742. {
  743. if (pConn->m_pScheduleOld)
  744. {
  745. hr = CopySchedule(pConn->m_pScheduleOld, &pConn->m_pScheduleNew);
  746. RETURN_IF_FAILED(hr);
  747. } else
  748. {
  749. if (CONNECTION_OPTYPE_OTHERS == pConn->m_opType)
  750. {
  751. //
  752. // read schedule on an existing connection for the very first time
  753. //
  754. VARIANT var;
  755. VariantInit(&var);
  756. hr = m_piReplicaSet->GetConnectionScheduleEx(pConn->m_bstrFromMemberDN, pConn->m_bstrToMemberDN, &var);
  757. RETURN_IF_FAILED(hr);
  758. hr = VariantToSchedule(&var, &pConn->m_pScheduleOld);
  759. VariantClear(&var);
  760. if (SUCCEEDED(hr))
  761. hr = CopySchedule(pConn->m_pScheduleOld, &pConn->m_pScheduleNew);
  762. RETURN_IF_FAILED(hr);
  763. } else
  764. { // must be ADD operation
  765. hr = GetDefaultSchedule(&pConn->m_pScheduleNew);
  766. RETURN_IF_FAILED(hr);
  767. }
  768. }
  769. }
  770. }
  771. return hr;
  772. }
  773. HRESULT CCustomTopology::_UpdateScheduleOnSelectedConnections(IN SCHEDULE* i_pSchedule)
  774. {
  775. HRESULT hr = S_OK;
  776. int nIndex = -1;
  777. CCusTopConnection* pConn = NULL;
  778. HWND hwnd = GetDlgItem(IDC_FRS_CUSTOP_CONNECTIONS);
  779. while ( -1 != (nIndex = ListView_GetNextItem(hwnd, nIndex, LVNI_ALL | LVNI_SELECTED)) &&
  780. NULL != (pConn = (CCusTopConnection *)GetListViewItemData(hwnd, nIndex)))
  781. {
  782. if (pConn->m_pScheduleNew)
  783. {
  784. free(pConn->m_pScheduleNew);
  785. pConn->m_pScheduleNew = NULL;
  786. }
  787. hr = CopySchedule(i_pSchedule, &pConn->m_pScheduleNew);
  788. BREAK_IF_FAILED(hr);
  789. }
  790. return hr;
  791. }
  792. LRESULT CCustomTopology::OnSchedule
  793. (
  794. WORD wNotifyCode,
  795. WORD wID,
  796. HWND hWndCtl,
  797. BOOL& bHandled
  798. )
  799. {
  800. HRESULT hr = S_OK;
  801. HWND hwnd = GetDlgItem(IDC_FRS_CUSTOP_CONNECTIONS);
  802. int nCount = ListView_GetSelectedCount(hwnd);
  803. if (nCount < 1)
  804. {
  805. DisplayMessageBoxWithOK(IDS_FRS_CUSTOP_NOSELECTION);
  806. return FALSE;
  807. }
  808. do {
  809. //
  810. // get schedule info on each selected connections
  811. //
  812. hr = _InitScheduleOnSelectedConnections();
  813. BREAK_IF_FAILED(hr);
  814. //
  815. // get schedule of the first selected item
  816. //
  817. int nIndex = ListView_GetNextItem(hwnd, -1, LVNI_ALL | LVNI_SELECTED);
  818. if (-1 == nIndex)
  819. {
  820. hr = E_INVALIDARG;
  821. break;
  822. }
  823. CCusTopConnection* pConn = (CCusTopConnection *)GetListViewItemData(hwnd, nIndex);
  824. if (!pConn)
  825. {
  826. hr = E_INVALIDARG;
  827. break;
  828. }
  829. SCHEDULE* pSchedule = NULL;
  830. hr = CopySchedule(pConn->m_pScheduleNew, &pSchedule);
  831. BREAK_IF_FAILED(hr);
  832. hr = InvokeScheduleDlg(m_hWnd, pSchedule);
  833. if (S_OK == hr)
  834. hr = _UpdateScheduleOnSelectedConnections(pSchedule);
  835. free(pSchedule);
  836. } while (0);
  837. if (FAILED(hr))
  838. DisplayMessageBoxForHR(hr);
  839. return (SUCCEEDED(hr));
  840. }
  841. LRESULT CCustomTopology::OnAdvanced
  842. (
  843. WORD wNotifyCode,
  844. WORD wID,
  845. HWND hWndCtl,
  846. BOOL& bHandled
  847. )
  848. {
  849. HWND hwnd = GetDlgItem(IDC_FRS_CUSTOP_CONNECTIONS);
  850. //
  851. // get ToServer of the first selected connection
  852. //
  853. PTSTR pszToServer = NULL;
  854. int nIndex = ListView_GetNextItem(hwnd, -1, LVNI_ALL | LVNI_SELECTED);
  855. if (-1 != nIndex)
  856. {
  857. CCusTopConnection* pConn = (CCusTopConnection *)GetListViewItemData(hwnd, nIndex);
  858. if (pConn)
  859. {
  860. pszToServer = pConn->m_bstrToServer;
  861. }
  862. }
  863. // Building the RING topology might have sorted the members on sites,
  864. // we need to re-sort it on server alphabetically before we give it to the Advanced dialog
  865. _SortMemberList();
  866. CFRSAdvanced frsAdvancedDlg;
  867. HRESULT hr = frsAdvancedDlg.Initialize(&m_MemberList, &m_ConnectionList, pszToServer);
  868. if (SUCCEEDED(hr))
  869. {
  870. hr = frsAdvancedDlg.DoModal();
  871. }
  872. if (FAILED(hr))
  873. DisplayMessageBoxForHR(hr);
  874. return (SUCCEEDED(hr));
  875. }
  876. LRESULT CCustomTopology::OnOK
  877. (
  878. WORD wNotifyCode,
  879. WORD wID,
  880. HWND hWndCtl,
  881. BOOL& bHandled
  882. )
  883. {
  884. CWaitCursor wait;
  885. BOOL bValidInput = FALSE;
  886. int idString = 0;
  887. HRESULT hr = S_OK;
  888. do {
  889. //
  890. // if changed, update TopologyPref
  891. //
  892. int index = SendDlgItemMessage(IDC_FRS_CUSTOP_TOPOLOGYPREF, CB_GETCURSEL, 0, 0);
  893. if (0 != lstrcmpi(m_bstrTopologyPref, g_TopologyPref[index].pszTopologyPref))
  894. {
  895. hr = m_piReplicaSet->put_TopologyPref(g_TopologyPref[index].pszTopologyPref);
  896. BREAK_IF_FAILED(hr);
  897. }
  898. //
  899. // if changed, update HubServer
  900. //
  901. if (!lstrcmpi(m_bstrTopologyPref, FRS_RSTOPOLOGYPREF_HUBSPOKE) &&
  902. 0 != lstrcmpi(g_TopologyPref[index].pszTopologyPref, FRS_RSTOPOLOGYPREF_HUBSPOKE))
  903. {
  904. hr = m_piReplicaSet->put_HubMemberDN(NULL);
  905. BREAK_IF_FAILED(hr);
  906. } else if (!lstrcmpi(g_TopologyPref[index].pszTopologyPref, FRS_RSTOPOLOGYPREF_HUBSPOKE))
  907. {
  908. CCusTopMember* pHubMember = NULL;
  909. hr = _GetHubMember(&pHubMember);
  910. BREAK_IF_FAILED(hr);
  911. if (0 != lstrcmpi(m_bstrHubMemberDN, pHubMember->m_bstrMemberDN))
  912. hr = m_piReplicaSet->put_HubMemberDN(pHubMember->m_bstrMemberDN);
  913. BREAK_IF_FAILED(hr);
  914. }
  915. //
  916. // if changed, update connections
  917. //
  918. CCusTopConnection* pConn = NULL;
  919. HWND hwnd = GetDlgItem(IDC_FRS_CUSTOP_CONNECTIONS);
  920. index = -1;
  921. while (-1 != (index = ListView_GetNextItem(hwnd, index, LVNI_ALL)))
  922. {
  923. pConn = (CCusTopConnection *)GetListViewItemData(hwnd, index);
  924. if (pConn)
  925. pConn->m_bStateNew = ListView_GetCheckState(hwnd, index);
  926. }
  927. hr = _MakeConnections();
  928. BREAK_IF_FAILED(hr);
  929. bValidInput = TRUE;
  930. } while (0);
  931. if (FAILED(hr))
  932. {
  933. DisplayMessageBoxForHR(hr);
  934. return FALSE;
  935. } else if (bValidInput)
  936. {
  937. EndDialog(S_OK);
  938. return TRUE;
  939. }
  940. else
  941. {
  942. if (idString)
  943. DisplayMessageBoxWithOK(idString);
  944. return FALSE;
  945. }
  946. }
  947. LRESULT CCustomTopology::OnCancel
  948. (
  949. WORD wNotifyCode,
  950. WORD wID,
  951. HWND hWndCtl,
  952. BOOL& bHandled
  953. )
  954. {
  955. /*++
  956. Routine Description:
  957. Called OnCancel. Ends the dialog with S_FALSE;
  958. */
  959. EndDialog(S_FALSE);
  960. return(true);
  961. }
  962. int CALLBACK ConnectionsListCompareProc(
  963. IN LPARAM lParam1,
  964. IN LPARAM lParam2,
  965. IN LPARAM lParamColumn)
  966. {
  967. CCusTopConnection* pItem1 = (CCusTopConnection *)lParam1;
  968. CCusTopConnection* pItem2 = (CCusTopConnection *)lParam2;
  969. int iResult = 0;
  970. if (pItem1 && pItem2)
  971. {
  972. g_FRS_CUSTOP_Last_SortColumn = lParamColumn;
  973. switch (lParamColumn)
  974. {
  975. case 0: // Sort by bStateNew.
  976. iResult = pItem1->m_bStateNew - pItem2->m_bStateNew;
  977. break;
  978. case 1: // Sort by From Server.
  979. iResult = lstrcmpi(pItem1->m_bstrFromServer, pItem2->m_bstrFromServer);
  980. break;
  981. case 2: // Sort by To Server.
  982. iResult = lstrcmpi(pItem1->m_bstrToServer, pItem2->m_bstrToServer);
  983. break;
  984. case 3: // Sort by From Site.
  985. iResult = lstrcmpi(pItem1->m_bstrFromSite, pItem2->m_bstrFromSite);
  986. break;
  987. case 4: // Sort by To Site.
  988. iResult = lstrcmpi(pItem1->m_bstrToSite, pItem2->m_bstrToSite);
  989. break;
  990. default:
  991. iResult = 0;
  992. break;
  993. }
  994. }
  995. return(iResult);
  996. }
  997. LRESULT
  998. CCustomTopology::OnNotify(
  999. IN UINT i_uMsg,
  1000. IN WPARAM i_wParam,
  1001. IN LPARAM i_lParam,
  1002. IN OUT BOOL& io_bHandled
  1003. )
  1004. {
  1005. NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)i_lParam;
  1006. io_bHandled = FALSE; // So that the base class gets this notify too
  1007. if (IDC_FRS_CUSTOP_CONNECTIONS == pNMListView->hdr.idFrom)
  1008. {
  1009. HWND hwndList = GetDlgItem(IDC_FRS_CUSTOP_CONNECTIONS);
  1010. if (LVN_COLUMNCLICK == pNMListView->hdr.code)
  1011. {
  1012. // sort items
  1013. ListView_SortItems( hwndList,
  1014. ConnectionsListCompareProc,
  1015. (LPARAM)(pNMListView->iSubItem));
  1016. io_bHandled = TRUE;
  1017. } else if (LVN_ITEMCHANGED == pNMListView->hdr.code)
  1018. {
  1019. _EnableButtonsForConnectionList();
  1020. }
  1021. }
  1022. return io_bHandled;
  1023. }
  1024. //
  1025. // Update the connection objects in the DS
  1026. //
  1027. HRESULT CCustomTopology::_MakeConnections()
  1028. {
  1029. HRESULT hr = S_OK;
  1030. CCusTopConnectionList::iterator it;
  1031. for (it = m_ConnectionList.begin(); it != m_ConnectionList.end(); it++)
  1032. {
  1033. switch ((*it)->m_opType)
  1034. {
  1035. case CONNECTION_OPTYPE_ADD:
  1036. hr = m_piReplicaSet->AddConnection(
  1037. (*it)->m_bstrFromMemberDN,
  1038. (*it)->m_bstrToMemberDN,
  1039. (*it)->m_bStateNew,
  1040. (*it)->m_bSyncImmediatelyNew,
  1041. (*it)->m_nPriorityNew,
  1042. NULL
  1043. );
  1044. BREAK_IF_FAILED(hr);
  1045. if ((*it)->m_pScheduleNew)
  1046. {
  1047. VARIANT var;
  1048. VariantInit(&var);
  1049. hr = ScheduleToVariant((*it)->m_pScheduleNew, &var);
  1050. BREAK_IF_FAILED(hr);
  1051. hr = m_piReplicaSet->SetConnectionScheduleEx(
  1052. (*it)->m_bstrFromMemberDN,
  1053. (*it)->m_bstrToMemberDN,
  1054. &var);
  1055. VariantClear(&var);
  1056. }
  1057. break;
  1058. case CONNECTION_OPTYPE_DEL:
  1059. hr = m_piReplicaSet->RemoveConnectionEx(
  1060. (*it)->m_bstrFromMemberDN,
  1061. (*it)->m_bstrToMemberDN
  1062. );
  1063. break;
  1064. default:
  1065. if ((*it)->m_bStateNew != (*it)->m_bStateOld)
  1066. {
  1067. hr = m_piReplicaSet->EnableConnectionEx(
  1068. (*it)->m_bstrFromMemberDN,
  1069. (*it)->m_bstrToMemberDN,
  1070. (*it)->m_bStateNew
  1071. );
  1072. BREAK_IF_FAILED(hr);
  1073. }
  1074. if ((*it)->m_bSyncImmediatelyNew != (*it)->m_bSyncImmediatelyOld ||
  1075. (*it)->m_nPriorityNew != (*it)->m_nPriorityOld)
  1076. {
  1077. hr = m_piReplicaSet->SetConnectionOptionsEx(
  1078. (*it)->m_bstrFromMemberDN,
  1079. (*it)->m_bstrToMemberDN,
  1080. (*it)->m_bSyncImmediatelyNew,
  1081. (*it)->m_nPriorityNew
  1082. );
  1083. BREAK_IF_FAILED(hr);
  1084. }
  1085. if (S_OK == CompareSchedules((*it)->m_pScheduleNew, (*it)->m_pScheduleOld))
  1086. break; // no change on shcedule
  1087. if ((*it)->m_pScheduleNew)
  1088. {
  1089. VARIANT var;
  1090. VariantInit(&var);
  1091. hr = ScheduleToVariant((*it)->m_pScheduleNew, &var);
  1092. BREAK_IF_FAILED(hr);
  1093. hr = m_piReplicaSet->SetConnectionScheduleEx(
  1094. (*it)->m_bstrFromMemberDN,
  1095. (*it)->m_bstrToMemberDN,
  1096. &var);
  1097. VariantClear(&var);
  1098. }
  1099. break;
  1100. }
  1101. BREAK_IF_FAILED(hr);
  1102. }
  1103. return hr;
  1104. }
  1105. //////////////////////////////////////////////////////////
  1106. //
  1107. //
  1108. void FreeCusTopMembers(CCusTopMemberList* pList)
  1109. {
  1110. if (pList && !pList->empty())
  1111. {
  1112. for (CCusTopMemberList::iterator i = pList->begin(); i != pList->end(); i++)
  1113. delete (*i);
  1114. pList->clear();
  1115. }
  1116. }
  1117. void FreeCusTopConnections(CCusTopConnectionList* pList)
  1118. {
  1119. if (pList && !pList->empty())
  1120. {
  1121. for (CCusTopConnectionList::iterator i = pList->begin(); i != pList->end(); i++)
  1122. delete (*i);
  1123. pList->clear();
  1124. }
  1125. }
  1126. //////////////////////////////////////////////////////////
  1127. //
  1128. // CCusTopMember
  1129. //
  1130. CCusTopMember::~CCusTopMember()
  1131. {
  1132. _Reset();
  1133. }
  1134. HRESULT CCusTopMember::Init(BSTR bstrMemberDN, BSTR bstrServer, BSTR bstrSite)
  1135. {
  1136. RETURN_INVALIDARG_IF_NULL(bstrMemberDN);
  1137. RETURN_INVALIDARG_IF_NULL(bstrServer);
  1138. RETURN_INVALIDARG_IF_NULL(bstrSite);
  1139. _Reset();
  1140. HRESULT hr = S_OK;
  1141. do {
  1142. m_bstrMemberDN = bstrMemberDN;
  1143. BREAK_OUTOFMEMORY_IF_NULL((BSTR)m_bstrMemberDN, &hr);
  1144. m_bstrServer = bstrServer;
  1145. BREAK_OUTOFMEMORY_IF_NULL((BSTR)m_bstrServer, &hr);
  1146. m_bstrSite = bstrSite;
  1147. BREAK_OUTOFMEMORY_IF_NULL((BSTR)m_bstrSite, &hr);
  1148. } while (0);
  1149. if (FAILED(hr))
  1150. _Reset();
  1151. return hr;
  1152. }
  1153. void CCusTopMember::_Reset()
  1154. {
  1155. m_bstrMemberDN.Empty();
  1156. m_bstrServer.Empty();
  1157. m_bstrSite.Empty();
  1158. }
  1159. //////////////////////////////////////////////////////////
  1160. //
  1161. // CCusTopConnection
  1162. //
  1163. CCusTopConnection::CCusTopConnection()
  1164. {
  1165. m_bStateNew = m_bStateOld= TRUE;
  1166. m_bSyncImmediatelyNew = m_bSyncImmediatelyOld = FALSE;
  1167. m_nPriorityNew = m_nPriorityOld = PRIORITY_LOW;
  1168. m_pScheduleNew = m_pScheduleOld = NULL;
  1169. m_opType = CONNECTION_OPTYPE_OTHERS;
  1170. }
  1171. CCusTopConnection::~CCusTopConnection()
  1172. {
  1173. _Reset();
  1174. }
  1175. HRESULT CCusTopConnection::Init(
  1176. BSTR bstrFromMemberDN, BSTR bstrFromServer, BSTR bstrFromSite,
  1177. BSTR bstrToMemberDN, BSTR bstrToServer, BSTR bstrToSite,
  1178. BOOL bState, // = TRUE
  1179. CONNECTION_OPTYPE opType, // = CONNECTION_OPTYPE_OTHERS
  1180. BOOL bSyncImmediately, // = FALSE,
  1181. Connection_Priority nPriority // = PRIORITY_LOW
  1182. )
  1183. {
  1184. RETURN_INVALIDARG_IF_NULL(bstrFromMemberDN);
  1185. RETURN_INVALIDARG_IF_NULL(bstrFromServer);
  1186. RETURN_INVALIDARG_IF_NULL(bstrFromSite);
  1187. RETURN_INVALIDARG_IF_NULL(bstrToMemberDN);
  1188. RETURN_INVALIDARG_IF_NULL(bstrToServer);
  1189. RETURN_INVALIDARG_IF_NULL(bstrToSite);
  1190. _Reset();
  1191. HRESULT hr = S_OK;
  1192. do {
  1193. m_bstrFromMemberDN = bstrFromMemberDN;
  1194. BREAK_OUTOFMEMORY_IF_NULL((BSTR)m_bstrFromMemberDN, &hr);
  1195. m_bstrFromServer = bstrFromServer;
  1196. BREAK_OUTOFMEMORY_IF_NULL((BSTR)m_bstrFromServer, &hr);
  1197. m_bstrFromSite = bstrFromSite;
  1198. BREAK_OUTOFMEMORY_IF_NULL((BSTR)m_bstrFromSite, &hr);
  1199. m_bstrToMemberDN = bstrToMemberDN;
  1200. BREAK_OUTOFMEMORY_IF_NULL((BSTR)m_bstrToMemberDN, &hr);
  1201. m_bstrToServer = bstrToServer;
  1202. BREAK_OUTOFMEMORY_IF_NULL((BSTR)m_bstrToServer, &hr);
  1203. m_bstrToSite = bstrToSite;
  1204. BREAK_OUTOFMEMORY_IF_NULL((BSTR)m_bstrToSite, &hr);
  1205. m_bStateNew = m_bStateOld = bState;
  1206. m_opType = opType;
  1207. m_pScheduleOld = m_pScheduleNew = NULL;
  1208. m_bSyncImmediatelyNew = m_bSyncImmediatelyOld = bSyncImmediately;
  1209. m_nPriorityNew = m_nPriorityOld = nPriority;
  1210. } while (0);
  1211. if (FAILED(hr))
  1212. _Reset();
  1213. return hr;
  1214. }
  1215. void CCusTopConnection::_Reset()
  1216. {
  1217. m_bstrFromMemberDN.Empty();
  1218. m_bstrFromServer.Empty();
  1219. m_bstrFromSite.Empty();
  1220. m_bstrToMemberDN.Empty();
  1221. m_bstrToServer.Empty();
  1222. m_bstrToSite.Empty();
  1223. m_bStateOld = TRUE;
  1224. m_bStateNew = TRUE;
  1225. m_opType = CONNECTION_OPTYPE_OTHERS;
  1226. m_bSyncImmediatelyNew = m_bSyncImmediatelyOld = FALSE;
  1227. m_nPriorityNew = m_nPriorityOld = PRIORITY_LOW;
  1228. if (m_pScheduleOld)
  1229. {
  1230. free(m_pScheduleOld);
  1231. m_pScheduleOld = NULL;
  1232. }
  1233. if (m_pScheduleNew)
  1234. {
  1235. free(m_pScheduleNew);
  1236. m_pScheduleNew = NULL;
  1237. }
  1238. }
  1239. HRESULT CCusTopConnection::Copy(CCusTopConnection* pConn)
  1240. {
  1241. if (!pConn || !(pConn->m_bstrFromMemberDN) || !*(pConn->m_bstrFromMemberDN))
  1242. return E_INVALIDARG;
  1243. _Reset();
  1244. HRESULT hr = S_OK;
  1245. do {
  1246. m_bstrFromMemberDN = pConn->m_bstrFromMemberDN;
  1247. BREAK_OUTOFMEMORY_IF_NULL((BSTR)m_bstrFromMemberDN, &hr);
  1248. m_bstrFromServer = pConn->m_bstrFromServer;
  1249. BREAK_OUTOFMEMORY_IF_NULL((BSTR)m_bstrFromServer, &hr);
  1250. m_bstrFromSite = pConn->m_bstrFromSite;
  1251. BREAK_OUTOFMEMORY_IF_NULL((BSTR)m_bstrFromSite, &hr);
  1252. m_bstrToMemberDN = pConn->m_bstrToMemberDN;
  1253. BREAK_OUTOFMEMORY_IF_NULL((BSTR)m_bstrToMemberDN, &hr);
  1254. m_bstrToServer = pConn->m_bstrToServer;
  1255. BREAK_OUTOFMEMORY_IF_NULL((BSTR)m_bstrToServer, &hr);
  1256. m_bstrToSite = pConn->m_bstrToSite;
  1257. BREAK_OUTOFMEMORY_IF_NULL((BSTR)m_bstrToSite, &hr);
  1258. m_bStateOld = pConn->m_bStateOld;
  1259. m_bStateNew = pConn->m_bStateNew;
  1260. m_bSyncImmediatelyOld = pConn->m_bSyncImmediatelyOld;
  1261. m_bSyncImmediatelyNew = pConn->m_bSyncImmediatelyNew;
  1262. m_nPriorityOld = pConn->m_nPriorityOld;
  1263. m_nPriorityNew = pConn->m_nPriorityNew;
  1264. m_opType = pConn->m_opType;
  1265. if (pConn->m_pScheduleOld)
  1266. {
  1267. hr = CopySchedule(pConn->m_pScheduleOld, &m_pScheduleOld);
  1268. BREAK_IF_FAILED(hr);
  1269. }
  1270. if (pConn->m_pScheduleNew)
  1271. {
  1272. hr = CopySchedule(pConn->m_pScheduleNew, &m_pScheduleNew);
  1273. BREAK_IF_FAILED(hr);
  1274. }
  1275. } while (0);
  1276. if (FAILED(hr))
  1277. _Reset();
  1278. return hr;
  1279. }
  1280. HRESULT CCustomTopology::_AddToConnectionListAndView(CCusTopConnection* pConn)
  1281. {
  1282. RETURN_INVALIDARG_IF_NULL(pConn);
  1283. BOOL bFound = FALSE;
  1284. CCusTopConnectionList::iterator it;
  1285. for (it = m_ConnectionList.begin(); it != m_ConnectionList.end(); it++)
  1286. {
  1287. if (!lstrcmpi((*it)->m_bstrFromMemberDN, pConn->m_bstrFromMemberDN) &&
  1288. !lstrcmpi((*it)->m_bstrToMemberDN, pConn->m_bstrToMemberDN))
  1289. {
  1290. bFound = TRUE;
  1291. break;
  1292. }
  1293. }
  1294. HRESULT hr = S_OK;
  1295. if (!bFound)
  1296. {
  1297. CCusTopConnection* pNew = new CCusTopConnection;
  1298. RETURN_OUTOFMEMORY_IF_NULL(pNew);
  1299. hr = pNew->Copy(pConn);
  1300. if (FAILED(hr))
  1301. delete pNew;
  1302. else
  1303. {
  1304. m_ConnectionList.push_back(pNew);
  1305. hr = _InsertConnection(pNew);
  1306. }
  1307. } else
  1308. {
  1309. (*it)->m_bStateNew = TRUE;
  1310. (*it)->m_bSyncImmediatelyNew = FALSE;
  1311. (*it)->m_nPriorityNew = PRIORITY_LOW;
  1312. if ((*it)->m_opType == CONNECTION_OPTYPE_DEL)
  1313. {
  1314. (*it)->m_opType = CONNECTION_OPTYPE_OTHERS;
  1315. hr = _InsertConnection(*it);
  1316. } else
  1317. {
  1318. (*it)->m_opType = CONNECTION_OPTYPE_OTHERS;
  1319. hr = _SetConnectionState(*it);
  1320. }
  1321. }
  1322. return hr;
  1323. }
  1324. HRESULT CCustomTopology::_RemoveFromConnectionList(CCusTopConnection* pConn)
  1325. {
  1326. RETURN_INVALIDARG_IF_NULL(pConn);
  1327. BOOL bFound = FALSE;
  1328. CCusTopConnectionList::iterator it;
  1329. for (it = m_ConnectionList.begin(); it != m_ConnectionList.end(); it++)
  1330. {
  1331. if (!lstrcmpi((*it)->m_bstrFromMemberDN, pConn->m_bstrFromMemberDN) &&
  1332. !lstrcmpi((*it)->m_bstrToMemberDN, pConn->m_bstrToMemberDN))
  1333. {
  1334. bFound = TRUE;
  1335. break;
  1336. }
  1337. }
  1338. if (it != m_ConnectionList.end())
  1339. {
  1340. if (CONNECTION_OPTYPE_ADD == (*it)->m_opType)
  1341. {
  1342. delete (*it);
  1343. m_ConnectionList.erase(it);
  1344. } else
  1345. {
  1346. (*it)->m_opType = CONNECTION_OPTYPE_DEL;
  1347. }
  1348. }
  1349. return S_OK;
  1350. }
  1351. /////////////////////////////////////////////////////////////////////////////
  1352. //
  1353. // CNewConnections
  1354. //
  1355. CNewConnections::CNewConnections() : m_pMemberList(NULL)
  1356. {
  1357. }
  1358. CNewConnections::~CNewConnections()
  1359. {
  1360. FreeCusTopConnections(&m_NewConnectionList);
  1361. }
  1362. HRESULT CNewConnections::Initialize
  1363. (
  1364. CCusTopMemberList* i_pMemberList
  1365. )
  1366. {
  1367. RETURN_INVALIDARG_IF_NULL(i_pMemberList);
  1368. m_pMemberList = i_pMemberList;
  1369. return S_OK;
  1370. }
  1371. #define NUM_OF_FRS_NEWCONN_COLUMNS 2
  1372. LRESULT CNewConnections::OnInitDialog
  1373. (
  1374. UINT uMsg,
  1375. WPARAM wParam,
  1376. LPARAM lParam,
  1377. BOOL& bHandled
  1378. )
  1379. {
  1380. int nControlID[] = {IDC_FRS_NEWCONN_FROM, IDC_FRS_NEWCONN_TO};
  1381. int nColStringID[] = {IDS_FRS_NEWCONN_COL_FROMSERVER, IDS_FRS_NEWCONN_COL_TOSERVER};
  1382. HWND hwndControl = NULL;
  1383. CCusTopMemberList::iterator it;
  1384. for (int i = 0; i < 2; i++)
  1385. {
  1386. hwndControl = GetDlgItem(nControlID[i]);
  1387. AddLVColumns(hwndControl, nColStringID[i], NUM_OF_FRS_NEWCONN_COLUMNS);
  1388. ListView_SetExtendedListViewStyle(hwndControl, LVS_EX_FULLROWSELECT);
  1389. for (it = m_pMemberList->begin(); it != m_pMemberList->end(); it++)
  1390. {
  1391. LVITEM lvItem;
  1392. lvItem.mask = LVIF_TEXT | LVIF_PARAM;
  1393. lvItem.lParam = (LPARAM)(*it);
  1394. lvItem.pszText = (*it)->m_bstrServer;
  1395. lvItem.iSubItem = 0;
  1396. int iItemIndex = ListView_InsertItem(hwndControl, &lvItem);
  1397. lvItem.mask = LVIF_TEXT;
  1398. lvItem.iItem = iItemIndex;
  1399. lvItem.pszText = (*it)->m_bstrSite;
  1400. lvItem.iSubItem = 1;
  1401. ListView_SetItem(hwndControl, &lvItem);
  1402. }
  1403. }
  1404. return TRUE; // Let the system set the focus
  1405. }
  1406. /*++
  1407. This function is called when a user clicks the ? in the top right of a property sheet
  1408. and then clciks a control, or when they hit F1 in a control.
  1409. --*/
  1410. LRESULT CNewConnections::OnCtxHelp(
  1411. IN UINT i_uMsg,
  1412. IN WPARAM i_wParam,
  1413. IN LPARAM i_lParam,
  1414. IN OUT BOOL& io_bHandled
  1415. )
  1416. {
  1417. LPHELPINFO lphi = (LPHELPINFO) i_lParam;
  1418. if (!lphi || lphi->iContextType != HELPINFO_WINDOW || lphi->iCtrlId < 0)
  1419. return FALSE;
  1420. ::WinHelp((HWND)(lphi->hItemHandle),
  1421. DFS_CTX_HELP_FILE,
  1422. HELP_WM_HELP,
  1423. (DWORD_PTR)(PVOID)g_aHelpIDs_IDD_FRS_NEWCONN);
  1424. return TRUE;
  1425. }
  1426. /*++
  1427. This function handles "What's This" help when a user right clicks the control
  1428. --*/
  1429. LRESULT CNewConnections::OnCtxMenuHelp(
  1430. IN UINT i_uMsg,
  1431. IN WPARAM i_wParam,
  1432. IN LPARAM i_lParam,
  1433. IN OUT BOOL& io_bHandled
  1434. )
  1435. {
  1436. ::WinHelp((HWND)i_wParam,
  1437. DFS_CTX_HELP_FILE,
  1438. HELP_CONTEXTMENU,
  1439. (DWORD_PTR)(PVOID)g_aHelpIDs_IDD_FRS_NEWCONN);
  1440. return TRUE;
  1441. }
  1442. HRESULT CNewConnections::get_NewConnections(CCusTopConnectionList** ppConnectionList)
  1443. {
  1444. RETURN_INVALIDARG_IF_NULL(ppConnectionList);
  1445. *ppConnectionList = &m_NewConnectionList;
  1446. return S_OK;
  1447. }
  1448. LRESULT CNewConnections::OnOK
  1449. (
  1450. WORD wNotifyCode,
  1451. WORD wID,
  1452. HWND hWndCtl,
  1453. BOOL& bHandled
  1454. )
  1455. {
  1456. BOOL bValidInput = FALSE;
  1457. int idString = 0;
  1458. HRESULT hr = S_OK;
  1459. do {
  1460. idString = IDS_FRS_NEWCONN_NOSELECTION;
  1461. //
  1462. // get all selected From servers
  1463. //
  1464. CCusTopMember* pMember;
  1465. CCusTopMemberList fromMemberList;
  1466. HWND hwndFrom = GetDlgItem(IDC_FRS_NEWCONN_FROM);
  1467. int nIndexFrom = ListView_GetNextItem(hwndFrom, -1, LVNI_ALL | LVNI_SELECTED);
  1468. while (-1 != nIndexFrom &&
  1469. (pMember = (CCusTopMember *)GetListViewItemData(hwndFrom, nIndexFrom)))
  1470. {
  1471. fromMemberList.push_back(pMember);
  1472. nIndexFrom = ListView_GetNextItem(hwndFrom, nIndexFrom, LVNI_ALL | LVNI_SELECTED);
  1473. }
  1474. if (fromMemberList.empty())
  1475. break;
  1476. //
  1477. // get all selected To servers
  1478. //
  1479. CCusTopMemberList toMemberList;
  1480. HWND hwndTo = GetDlgItem(IDC_FRS_NEWCONN_TO);
  1481. int nIndexTo = ListView_GetNextItem(hwndTo, -1, LVNI_ALL | LVNI_SELECTED);
  1482. while (-1 != nIndexTo &&
  1483. (pMember = (CCusTopMember *)GetListViewItemData(hwndTo, nIndexTo)))
  1484. {
  1485. toMemberList.push_back(pMember);
  1486. nIndexTo = ListView_GetNextItem(hwndTo, nIndexTo, LVNI_ALL | LVNI_SELECTED);
  1487. }
  1488. if (toMemberList.empty())
  1489. break;
  1490. //
  1491. // init the list
  1492. //
  1493. FreeCusTopConnections(&m_NewConnectionList);
  1494. //
  1495. // build the connection list
  1496. //
  1497. CCusTopMemberList::iterator from, to;
  1498. for (from = fromMemberList.begin(); from != fromMemberList.end(); from++)
  1499. {
  1500. for (to = toMemberList.begin(); to != toMemberList.end(); to++)
  1501. {
  1502. if (!lstrcmpi((*from)->m_bstrServer, (*to)->m_bstrServer))
  1503. continue;
  1504. CCusTopConnection* pNew = new CCusTopConnection;
  1505. BREAK_OUTOFMEMORY_IF_NULL(pNew, &hr);
  1506. hr = pNew->Init(
  1507. (*from)->m_bstrMemberDN, (*from)->m_bstrServer, (*from)->m_bstrSite,
  1508. (*to)->m_bstrMemberDN, (*to)->m_bstrServer, (*to)->m_bstrSite,
  1509. TRUE, CONNECTION_OPTYPE_ADD);
  1510. BREAK_IF_FAILED(hr);
  1511. m_NewConnectionList.push_back(pNew);
  1512. }
  1513. BREAK_IF_FAILED(hr);
  1514. }
  1515. if (SUCCEEDED(hr))
  1516. bValidInput = TRUE;
  1517. } while (0);
  1518. if (FAILED(hr))
  1519. {
  1520. DisplayMessageBoxForHR(hr);
  1521. return FALSE;
  1522. } else if (bValidInput)
  1523. {
  1524. EndDialog(S_OK);
  1525. return TRUE;
  1526. }
  1527. else
  1528. {
  1529. if (idString)
  1530. DisplayMessageBoxWithOK(idString);
  1531. return FALSE;
  1532. }
  1533. }
  1534. LRESULT CNewConnections::OnCancel
  1535. (
  1536. WORD wNotifyCode,
  1537. WORD wID,
  1538. HWND hWndCtl,
  1539. BOOL& bHandled
  1540. )
  1541. {
  1542. /*++
  1543. Routine Description:
  1544. Called OnCancel. Ends the dialog with S_FALSE;
  1545. */
  1546. EndDialog(S_FALSE);
  1547. return(true);
  1548. }
  1549. int CALLBACK MembersListCompareProc(
  1550. IN LPARAM lParam1,
  1551. IN LPARAM lParam2,
  1552. IN LPARAM lParamColumn)
  1553. {
  1554. CCusTopMember* pItem1 = (CCusTopMember *)lParam1;
  1555. CCusTopMember* pItem2 = (CCusTopMember *)lParam2;
  1556. int iResult = 0;
  1557. if (pItem1 && pItem2)
  1558. {
  1559. switch( lParamColumn)
  1560. {
  1561. case 0: // Sort by Server.
  1562. iResult = lstrcmpi(pItem1->m_bstrServer, pItem2->m_bstrServer);
  1563. break;
  1564. case 1: // Sort by Site.
  1565. iResult = lstrcmpi(pItem1->m_bstrSite, pItem2->m_bstrSite);
  1566. break;
  1567. default:
  1568. iResult = 0;
  1569. break;
  1570. }
  1571. }
  1572. return(iResult);
  1573. }
  1574. LRESULT
  1575. CNewConnections::OnNotify(
  1576. IN UINT i_uMsg,
  1577. IN WPARAM i_wParam,
  1578. IN LPARAM i_lParam,
  1579. IN OUT BOOL& io_bHandled
  1580. )
  1581. {
  1582. NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)i_lParam;
  1583. io_bHandled = FALSE; // So that the base class gets this notify too
  1584. if (IDC_FRS_NEWCONN_FROM == pNMListView->hdr.idFrom ||
  1585. IDC_FRS_NEWCONN_TO == pNMListView->hdr.idFrom)
  1586. {
  1587. HWND hwndList = GetDlgItem(pNMListView->hdr.idFrom);
  1588. if (LVN_COLUMNCLICK == pNMListView->hdr.code)
  1589. {
  1590. // sort items
  1591. ListView_SortItems( hwndList,
  1592. MembersListCompareProc,
  1593. (LPARAM)(pNMListView->iSubItem));
  1594. io_bHandled = TRUE;
  1595. }
  1596. }
  1597. return io_bHandled;
  1598. }