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.

1545 lines
42 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (C) Microsoft Corporation, 1997 - 1999
  6. //
  7. // File: statdlg.cpp
  8. //
  9. //--------------------------------------------------------------------------
  10. #include "pch.h"
  11. #pragma hdrstop
  12. #include "statdlg.h"
  13. #include "resource.h"
  14. #include "folder.h"
  15. #include "cscst.h"
  16. #include "options.h"
  17. #include "fopendlg.h"
  18. #include "msgbox.h"
  19. #include "strings.h"
  20. // Global used for IsDialogMessage processing
  21. HWND g_hwndStatusDlg = NULL;
  22. CStatusDlg::CStatusDlg(
  23. HINSTANCE hInstance,
  24. LPCTSTR pszText,
  25. eSysTrayState eState,
  26. Modes mode // Optional. Default is MODE_NORMAL
  27. ) : m_hInstance(hInstance),
  28. m_hwndDlg(NULL),
  29. m_hwndLV(NULL),
  30. m_himl(NULL),
  31. m_mode(mode),
  32. m_bExpanded(true),
  33. m_bSortAscending(true),
  34. m_iLastColSorted(-1),
  35. m_eSysTrayState(eState),
  36. m_cyExpanded(0),
  37. m_pszText(StrDup(pszText))
  38. {
  39. }
  40. CStatusDlg::~CStatusDlg(
  41. void
  42. )
  43. {
  44. if (NULL != m_pszText)
  45. {
  46. LocalFree(m_pszText);
  47. }
  48. }
  49. int
  50. CStatusDlg::Create(
  51. HWND hwndParent,
  52. LPCTSTR pszText,
  53. eSysTrayState eState,
  54. Modes mode
  55. )
  56. {
  57. int iResult = 0;
  58. CStatusDlg *pdlg = new CStatusDlg(g_hInstance, pszText, eState, mode);
  59. if (pdlg)
  60. {
  61. iResult = pdlg->Run(hwndParent);
  62. if (!iResult)
  63. delete pdlg;
  64. // else pdlg is automatically deleted when the dialog is closed
  65. }
  66. return iResult;
  67. }
  68. //
  69. // Run the status dialog as a modeless dialog.
  70. // Activates an existing instance if one is available.
  71. //
  72. int
  73. CStatusDlg::Run(
  74. HWND hwndParent
  75. )
  76. {
  77. //
  78. // First activate an existing instance if one is already running.
  79. //
  80. int iResult = 0;
  81. TCHAR szDlgTitle[MAX_PATH];
  82. LoadString(m_hInstance, IDS_STATUSDLG_TITLE, szDlgTitle, ARRAYSIZE(szDlgTitle));
  83. m_hwndDlg = FindWindow(NULL, szDlgTitle);
  84. if (NULL != m_hwndDlg && IsWindow(m_hwndDlg) && IsWindowVisible(m_hwndDlg))
  85. {
  86. SetForegroundWindow(m_hwndDlg);
  87. }
  88. else
  89. {
  90. //
  91. // Otherwise create a new dialog.
  92. // We need to use CreateDialog rather than DialogBox because
  93. // sometimes the dialog is hidden. DialogBox doesn't allow us to
  94. // change the visibility attributed defined in the dialog template.
  95. //
  96. m_hwndDlg = CreateDialogParam(m_hInstance,
  97. MAKEINTRESOURCE(IDD_CSCUI_STATUS),
  98. hwndParent,
  99. DlgProc,
  100. (LPARAM)this);
  101. if (NULL != m_hwndDlg)
  102. {
  103. MSG msg;
  104. ShowWindow(m_hwndDlg, MODE_NORMAL == m_mode ? SW_NORMAL : SW_HIDE);
  105. UpdateWindow(m_hwndDlg);
  106. // We don't need a message loop here. We're on systray's main
  107. // thread which has a message pump.
  108. iResult = 1;
  109. }
  110. }
  111. g_hwndStatusDlg = m_hwndDlg;
  112. return iResult;
  113. }
  114. void
  115. CStatusDlg::Destroy(
  116. void
  117. )
  118. {
  119. if (NULL != m_hwndDlg)
  120. DestroyWindow(m_hwndDlg);
  121. g_hwndStatusDlg = NULL;
  122. }
  123. INT_PTR CALLBACK
  124. CStatusDlg::DlgProc(
  125. HWND hwnd,
  126. UINT uMsg,
  127. WPARAM wParam,
  128. LPARAM lParam
  129. )
  130. {
  131. CStatusDlg *pThis = (CStatusDlg *)GetWindowLongPtr(hwnd, DWLP_USER);
  132. BOOL bResult = FALSE;
  133. switch(uMsg)
  134. {
  135. case WM_INITDIALOG:
  136. {
  137. pThis = (CStatusDlg *)lParam;
  138. SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR)pThis);
  139. pThis->m_hwndDlg = hwnd;
  140. bResult = pThis->OnInitDialog(wParam, lParam);
  141. break;
  142. }
  143. case WM_COMMAND:
  144. if (NULL != pThis)
  145. bResult = pThis->OnCommand(wParam, lParam);
  146. break;
  147. case WM_NOTIFY:
  148. bResult = pThis->OnNotify(wParam, lParam);
  149. break;
  150. case WM_DESTROY:
  151. pThis->OnDestroy();
  152. break;
  153. }
  154. return bResult;
  155. }
  156. //
  157. // WM_INITDIALOG handler.
  158. //
  159. BOOL
  160. CStatusDlg::OnInitDialog(
  161. WPARAM wParam,
  162. LPARAM lParam
  163. )
  164. {
  165. TCHAR szScratch[MAX_PATH];
  166. BOOL bResult = TRUE;
  167. RECT rcExpanded;
  168. CConfig& config = CConfig::GetSingleton();
  169. m_hwndLV = GetDlgItem(m_hwndDlg, IDC_LV_STATUSDLG);
  170. //
  171. // Center the dialog on the desktop before contraction.
  172. //
  173. CenterWindow(m_hwndDlg, GetDesktopWindow());
  174. //
  175. // Start with the dialog not expanded.
  176. //
  177. GetWindowRect(m_hwndDlg, &rcExpanded);
  178. m_cyExpanded = rcExpanded.bottom - rcExpanded.top;
  179. //
  180. // Set the cached "expanded" member to be the opposite of the user's
  181. // preference for expansion. ExpandDialog will only change the
  182. // expanded state if it's different from the current state.
  183. //
  184. m_bExpanded = !UserLikesDialogExpanded();
  185. ExpandDialog(!m_bExpanded);
  186. //
  187. // Disable buttons as necessary.
  188. //
  189. if (config.NoCacheViewer())
  190. EnableWindow(GetDlgItem(m_hwndDlg, IDC_BTN_VIEWFILES), FALSE);
  191. if (config.NoConfigCache())
  192. EnableWindow(GetDlgItem(m_hwndDlg, IDC_BTN_SETTINGS), FALSE);
  193. //
  194. // Initialize the message text.
  195. //
  196. SetWindowText(GetDlgItem(m_hwndDlg, IDC_TXT_STATUSDLG), m_pszText ? m_pszText : TEXT(""));
  197. //
  198. // Turn on checkboxes for column 0.
  199. //
  200. EnableListviewCheckboxes(true);
  201. //
  202. // Create the imagelist.
  203. //
  204. m_himl = CreateImageList();
  205. if (NULL != m_himl)
  206. ListView_SetImageList(m_hwndLV, m_himl, LVSIL_SMALL);
  207. //
  208. // Create the listview columns.
  209. //
  210. CreateListColumns();
  211. //
  212. // Fill the listview.
  213. //
  214. FillListView();
  215. if (MODE_AUTOSYNC == m_mode)
  216. {
  217. //
  218. // The dialog is being invoked for it's synchronize function only.
  219. // The dialog will not be displayed but we'll invoke the synchronize
  220. // function just as if it had been displayed. This feature is used
  221. // by the systray context menu to ensure we get the same synchronize
  222. // behavior if the action is invoked through either the dialog or
  223. // the systray context menu.
  224. //
  225. PostMessage(m_hwndDlg, WM_COMMAND, IDOK, 0);
  226. }
  227. else
  228. {
  229. //
  230. // Since we're a child of the hidden systray window we need to force ourselves
  231. // to the forground.
  232. //
  233. SetForegroundWindow(m_hwndDlg);
  234. }
  235. return bResult;
  236. }
  237. //
  238. // WM_DESTROY handler.
  239. //
  240. BOOL
  241. CStatusDlg::OnDestroy(
  242. void
  243. )
  244. {
  245. RememberUsersDialogSizePref(m_bExpanded);
  246. DestroyLVEntries();
  247. //
  248. // Destroy the CStatusDlg object
  249. //
  250. delete this;
  251. //
  252. // Image list is automatically destroyed by the listview in comctl32.
  253. //
  254. return FALSE;
  255. }
  256. //
  257. // WM_COMMAND handler.
  258. //
  259. BOOL
  260. CStatusDlg::OnCommand(
  261. WPARAM wParam,
  262. LPARAM lParam
  263. )
  264. {
  265. BOOL bResult = TRUE;
  266. switch(LOWORD(wParam))
  267. {
  268. case IDOK:
  269. SynchronizeServers();
  270. // Fall through and destroy the dialog
  271. case IDCANCEL:
  272. case IDCLOSE:
  273. Destroy();
  274. break;
  275. case IDC_BTN_VIEWFILES:
  276. COfflineFilesFolder::Open();
  277. break;
  278. case IDC_BTN_SETTINGS:
  279. COfflineFilesSheet::CreateAndRun(g_hInstance, GetDesktopWindow(), &g_cRefCount);
  280. break;
  281. case IDC_BTN_DETAILS:
  282. ExpandDialog(!m_bExpanded);
  283. break;
  284. default:
  285. bResult = FALSE;
  286. break;
  287. }
  288. return bResult;
  289. }
  290. //
  291. // WM_NOTIFY handler.
  292. //
  293. BOOL
  294. CStatusDlg::OnNotify(
  295. WPARAM wParam,
  296. LPARAM lParam
  297. )
  298. {
  299. BOOL bResult = TRUE;
  300. //int idCtl = int(wParam);
  301. LPNMHDR pnm = (LPNMHDR)lParam;
  302. switch(pnm->code)
  303. {
  304. case LVN_GETDISPINFO:
  305. OnLVN_GetDispInfo((LV_DISPINFO *)lParam);
  306. break;
  307. case LVN_COLUMNCLICK:
  308. OnLVN_ColumnClick((NM_LISTVIEW *)lParam);
  309. break;
  310. default:
  311. bResult = FALSE;
  312. break;
  313. }
  314. return bResult;
  315. }
  316. //
  317. // LVN_GETDISPINFO handler.
  318. //
  319. void
  320. CStatusDlg::OnLVN_GetDispInfo(
  321. LV_DISPINFO *plvdi
  322. )
  323. {
  324. LVEntry *pEntry = (LVEntry *)plvdi->item.lParam;
  325. if (LVIF_TEXT & plvdi->item.mask)
  326. {
  327. static TCHAR szText[MAX_PATH];
  328. szText[0] = TEXT('\0');
  329. switch(plvdi->item.iSubItem)
  330. {
  331. case iLVSUBITEM_SERVER:
  332. lstrcpyn(szText, pEntry->Server(), ARRAYSIZE(szText));
  333. break;
  334. case iLVSUBITEM_STATUS:
  335. pEntry->GetStatusText(szText, ARRAYSIZE(szText));
  336. break;
  337. case iLVSUBITEM_INFO:
  338. pEntry->GetInfoText(szText, ARRAYSIZE(szText));
  339. break;
  340. }
  341. plvdi->item.pszText = szText;
  342. }
  343. if (LVIF_IMAGE & plvdi->item.mask)
  344. {
  345. plvdi->item.iImage = pEntry->GetImageIndex();
  346. }
  347. }
  348. //
  349. // LVN_COLUMNCLICK handler.
  350. //
  351. void
  352. CStatusDlg::OnLVN_ColumnClick(
  353. NM_LISTVIEW *pnmlv
  354. )
  355. {
  356. if (m_iLastColSorted != pnmlv->iSubItem)
  357. {
  358. m_bSortAscending = true;
  359. m_iLastColSorted = pnmlv->iSubItem;
  360. }
  361. else
  362. {
  363. m_bSortAscending = !m_bSortAscending;
  364. }
  365. ListView_SortItems(m_hwndLV, CompareLVItems, LPARAM(this));
  366. }
  367. //
  368. // Create the server listview columns.
  369. //
  370. void
  371. CStatusDlg::CreateListColumns(
  372. void
  373. )
  374. {
  375. //
  376. // Clear out the listview and header.
  377. //
  378. ListView_DeleteAllItems(m_hwndLV);
  379. HWND hwndHeader = ListView_GetHeader(m_hwndLV);
  380. if (NULL != hwndHeader)
  381. {
  382. while(0 < Header_GetItemCount(hwndHeader))
  383. ListView_DeleteColumn(m_hwndLV, 0);
  384. }
  385. //
  386. // Create the header titles.
  387. //
  388. TCHAR szServer[80] = {0};
  389. TCHAR szStatus[80] = {0};
  390. TCHAR szInfo[80] = {0};
  391. LoadString(m_hInstance, IDS_STATUSDLG_HDR_SERVER, szServer, ARRAYSIZE(szServer));
  392. LoadString(m_hInstance, IDS_STATUSDLG_HDR_STATUS, szStatus, ARRAYSIZE(szStatus));
  393. LoadString(m_hInstance, IDS_STATUSDLG_HDR_INFO, szInfo, ARRAYSIZE(szInfo));
  394. RECT rcList;
  395. GetClientRect(m_hwndLV, &rcList);
  396. int cxLV = rcList.right - rcList.left;
  397. #define LVCOLMASK (LVCF_FMT | LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM)
  398. LV_COLUMN rgCols[] = {
  399. { LVCOLMASK, LVCFMT_LEFT, cxLV/4, szServer, 0, iLVSUBITEM_SERVER },
  400. { LVCOLMASK, LVCFMT_LEFT, cxLV/4, szStatus, 0, iLVSUBITEM_STATUS },
  401. { LVCOLMASK, LVCFMT_LEFT, cxLV/2, szInfo, 0, iLVSUBITEM_INFO }
  402. };
  403. //
  404. // Add the columns to the listview.
  405. //
  406. for (INT i = 0; i < ARRAYSIZE(rgCols); i++)
  407. {
  408. ListView_InsertColumn(m_hwndLV, i, &rgCols[i]);
  409. }
  410. }
  411. //
  412. // Populate the listview.
  413. //
  414. void
  415. CStatusDlg::FillListView(
  416. void
  417. )
  418. {
  419. DWORD dwStatus;
  420. DWORD dwPinCount;
  421. DWORD dwHintFlags;
  422. FILETIME ft;
  423. WIN32_FIND_DATA fd;
  424. CCscFindHandle hFind = CacheFindFirst(NULL, &fd, &dwStatus, &dwPinCount, &dwHintFlags, &ft);
  425. if (hFind.IsValid())
  426. {
  427. LPTSTR pszServer = fd.cFileName;
  428. LPTSTR pszEnd;
  429. LVEntry *pEntry;
  430. CSCSHARESTATS stats;
  431. do
  432. {
  433. //
  434. // Exclude the following:
  435. // 1. Directories.
  436. // 2. Files marked as "locally deleted".
  437. //
  438. // NOTE: The filtering done by this function must be the same as
  439. // in several other places throughout the CSCUI code.
  440. // To locate these, search the source for the comment
  441. // string CSCUI_ITEM_FILTER.
  442. //
  443. const DWORD fExclude = SSEF_LOCAL_DELETED |
  444. SSEF_DIRECTORY;
  445. CSCGETSTATSINFO si = { fExclude,
  446. SSUF_NONE,
  447. false, // No access info reqd (faster).
  448. false };
  449. if (_GetShareStatisticsForUser(fd.cFileName, &si, &stats) &&
  450. (0 < stats.cTotal || stats.bOffline))
  451. {
  452. bool bReplacedBackslash = false;
  453. //
  454. // Extract the server name from the share name returned by CSC.
  455. //
  456. while(*pszServer && TEXT('\\') == *pszServer)
  457. pszServer++;
  458. pszEnd = pszServer;
  459. while(*pszEnd && TEXT('\\') != *pszEnd)
  460. pszEnd++;
  461. if (TEXT('\\') == *pszEnd)
  462. {
  463. *pszEnd = TEXT('\0');
  464. bReplacedBackslash = true;
  465. }
  466. //
  467. // Find an existing server entry. If none found, create a new one.
  468. //
  469. if (NULL == (pEntry = FindLVEntry(pszServer)))
  470. {
  471. bool bConnectable = boolify(SendToSystray(CSCWM_ISSERVERBACK, 0, (LPARAM)fd.cFileName));
  472. pEntry = CreateLVEntry(pszServer, bConnectable);
  473. }
  474. if (NULL != pEntry)
  475. {
  476. if (bReplacedBackslash)
  477. *pszEnd = TEXT('\\');
  478. //
  479. // If we're running in "normal" mode, we
  480. // can't trust the share's "modified offline" bit.
  481. // Use the info we got by scanning the cache.
  482. // If we're running in "autosync" mode, we can just
  483. // use the share's "modified offline" indicator.
  484. // If something is truly modified offline, the bit
  485. // will be set.
  486. //
  487. if (MODE_NORMAL == m_mode)
  488. {
  489. dwStatus &= ~FLAG_CSC_SHARE_STATUS_MODIFIED_OFFLINE;
  490. if (0 < stats.cModified)
  491. dwStatus |= FLAG_CSC_SHARE_STATUS_MODIFIED_OFFLINE;
  492. }
  493. //
  494. // Add this share and it's statistics to the
  495. // server's list entry.
  496. //
  497. pEntry->AddShare(fd.cFileName, stats, dwStatus);
  498. }
  499. }
  500. }
  501. while(CacheFindNext(hFind, &fd, &dwStatus, &dwPinCount, &dwHintFlags, &ft));
  502. //
  503. // Remove those servers that the user won't be interested in.
  504. // Also place a checkmark next to those servers that are available
  505. // for reconnection.
  506. //
  507. PrepListForDisplay();
  508. }
  509. }
  510. //
  511. // Build the image list used by the server listview.
  512. //
  513. HIMAGELIST
  514. CStatusDlg::CreateImageList(
  515. void
  516. )
  517. {
  518. HIMAGELIST himl = NULL;
  519. //
  520. // Note: The order of these icon ID's in this array must match with the
  521. // iIMAGELIST_ICON_XXXXX enumeration.
  522. // The enum values represent the image indices in the image list.
  523. //
  524. static const struct IconDef
  525. {
  526. LPTSTR szName;
  527. HINSTANCE hInstance;
  528. } rgIcons[] = {
  529. { MAKEINTRESOURCE(IDI_SERVER), m_hInstance },
  530. { MAKEINTRESOURCE(IDI_SERVER_OFFLINE), m_hInstance },
  531. { MAKEINTRESOURCE(IDI_CSCINFORMATION), m_hInstance },
  532. { MAKEINTRESOURCE(IDI_CSCWARNING), m_hInstance }
  533. };
  534. //
  535. // Create the image lists for the listview.
  536. //
  537. int cxIcon = GetSystemMetrics(SM_CXSMICON);
  538. int cyIcon = GetSystemMetrics(SM_CYSMICON);
  539. himl = ImageList_Create(cxIcon,
  540. cyIcon,
  541. ILC_MASK,
  542. ARRAYSIZE(rgIcons),
  543. 10);
  544. if (NULL != himl)
  545. {
  546. for (UINT i = 0; i < ARRAYSIZE(rgIcons); i++)
  547. {
  548. HICON hIcon = (HICON)LoadImage(rgIcons[i].hInstance,
  549. rgIcons[i].szName,
  550. IMAGE_ICON,
  551. cxIcon,
  552. cyIcon,
  553. 0);
  554. if (NULL != hIcon)
  555. {
  556. ImageList_AddIcon(himl, hIcon);
  557. DestroyIcon(hIcon);
  558. }
  559. }
  560. ImageList_SetBkColor(himl, CLR_NONE); // Transparent background.
  561. }
  562. return himl;
  563. }
  564. void
  565. CStatusDlg::EnableListviewCheckboxes(
  566. bool bEnable
  567. )
  568. {
  569. DWORD dwStyle = ListView_GetExtendedListViewStyle(m_hwndLV);
  570. DWORD dwNewStyle = bEnable ? (dwStyle | LVS_EX_CHECKBOXES) :
  571. (dwStyle & ~LVS_EX_CHECKBOXES);
  572. ListView_SetExtendedListViewStyle(m_hwndLV, dwNewStyle);
  573. }
  574. //
  575. // The "Details" button changes it's title depending on
  576. // the dialog state (expanded or not).
  577. //
  578. void
  579. CStatusDlg::UpdateDetailsBtnTitle(
  580. void
  581. )
  582. {
  583. TCHAR szBtnTitle[80];
  584. int idsBtnTitle = IDS_OPENDETAILS;
  585. if (m_bExpanded)
  586. idsBtnTitle = IDS_CLOSEDETAILS;
  587. LoadString(m_hInstance, idsBtnTitle, szBtnTitle, ARRAYSIZE(szBtnTitle));
  588. SetWindowText(GetDlgItem(m_hwndDlg, IDC_BTN_DETAILS), szBtnTitle);
  589. }
  590. //
  591. // Expand or contract the dialog vertically.
  592. // When expanded, the server listview is made visible along
  593. // with the "Settings..." and "View Files..." buttons.
  594. //
  595. void
  596. CStatusDlg::ExpandDialog(
  597. bool bExpand
  598. )
  599. {
  600. if (bExpand != m_bExpanded)
  601. {
  602. CConfig& config = CConfig::GetSingleton();
  603. //
  604. // Table describing enable/disable state of controls in the lower part
  605. // of the dialog that are displayed when the dialog is expanded.
  606. //
  607. struct
  608. {
  609. UINT idCtl;
  610. bool bEnable;
  611. } rgidExpanded[] = { { IDC_LV_STATUSDLG, bExpand },
  612. { IDC_BTN_SETTINGS, bExpand && !config.NoConfigCache() },
  613. { IDC_BTN_VIEWFILES, bExpand && !config.NoCacheViewer() }
  614. };
  615. RECT rcDlg;
  616. GetWindowRect(m_hwndDlg, &rcDlg);
  617. if (!bExpand)
  618. {
  619. //
  620. // Closing details.
  621. //
  622. RECT rcSep;
  623. GetWindowRect(GetDlgItem(m_hwndDlg, IDC_SEP_STATUSDLG), &rcSep);
  624. rcDlg.bottom = rcSep.top;
  625. }
  626. else
  627. {
  628. //
  629. // Opening details.
  630. //
  631. rcDlg.bottom = rcDlg.top + m_cyExpanded;
  632. }
  633. //
  634. // If the dialog is not expanded, we want to disable all of the
  635. // "tabbable" items in the hidden part so they don't participate
  636. // in the dialog's tab order. Note that the "Settings" and
  637. // "View Files" buttons also have a policy setting involved in
  638. // the enabling logic.
  639. //
  640. for (int i = 0; i < ARRAYSIZE(rgidExpanded); i++)
  641. {
  642. EnableWindow(GetDlgItem(m_hwndDlg, rgidExpanded[i].idCtl), rgidExpanded[i].bEnable);
  643. }
  644. SetWindowPos(m_hwndDlg,
  645. NULL,
  646. rcDlg.left,
  647. rcDlg.top,
  648. rcDlg.right - rcDlg.left,
  649. rcDlg.bottom - rcDlg.top,
  650. SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
  651. m_bExpanded = bExpand;
  652. UpdateDetailsBtnTitle();
  653. }
  654. }
  655. //
  656. // Queries the HKCU reg data to see if the user closed the dialog
  657. // expanded or not expanded last time the dialog was used.
  658. // Returns: true = expanded, false = not expanded.
  659. //
  660. bool
  661. CStatusDlg::UserLikesDialogExpanded(
  662. void
  663. )
  664. {
  665. DWORD dwExpanded = 0;
  666. HKEY hkey;
  667. DWORD dwStatus = RegOpenKeyEx(HKEY_CURRENT_USER,
  668. REGSTR_KEY_OFFLINEFILES,
  669. 0,
  670. KEY_QUERY_VALUE,
  671. &hkey);
  672. if (ERROR_SUCCESS == dwStatus)
  673. {
  674. DWORD dwType;
  675. DWORD cbData = sizeof(DWORD);
  676. RegQueryValueEx(hkey,
  677. REGSTR_VAL_EXPANDSTATUSDLG,
  678. NULL,
  679. &dwType,
  680. (LPBYTE)&dwExpanded,
  681. &cbData);
  682. RegCloseKey(hkey);
  683. }
  684. return !!dwExpanded;
  685. }
  686. //
  687. // Stores the current state of the status dialog in per-user
  688. // reg data. Used next time the dialog is opened so that if the
  689. // user likes the dialog expanded, it opens expanded.
  690. //
  691. void
  692. CStatusDlg::RememberUsersDialogSizePref(
  693. bool bExpanded
  694. )
  695. {
  696. HKEY hkey;
  697. DWORD dwStatus = RegOpenKeyEx(HKEY_CURRENT_USER,
  698. REGSTR_KEY_OFFLINEFILES,
  699. 0,
  700. KEY_SET_VALUE,
  701. &hkey);
  702. if (ERROR_SUCCESS == dwStatus)
  703. {
  704. DWORD cbData = sizeof(DWORD);
  705. DWORD dwExpanded = DWORD(bExpanded);
  706. RegSetValueEx(hkey,
  707. REGSTR_VAL_EXPANDSTATUSDLG,
  708. 0,
  709. REG_DWORD,
  710. (CONST BYTE *)&dwExpanded,
  711. cbData);
  712. RegCloseKey(hkey);
  713. }
  714. }
  715. //
  716. // Build a list of share names for synchronization and
  717. // reconnect.
  718. //
  719. HRESULT
  720. CStatusDlg::BuildFilenameList(
  721. CscFilenameList *pfnl
  722. )
  723. {
  724. LVEntry *pEntry;
  725. LVITEM item;
  726. int cEntries = ListView_GetItemCount(m_hwndLV);
  727. for (int i = 0; i < cEntries; i++)
  728. {
  729. if (ListView_GetCheckState(m_hwndLV, i))
  730. {
  731. //
  732. // Server has checkmark so we add it's
  733. // shares to the filename list.
  734. //
  735. item.mask = LVIF_PARAM;
  736. item.iItem = i;
  737. item.iSubItem = 0;
  738. if (ListView_GetItem(m_hwndLV, &item))
  739. {
  740. pEntry = (LVEntry *)item.lParam;
  741. int cShares = pEntry->GetShareCount();
  742. for (int iShare = 0; iShare < cShares; iShare++)
  743. {
  744. LPCTSTR pszShare = pEntry->GetShareName(iShare);
  745. if (NULL != pszShare)
  746. {
  747. if (!pfnl->AddFile(pszShare, TRUE))
  748. return E_OUTOFMEMORY;
  749. }
  750. }
  751. }
  752. }
  753. }
  754. return NOERROR;
  755. }
  756. //
  757. // Synchronize all of the checked servers from the listview and
  758. // reconnect them.
  759. //
  760. HRESULT
  761. CStatusDlg::SynchronizeServers(
  762. void
  763. )
  764. {
  765. HRESULT hr = NOERROR;
  766. const bool bSkipTheSync = BST_CHECKED == IsDlgButtonChecked(m_hwndDlg, IDC_CBX_STATUSDLG);
  767. if (::IsSyncInProgress())
  768. {
  769. CscMessageBox(m_hwndDlg,
  770. MB_OK | MB_ICONINFORMATION,
  771. m_hInstance,
  772. bSkipTheSync ? IDS_CANTRECONN_SYNCINPROGRESS : IDS_CANTSYNC_SYNCINPROGRESS);
  773. }
  774. else
  775. {
  776. //
  777. // First build the FilenameList containing shares to sync.
  778. //
  779. CscFilenameList fnl;
  780. hr = BuildFilenameList(&fnl);
  781. if (SUCCEEDED(hr))
  782. {
  783. if (bSkipTheSync)
  784. {
  785. //
  786. // User has checked "reconnect without sync" checkbox.
  787. // Therefore, we skip the sync and go straight to reconnect.
  788. //
  789. hr = ReconnectServers(&fnl, TRUE, FALSE);
  790. if (S_OK == hr)
  791. {
  792. PostToSystray(CSCWM_UPDATESTATUS, STWM_STATUSCHECK, 0);
  793. }
  794. }
  795. else
  796. {
  797. const DWORD dwUpdateFlags = CSC_UPDATE_STARTNOW |
  798. CSC_UPDATE_SELECTION |
  799. CSC_UPDATE_REINT |
  800. CSC_UPDATE_NOTIFY_DONE |
  801. CSC_UPDATE_SHOWUI_ALWAYS |
  802. CSC_UPDATE_RECONNECT;
  803. //
  804. // Syncing is an asynchronous operation involving
  805. // mobsync.exe. The code in CscUpdateCache will check for open
  806. // files and notify the user. When the sync is done, it will
  807. // transition everything in the file list to online mode.
  808. //
  809. hr = CscUpdateCache(dwUpdateFlags, &fnl);
  810. }
  811. }
  812. }
  813. return hr;
  814. }
  815. //
  816. // Create an entry for the listview.
  817. // Returns ptr to new entry on success. NULL on failure.
  818. //
  819. CStatusDlg::LVEntry *
  820. CStatusDlg::CreateLVEntry(
  821. LPCTSTR pszServer,
  822. bool bConnectable
  823. )
  824. {
  825. LVEntry *pEntry = new LVEntry(m_hInstance, pszServer, bConnectable);
  826. if (NULL != pEntry)
  827. {
  828. LVITEM item;
  829. item.mask = LVIF_PARAM | LVIF_TEXT | LVIF_IMAGE;
  830. item.lParam = (LPARAM)pEntry;
  831. item.iItem = ListView_GetItemCount(m_hwndLV);
  832. item.iSubItem = 0;
  833. item.pszText = LPSTR_TEXTCALLBACK;
  834. item.iImage = I_IMAGECALLBACK;
  835. if (-1 == ListView_InsertItem(m_hwndLV, &item))
  836. {
  837. delete pEntry;
  838. pEntry = NULL;
  839. }
  840. }
  841. return pEntry;
  842. }
  843. //
  844. // Find an entry in the listview using the servername as the key.
  845. // Return ptr to entry on success. NULL on failure.
  846. //
  847. CStatusDlg::LVEntry *
  848. CStatusDlg::FindLVEntry(
  849. LPCTSTR pszServer
  850. )
  851. {
  852. LVEntry *pEntry = NULL;
  853. LVITEM item;
  854. int cEntries = ListView_GetItemCount(m_hwndLV);
  855. for (int i = 0; i < cEntries; i++)
  856. {
  857. item.mask = LVIF_PARAM;
  858. item.iItem = i;
  859. item.iSubItem = 0;
  860. if (ListView_GetItem(m_hwndLV, &item))
  861. {
  862. pEntry = (LVEntry *)item.lParam;
  863. //
  864. // This comparison must be case-INSENSITIVE. Entries
  865. // in the CSC database are on a "\\server\share" basis and
  866. // are at the mercy of what was passed in through the CSC APIs.
  867. // Therefore, the database can contain "\\Foo\bar" and
  868. // "\\foo\bar2". We must treat "Foo" and "foo" as the single
  869. // server they represent.
  870. //
  871. if (0 == lstrcmpi(pEntry->Server(), pszServer))
  872. break;
  873. pEntry = NULL;
  874. }
  875. }
  876. return pEntry;
  877. }
  878. //
  879. // Clear out the listview. Ensures all listview item objects
  880. // are destroyed.
  881. //
  882. void
  883. CStatusDlg::DestroyLVEntries(
  884. void
  885. )
  886. {
  887. LVITEM item;
  888. int cEntries = ListView_GetItemCount(m_hwndLV);
  889. for (int i = 0; i < cEntries; i++)
  890. {
  891. item.mask = LVIF_PARAM;
  892. item.iItem = i;
  893. item.iSubItem = 0;
  894. if (ListView_GetItem(m_hwndLV, &item))
  895. {
  896. delete (LVEntry *)item.lParam;
  897. if (ListView_DeleteItem(m_hwndLV, i))
  898. {
  899. i--;
  900. cEntries--;
  901. }
  902. }
  903. }
  904. }
  905. //
  906. // Determine if a listview entry should remain visible in the listview.
  907. // Currently we include servers that currently connected through the
  908. // network redirector and are offline OR those that are dirty.
  909. //
  910. bool
  911. CStatusDlg::ShouldIncludeLVEntry(
  912. const CStatusDlg::LVEntry& entry
  913. )
  914. {
  915. DWORD dwCscStatus;
  916. entry.GetStats(NULL, &dwCscStatus);
  917. return (FLAG_CSC_SHARE_STATUS_DISCONNECTED_OP & dwCscStatus) ||
  918. (FLAG_CSC_SHARE_STATUS_MODIFIED_OFFLINE & dwCscStatus);
  919. }
  920. //
  921. // Determine if a checkmark should be placed next to an item in
  922. // the listview.
  923. //
  924. bool
  925. CStatusDlg::ShouldCheckLVEntry(
  926. const CStatusDlg::LVEntry& entry
  927. )
  928. {
  929. return true;
  930. }
  931. //
  932. // Remove all entries not to be displayed from the listview.
  933. // Initially we create LV entries for each server in the CSC cache.
  934. // After all servers have been entered and their statistics tallied,
  935. // we call PrepListForDisplay to remove the ones that the
  936. // user won't want to see.
  937. //
  938. void
  939. CStatusDlg::PrepListForDisplay(
  940. void
  941. )
  942. {
  943. LVEntry *pEntry;
  944. LVITEM item;
  945. int cEntries = ListView_GetItemCount(m_hwndLV);
  946. for (int i = 0; i < cEntries; i++)
  947. {
  948. item.mask = LVIF_PARAM;
  949. item.iItem = i;
  950. item.iSubItem = 0;
  951. if (ListView_GetItem(m_hwndLV, &item))
  952. {
  953. pEntry = (LVEntry *)item.lParam;
  954. if (ShouldIncludeLVEntry(*pEntry))
  955. {
  956. ListView_SetCheckState(m_hwndLV, i, ShouldCheckLVEntry(*pEntry));
  957. }
  958. else
  959. {
  960. delete pEntry;
  961. if (ListView_DeleteItem(m_hwndLV, i))
  962. {
  963. i--;
  964. cEntries--;
  965. }
  966. }
  967. }
  968. }
  969. }
  970. //
  971. // Listview item comparison callback.
  972. //
  973. int CALLBACK
  974. CStatusDlg::CompareLVItems(
  975. LPARAM lParam1,
  976. LPARAM lParam2,
  977. LPARAM lParamSort
  978. )
  979. {
  980. CStatusDlg *pdlg = reinterpret_cast<CStatusDlg *>(lParamSort);
  981. CStatusDlg::LVEntry *pEntry1 = reinterpret_cast<CStatusDlg::LVEntry *>(lParam1);
  982. CStatusDlg::LVEntry *pEntry2 = reinterpret_cast<CStatusDlg::LVEntry *>(lParam2);
  983. int diff = 0;
  984. TCHAR szText[2][MAX_PATH];
  985. //
  986. // This array controls the comparison column IDs used when
  987. // values for the selected column are equal. These should
  988. // remain in order of the iLVSUBITEM_xxxxx enumeration with
  989. // respect to the first element in each row.
  990. //
  991. static const int rgColComp[3][3] = {
  992. { iLVSUBITEM_SERVER, iLVSUBITEM_STATUS, iLVSUBITEM_INFO },
  993. { iLVSUBITEM_STATUS, iLVSUBITEM_SERVER, iLVSUBITEM_INFO },
  994. { iLVSUBITEM_INFO, iLVSUBITEM_SERVER, iLVSUBITEM_STATUS }
  995. };
  996. int iCompare = 0;
  997. while(0 == diff && iCompare < ARRAYSIZE(rgColComp))
  998. {
  999. switch(rgColComp[pdlg->m_iLastColSorted][iCompare++])
  1000. {
  1001. case iLVSUBITEM_SERVER:
  1002. lstrcpyn(szText[0], pEntry1->Server(), ARRAYSIZE(szText[0]));
  1003. lstrcpyn(szText[1], pEntry2->Server(), ARRAYSIZE(szText[1]));
  1004. break;
  1005. case iLVSUBITEM_STATUS:
  1006. pEntry1->GetStatusText(szText[0], ARRAYSIZE(szText[0]));
  1007. pEntry2->GetStatusText(szText[1], ARRAYSIZE(szText[1]));
  1008. break;
  1009. case iLVSUBITEM_INFO:
  1010. pEntry1->GetInfoText(szText[0], ARRAYSIZE(szText[0]));
  1011. pEntry2->GetInfoText(szText[1], ARRAYSIZE(szText[1]));
  1012. break;
  1013. default:
  1014. //
  1015. // If you hit this, you need to update this function
  1016. // to handle the new column you've added to the listview.
  1017. //
  1018. *szText[0] = TEXT('\0');
  1019. *szText[1] = TEXT('\0');
  1020. TraceAssert(false);
  1021. break;
  1022. }
  1023. //
  1024. // This comparison should be case-sensitive since it is controlling
  1025. // sort order of display columns.
  1026. //
  1027. diff = lstrcmp(szText[0], szText[1]);
  1028. }
  1029. return pdlg->m_bSortAscending ? diff : -1 * diff;
  1030. }
  1031. const TCHAR CStatusDlg::LVEntry::s_szBlank[] = TEXT("");
  1032. //
  1033. // There are 3 binary conditions that control the selection of the entry
  1034. // display information. Therefore we can use a simple 8-element map of string resource
  1035. // IDs and icon image list indices to determine the correct display information
  1036. // string for the corresponding LV entry state. GetDispInfoIndex() is called
  1037. // to retrieve the index for a particular LVEntry.
  1038. //
  1039. const CStatusDlg::LVEntry::DispInfo
  1040. CStatusDlg::LVEntry::s_rgDispInfo[] = { // online available modified
  1041. { IDS_SHARE_STATUS_OFFLINE, IDS_SHARE_INFO_UNAVAIL, CStatusDlg::iIMAGELIST_ICON_SERVER_OFFLINE }, // 0 0 0
  1042. { IDS_SHARE_STATUS_OFFLINE, IDS_SHARE_INFO_UNAVAIL_MOD, CStatusDlg::iIMAGELIST_ICON_SERVER_OFFLINE }, // 0 0 1
  1043. { IDS_SHARE_STATUS_OFFLINE, IDS_SHARE_INFO_AVAIL, CStatusDlg::iIMAGELIST_ICON_SERVER_BACK }, // 0 1 0
  1044. { IDS_SHARE_STATUS_OFFLINE, IDS_SHARE_INFO_AVAIL_MOD, CStatusDlg::iIMAGELIST_ICON_SERVER_BACK }, // 0 1 1
  1045. { IDS_SHARE_STATUS_ONLINE, IDS_SHARE_INFO_BLANK, CStatusDlg::iIMAGELIST_ICON_SERVER }, // 1 0 0
  1046. { IDS_SHARE_STATUS_ONLINE, IDS_SHARE_INFO_DIRTY, CStatusDlg::iIMAGELIST_ICON_SERVER_DIRTY }, // 1 0 1
  1047. { IDS_SHARE_STATUS_ONLINE, IDS_SHARE_INFO_BLANK, CStatusDlg::iIMAGELIST_ICON_SERVER }, // 1 1 0
  1048. { IDS_SHARE_STATUS_ONLINE, IDS_SHARE_INFO_DIRTY, CStatusDlg::iIMAGELIST_ICON_SERVER_DIRTY } // 1 1 1
  1049. };
  1050. CStatusDlg::LVEntry::LVEntry(
  1051. HINSTANCE hInstance,
  1052. LPCTSTR pszServer,
  1053. bool bConnectable
  1054. ) : m_hInstance(hInstance),
  1055. m_pszServer(StrDup(pszServer)),
  1056. m_dwCscStatus(0),
  1057. m_hdpaShares(DPA_Create(4)),
  1058. m_bConnectable(bConnectable),
  1059. m_iDispInfo(-1)
  1060. {
  1061. m_stats.cTotal = 0;
  1062. m_stats.cPinned = 0;
  1063. m_stats.cModified = 0;
  1064. m_stats.cSparse = 0;
  1065. if (NULL == m_pszServer)
  1066. m_pszServer = const_cast<LPTSTR>(s_szBlank);
  1067. }
  1068. CStatusDlg::LVEntry::~LVEntry(
  1069. void
  1070. )
  1071. {
  1072. if (s_szBlank != m_pszServer && NULL != m_pszServer)
  1073. {
  1074. LocalFree(m_pszServer);
  1075. }
  1076. if (NULL != m_hdpaShares)
  1077. {
  1078. int cShares = DPA_GetPtrCount(m_hdpaShares);
  1079. for (int i = 0; i < cShares; i++)
  1080. {
  1081. LPTSTR psz = (LPTSTR)DPA_GetPtr(m_hdpaShares, i);
  1082. if (NULL != psz)
  1083. {
  1084. LocalFree(psz);
  1085. }
  1086. }
  1087. DPA_Destroy(m_hdpaShares);
  1088. }
  1089. }
  1090. bool
  1091. CStatusDlg::LVEntry::AddShare(
  1092. LPCTSTR pszShare,
  1093. const CSCSHARESTATS& s,
  1094. DWORD dwCscStatus
  1095. )
  1096. {
  1097. bool bResult = false;
  1098. if (NULL != m_hdpaShares)
  1099. {
  1100. LPTSTR pszCopy = StrDup(pszShare);
  1101. if (NULL != pszCopy)
  1102. {
  1103. if (-1 != DPA_AppendPtr(m_hdpaShares, pszCopy))
  1104. {
  1105. m_stats.cTotal += s.cTotal;
  1106. m_stats.cPinned += s.cPinned;
  1107. m_stats.cModified += s.cModified;
  1108. m_stats.cSparse += s.cSparse;
  1109. m_dwCscStatus |= dwCscStatus;
  1110. bResult = true;
  1111. }
  1112. else
  1113. {
  1114. LocalFree(pszCopy);
  1115. }
  1116. }
  1117. }
  1118. return bResult;
  1119. }
  1120. int
  1121. CStatusDlg::LVEntry::GetShareCount(
  1122. void
  1123. ) const
  1124. {
  1125. if (NULL != m_hdpaShares)
  1126. return DPA_GetPtrCount(m_hdpaShares);
  1127. return 0;
  1128. }
  1129. LPCTSTR
  1130. CStatusDlg::LVEntry::GetShareName(
  1131. int iShare
  1132. ) const
  1133. {
  1134. if (NULL != m_hdpaShares)
  1135. return (LPCTSTR)DPA_GetPtr(m_hdpaShares, iShare);
  1136. return NULL;
  1137. }
  1138. void
  1139. CStatusDlg::LVEntry::GetStats(
  1140. CSCSHARESTATS *ps,
  1141. DWORD *pdwCscStatus
  1142. ) const
  1143. {
  1144. if (NULL != ps)
  1145. *ps = m_stats;
  1146. if (NULL != pdwCscStatus)
  1147. *pdwCscStatus = m_dwCscStatus;
  1148. }
  1149. //
  1150. // Determines the index into s_rgDispInfo[] for obtaining display information
  1151. // for the LV entry.
  1152. //
  1153. int
  1154. CStatusDlg::LVEntry::GetDispInfoIndex(
  1155. void
  1156. ) const
  1157. {
  1158. if (-1 == m_iDispInfo)
  1159. {
  1160. m_iDispInfo = 0;
  1161. if (IsModified())
  1162. m_iDispInfo |= DIF_MODIFIED;
  1163. if (!IsOffline())
  1164. m_iDispInfo |= DIF_ONLINE;
  1165. if (IsConnectable())
  1166. m_iDispInfo |= DIF_AVAILABLE;
  1167. }
  1168. return m_iDispInfo;
  1169. }
  1170. //
  1171. // Retrieve the entry's text for the "Status" column in the listview.
  1172. //
  1173. void
  1174. CStatusDlg::LVEntry::GetStatusText(
  1175. LPTSTR pszStatus,
  1176. int cchStatus
  1177. ) const
  1178. {
  1179. UINT idsStatusText = s_rgDispInfo[GetDispInfoIndex()].idsStatusText;
  1180. LoadString(m_hInstance, idsStatusText, pszStatus, cchStatus);
  1181. }
  1182. //
  1183. // Retrieve entry's text for the "Information" column in the listview.
  1184. //
  1185. void
  1186. CStatusDlg::LVEntry::GetInfoText(
  1187. LPTSTR pszInfo,
  1188. int cchInfo
  1189. ) const
  1190. {
  1191. int iInfoText = GetDispInfoIndex();
  1192. int idsInfoText = s_rgDispInfo[iInfoText].idsInfoText;
  1193. if (iInfoText & DIF_MODIFIED)
  1194. {
  1195. //
  1196. // Info text has embedded modified file count.
  1197. // Requires a little more work.
  1198. //
  1199. TCHAR szTemp[MAX_PATH];
  1200. INT_PTR rgcModified[] = { m_stats.cModified };
  1201. LoadString(m_hInstance, idsInfoText, szTemp, ARRAYSIZE(szTemp));
  1202. FormatMessage(FORMAT_MESSAGE_FROM_STRING |
  1203. FORMAT_MESSAGE_ARGUMENT_ARRAY,
  1204. szTemp,
  1205. 0,
  1206. 0,
  1207. pszInfo,
  1208. cchInfo,
  1209. (va_list *)rgcModified);
  1210. }
  1211. else
  1212. {
  1213. LoadString(m_hInstance, idsInfoText, pszInfo, cchInfo);
  1214. }
  1215. }
  1216. //
  1217. // Retrieve entry's imagelist index for the image displayed next to the
  1218. // entry in the listview.
  1219. //
  1220. int
  1221. CStatusDlg::LVEntry::GetImageIndex(
  1222. void
  1223. ) const
  1224. {
  1225. return s_rgDispInfo[GetDispInfoIndex()].iImage;
  1226. }
  1227. //
  1228. // Wrapper for CSCTransitionServerOnline
  1229. //
  1230. //
  1231. // CAUTION!
  1232. //
  1233. // TransitionShareOnline is called from ReconnectServers (below) which
  1234. // means we may be running in either explorer or mobsync. It's also
  1235. // called directly from the systray code when auto-reconnecting a
  1236. // server.
  1237. //
  1238. // 1. Be careful with SendMessage, since it may go out of process. Note that
  1239. // STDBGOUT uses SendMessage.
  1240. // 2. Be careful not to do anything that could cause a transition to offline,
  1241. // which results in a SendMessage from WinLogon, which deadlocks if we are
  1242. // running on the systray thread (the recipient of the SendMessage).
  1243. // For example, use SHSimpleIDListFromFindData instead of ILCreateFromPath.
  1244. //
  1245. BOOL
  1246. TransitionShareOnline(LPCTSTR pszShare,
  1247. BOOL bShareIsAlive, // TRUE skips CheckShareOnline
  1248. BOOL bCheckSpeed, // FALSE skips slow link check
  1249. DWORD dwPathSpeed) // Used if (bShareIsAlive && bCheckSpeed)
  1250. {
  1251. BOOL bShareTransitioned = FALSE;
  1252. DWORD dwShareStatus;
  1253. if (!pszShare || !*pszShare)
  1254. return FALSE;
  1255. //
  1256. // Protect against calling CSCCheckShareOnline & CSCTransitionServerOnline
  1257. // for shares that are already online. In particular, CSCCheckShareOnline
  1258. // establishes a net connection so it can be slow.
  1259. //
  1260. // This also means that we call CSCTransitionServerOnline for only one
  1261. // share on a given server, since all shares transition online/offline
  1262. // at the same time.
  1263. //
  1264. if (CSCQueryFileStatus(pszShare, &dwShareStatus, NULL, NULL)
  1265. && (FLAG_CSC_SHARE_STATUS_DISCONNECTED_OP & dwShareStatus))
  1266. {
  1267. //
  1268. // Only try to transition the server online if the share is
  1269. // truly available on the net. Otherwise we'll put the server online
  1270. // and the next call for net resources on that server will cause a net
  1271. // timeout.
  1272. //
  1273. if (!bShareIsAlive)
  1274. {
  1275. bShareIsAlive = CSCCheckShareOnlineEx(pszShare, &dwPathSpeed);
  1276. if (!bShareIsAlive)
  1277. {
  1278. DWORD dwErr = GetLastError();
  1279. if (ERROR_ACCESS_DENIED == dwErr ||
  1280. ERROR_LOGON_FAILURE == dwErr)
  1281. {
  1282. // The share is reachable, but we don't have valid
  1283. // credentials. We don't have a valid path speed,
  1284. // so the best we can do is assume fast link.
  1285. //
  1286. // Currently, this function is always called with
  1287. // bShareIsAlive and bCheckSpeed both TRUE or both FALSE
  1288. // so it doesn't make any difference. Could change in
  1289. // the future, though.
  1290. //
  1291. bShareIsAlive = TRUE;
  1292. bCheckSpeed = FALSE;
  1293. }
  1294. }
  1295. }
  1296. if (bShareIsAlive)
  1297. {
  1298. //
  1299. // Also, for auto-reconnection, we only transition if not on a
  1300. // slow link.
  1301. //
  1302. if (!bCheckSpeed || !_PathIsSlow(dwPathSpeed))
  1303. {
  1304. //
  1305. // Transition to online
  1306. //
  1307. STDBGOUT((2, TEXT("Transitioning server \"%s\" to online"), pszShare));
  1308. if (CSCTransitionServerOnline(pszShare))
  1309. {
  1310. STDBGOUT((1, TEXT("Server \"%s\" reconnected."), pszShare));
  1311. LPTSTR pszTemp;
  1312. if (LocalAllocString(&pszTemp, pszShare))
  1313. {
  1314. //
  1315. // Strip the path to only the "\\server" part.
  1316. //
  1317. PathStripToRoot(pszTemp);
  1318. PathRemoveFileSpec(pszTemp);
  1319. SendCopyDataToSystray(PWM_REFRESH_SHELL, StringByteSize(pszTemp), pszTemp);
  1320. LocalFreeString(&pszTemp);
  1321. }
  1322. bShareTransitioned = TRUE;
  1323. }
  1324. else
  1325. {
  1326. STDBGOUT((1, TEXT("Error %d reconnecting \"%s\""), GetLastError(), pszShare));
  1327. }
  1328. }
  1329. else
  1330. {
  1331. STDBGOUT((1, TEXT("Path to \"%s\" is SLOW. No reconnect."), pszShare));
  1332. }
  1333. }
  1334. else
  1335. {
  1336. STDBGOUT((1, TEXT("Unable to connect to \"%s\". No reconnect."), pszShare));
  1337. }
  1338. }
  1339. return bShareTransitioned;
  1340. }
  1341. //
  1342. // CAUTION!
  1343. //
  1344. // ReconnectServers is called from both the Status Dialog (explorer) and
  1345. // the sync update handler (mobsync.exe). Any communication with systray
  1346. // must be done in a process-safe manner.
  1347. //
  1348. // See comments for TransitionShareOnline above.
  1349. //
  1350. HRESULT
  1351. ReconnectServers(CscFilenameList *pfnl,
  1352. BOOL bCheckForOpenFiles,
  1353. BOOL bCheckSpeed) // FALSE skips slow link check
  1354. {
  1355. if (pfnl)
  1356. {
  1357. CscFilenameList::ShareIter si = pfnl->CreateShareIterator();
  1358. CscFilenameList::HSHARE hShare;
  1359. BOOL bRefreshShell = FALSE;
  1360. if (bCheckForOpenFiles)
  1361. {
  1362. //
  1363. // First scan the shares to see if any have open files.
  1364. //
  1365. while(si.Next(&hShare))
  1366. {
  1367. DWORD dwShareStatus;
  1368. if (CSCQueryFileStatus(pfnl->GetShareName(hShare), &dwShareStatus, NULL, NULL))
  1369. {
  1370. if ((FLAG_CSC_SHARE_STATUS_FILES_OPEN & dwShareStatus) &&
  1371. (FLAG_CSC_SHARE_STATUS_DISCONNECTED_OP & dwShareStatus))
  1372. {
  1373. if (IDOK != OpenFilesWarningDialog())
  1374. {
  1375. return S_FALSE; // User cancelled.
  1376. }
  1377. break;
  1378. }
  1379. }
  1380. }
  1381. si.Reset();
  1382. }
  1383. //
  1384. // Walk through the list, transitioning everything to online.
  1385. //
  1386. while(si.Next(&hShare))
  1387. {
  1388. if (TransitionShareOnline(pfnl->GetShareName(hShare), FALSE, bCheckSpeed, 0))
  1389. bRefreshShell = TRUE;
  1390. }
  1391. }
  1392. return S_OK;
  1393. }