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.

5241 lines
152 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (C) Microsoft Corporation, 1997 - 1999
  6. //
  7. // File: options.cpp
  8. //
  9. //--------------------------------------------------------------------------
  10. //////////////////////////////////////////////////////////////////////////////
  11. /* File: options.cpp
  12. Description: Displays a property-sheet-like dialog containing
  13. optional settings for CSC.
  14. Classes:
  15. COfflineFilesPage - Contains basic CSC settings. Designed
  16. to be dynamically added to the shell's View->Folder Options
  17. property sheet.
  18. CustomGOAAddDlg - Dialog for adding custom go-offline actions to
  19. the "advanced" dialog.
  20. CustomGOAEditDlg - Dialog for editing custom go-offline actions
  21. in the "advanced" dialog.
  22. CscOptPropSheetExt - Shell property sheet extension object for
  23. adding the COfflineFilesPage to the shell's View->Folder Options
  24. property sheet.
  25. Revision History:
  26. Date Description Programmer
  27. -------- --------------------------------------------------- ----------
  28. 12/03/97 Initial creation. BrianAu
  29. 05/28/97 Removed CscOptPropSheet class. Obsolete. BrianAu
  30. Renamed AdvancedPage to CAdvOptDlg. This better
  31. reflects the new behavior of the "advanced" dlg
  32. as a dialog rather than a property page as first
  33. designed.
  34. 07/29/98 Removed CscOptPropPage class. Now we only have BrianAu
  35. a single prop page so there was no reason for
  36. a common base class implementation. All base
  37. class functionality has been moved up into the
  38. COfflineFilesPage class.
  39. Renamed "GeneralPage" class to "COfflineFilesPage"
  40. to reflect the current naming in the UI.
  41. 08/21/98 Added PurgeCache and PurgeCacheCallback. BrianAu
  42. 08/27/98 Options dialog re-layout per PM changes. BrianAu
  43. - Replaced part/full sync radio buttons with cbx.
  44. - Added reminder balloon controls.
  45. 03/30/00 Added support for cache encryption. BrianAu
  46. */
  47. ///////////////////////////////////////////////////////////////////////////////
  48. #include "pch.h"
  49. #pragma hdrstop
  50. #include <math.h>
  51. #include <prsht.h>
  52. #include <resource.h>
  53. #include <winnetwk.h>
  54. #include <shlguidp.h>
  55. #include <process.h>
  56. #include <systrayp.h> // STWM_ENABLESERVICE, etc.
  57. #include <mobsyncp.h>
  58. #include <htmlhelp.h>
  59. #include "options.h"
  60. #include "ccinline.h"
  61. #include "msgbox.h"
  62. #include "registry.h"
  63. #include "filesize.h"
  64. #include "uuid.h"
  65. #include "config.h"
  66. #include "osver.h"
  67. #include "uihelp.h"
  68. #include "cscst.h" // For PWM_SETREMINDERTIMER
  69. #include "util.h" // Utils from "dll" directory.
  70. #include "folder.h"
  71. #include "purge.h"
  72. #include "security.h"
  73. #include "syncmgr.h"
  74. #include "strings.h"
  75. #include "termserv.h"
  76. //
  77. // Simple inline helper. Why this isn't this a Win32 macro?
  78. //
  79. inline void EnableDialogItem(HWND hwnd, UINT idCtl, bool bEnable)
  80. {
  81. EnableWindow(GetDlgItem(hwnd, idCtl), bEnable);
  82. }
  83. //
  84. // This is for assisting the context help functions.
  85. // Determine if the control has it's help text in windows.hlp or
  86. // in our cscui.hlp.
  87. //
  88. bool UseWindowsHelp(int idCtl)
  89. {
  90. bool bUseWindowsHelp = false;
  91. switch(idCtl)
  92. {
  93. case IDOK:
  94. case IDCANCEL:
  95. case IDC_STATIC:
  96. bUseWindowsHelp = true;
  97. break;
  98. default:
  99. break;
  100. }
  101. return bUseWindowsHelp;
  102. }
  103. //-----------------------------------------------------------------------------
  104. // COfflineFilesPage
  105. //-----------------------------------------------------------------------------
  106. const DWORD COfflineFilesPage::m_rgHelpIDs[] = {
  107. IDC_CBX_ENABLE_CSC, HIDC_CBX_ENABLE_CSC,
  108. IDC_CBX_FULLSYNC_AT_LOGON, HIDC_CBX_FULLSYNC_AT_LOGON,
  109. IDC_CBX_FULLSYNC_AT_LOGOFF, HIDC_CBX_FULLSYNC_AT_LOGOFF,
  110. IDC_CBX_LINK_ON_DESKTOP, HIDC_CBX_LINK_ON_DESKTOP,
  111. IDC_CBX_ENCRYPT_CSC, HIDC_CBX_ENCRYPT_CSC,
  112. IDC_CBX_REMINDERS, HIDC_REMINDERS_ENABLE,
  113. IDC_SPIN_REMINDERS, HIDC_REMINDERS_PERIOD,
  114. IDC_TXT_REMINDERS1, DWORD(-1), // "minutes."
  115. IDC_LBL_CACHESIZE_PCT, DWORD(-1),
  116. IDC_SLIDER_CACHESIZE_PCT, HIDC_CACHESIZE_PCT,
  117. IDC_TXT_CACHESIZE_PCT, DWORD(-1),
  118. IDC_BTN_DELETE_CACHE, HIDC_BTN_DELETE_CACHE,
  119. IDC_BTN_VIEW_CACHE, HIDC_BTN_VIEW_CACHE,
  120. IDC_BTN_ADVANCED, HIDC_BTN_ADVANCED,
  121. IDC_STATIC2, DWORD(-1), // Icon
  122. IDC_STATIC3, DWORD(-1), // Icon's text.
  123. 0, 0
  124. };
  125. //
  126. // This function is called in response to WM_INITDIALOG. It is also
  127. // called at other times to "reinitialize" the dialog controls to match
  128. // the current CSC configuration. This is why you see several checks
  129. // for uninitialized values throughout the function.
  130. //
  131. BOOL
  132. COfflineFilesPage::OnInitDialog(
  133. HWND hwnd,
  134. HWND hwndFocus,
  135. LPARAM lInitParam
  136. )
  137. {
  138. if (NULL == m_hwndDlg)
  139. {
  140. m_hwndDlg = hwnd;
  141. }
  142. //
  143. // Determine if the user has WRITE access to HKLM.
  144. //
  145. HKEY hkeyLM;
  146. DWORD disposition = 0;
  147. if (ERROR_SUCCESS == RegCreateKeyEx(HKEY_LOCAL_MACHINE,
  148. REGSTR_KEY_OFFLINEFILES,
  149. 0,
  150. NULL,
  151. 0,
  152. KEY_WRITE,
  153. NULL,
  154. &hkeyLM,
  155. &disposition))
  156. {
  157. m_bUserHasMachineAccess = true;
  158. RegCloseKey(hkeyLM);
  159. hkeyLM = NULL;
  160. }
  161. m_config.Load();
  162. if (!DisableForTerminalServer())
  163. {
  164. //
  165. // "Enable" checkbox. This reflects the true state of CSC.
  166. // Not the state of a registry setting.
  167. //
  168. CheckDlgButton(hwnd,
  169. IDC_CBX_ENABLE_CSC,
  170. IsCSCEnabled() ? BST_CHECKED : BST_UNCHECKED);
  171. //
  172. // "Sync at logon/logoff action checkboxes.
  173. //
  174. CheckDlgButton(hwnd,
  175. IDC_CBX_FULLSYNC_AT_LOGON,
  176. CConfig::eSyncFull == m_config.SyncAtLogon() ? BST_CHECKED : BST_UNCHECKED);
  177. CheckDlgButton(hwnd,
  178. IDC_CBX_FULLSYNC_AT_LOGOFF,
  179. CConfig::eSyncFull == m_config.SyncAtLogoff() ? BST_CHECKED : BST_UNCHECKED);
  180. //
  181. // Configure the "reminder" controls.
  182. //
  183. HWND hwndSpin = GetDlgItem(hwnd, IDC_SPIN_REMINDERS);
  184. HWND hwndEdit = GetDlgItem(hwnd, IDC_EDIT_REMINDERS);
  185. SendMessage(hwndSpin, UDM_SETRANGE, 0, MAKELONG((short)9999, (short)1));
  186. SendMessage(hwndSpin, UDM_SETBASE, 10, 0);
  187. UDACCEL rgAccel[] = {{ 2, 1 },
  188. { 4, 10 },
  189. 6, 100};
  190. SendMessage(hwndSpin, UDM_SETACCEL, (WPARAM)ARRAYSIZE(rgAccel), (LPARAM)rgAccel);
  191. SendMessage(hwndEdit, EM_SETLIMITTEXT, 4, 0);
  192. CheckDlgButton(hwnd,
  193. IDC_CBX_REMINDERS,
  194. m_config.NoReminders() ? BST_UNCHECKED : BST_CHECKED);
  195. SetDlgItemInt(hwnd, IDC_EDIT_REMINDERS, m_config.ReminderFreqMinutes(), FALSE);
  196. if (IsLinkOnDesktop())
  197. {
  198. CheckDlgButton(hwnd, IDC_CBX_LINK_ON_DESKTOP, BST_CHECKED);
  199. }
  200. //
  201. // Create tooltip for "Encrypt cache" checkbox.
  202. // If it should be initially visible, that is done
  203. // in response to PSN_SETACTIVE.
  204. //
  205. CreateEncryptionTooltip();
  206. //
  207. // Update the "Encrypt" checkbox.
  208. //
  209. UpdateEncryptionCheckbox();
  210. //
  211. // "Cache Size" slider
  212. //
  213. CSCSPACEUSAGEINFO sui;
  214. GetCscSpaceUsageInfo(&sui);
  215. m_hwndSlider = GetDlgItem(hwnd, IDC_SLIDER_CACHESIZE_PCT);
  216. InitSlider(hwnd, sui.llBytesOnVolume, sui.llBytesTotalInCache);
  217. //
  218. // Determine if the volume hosting the CSC database supports encryption.
  219. //
  220. m_bCscVolSupportsEncryption = CscVolumeSupportsEncryption(sui.szVolume);
  221. HWND hwndParent = GetParent(hwnd);
  222. if (NULL == m_pfnOldPropSheetWndProc)
  223. {
  224. //
  225. // Subclass the propsheet itself so we can intercept move messages
  226. // and adjust the balloon tip position when the dialog is moved.
  227. //
  228. m_pfnOldPropSheetWndProc = (WNDPROC)SetWindowLongPtr(hwndParent,
  229. GWLP_WNDPROC,
  230. (LONG_PTR)PropSheetSubclassWndProc);
  231. SetProp(hwndParent, c_szPropThis, (HANDLE)this);
  232. }
  233. if (NULL == m_pfnOldEncryptionTooltipWndProc)
  234. {
  235. //
  236. // Subclass the tooltip balloon so we can make it pop when selected.
  237. // Tracking tooltips don't pop themselves when clicked. You have
  238. // to do it for them.
  239. //
  240. m_pfnOldEncryptionTooltipWndProc = (WNDPROC)SetWindowLongPtr(m_hwndEncryptTooltip,
  241. GWLP_WNDPROC,
  242. (LONG_PTR)EncryptionTooltipSubclassWndProc);
  243. SetProp(m_hwndEncryptTooltip, c_szPropThis, (HANDLE)this);
  244. }
  245. }
  246. //
  247. // Save away the initial page state. This will be used to
  248. // determine when to enable the "Apply" button. See
  249. // HandlePageStateChange().
  250. //
  251. GetPageState(&m_state);
  252. return TRUE;
  253. }
  254. INT_PTR CALLBACK
  255. COfflineFilesPage::DlgProc(
  256. HWND hDlg,
  257. UINT message,
  258. WPARAM wParam,
  259. LPARAM lParam
  260. )
  261. {
  262. BOOL bResult = FALSE;
  263. //
  264. // Retrieve the "this" pointer from the dialog's userdata.
  265. // It was placed there in OnInitDialog().
  266. //
  267. COfflineFilesPage *pThis = (COfflineFilesPage *)GetWindowLongPtr(hDlg, DWLP_USER);
  268. switch(message)
  269. {
  270. case WM_INITDIALOG:
  271. {
  272. PROPSHEETPAGE *pPage = (PROPSHEETPAGE *)lParam;
  273. pThis = (COfflineFilesPage *)pPage->lParam;
  274. TraceAssert(NULL != pThis);
  275. SetWindowLongPtr(hDlg, DWLP_USER, (LONG_PTR)pThis);
  276. bResult = pThis->OnInitDialog(hDlg, (HWND)wParam, lParam);
  277. break;
  278. }
  279. case WM_NOTIFY:
  280. TraceAssert(NULL != pThis);
  281. bResult = pThis->OnNotify(hDlg, (int)wParam, (LPNMHDR)lParam);
  282. break;
  283. case WM_COMMAND:
  284. if (NULL != pThis)
  285. bResult = pThis->OnCommand(hDlg, HIWORD(wParam), LOWORD(wParam), (HWND)lParam);
  286. break;
  287. case WM_HELP:
  288. TraceAssert(NULL != pThis);
  289. bResult = pThis->OnHelp(hDlg, (LPHELPINFO)lParam);
  290. break;
  291. case WM_CONTEXTMENU:
  292. TraceAssert(NULL != pThis);
  293. bResult = pThis->OnContextMenu((HWND)wParam, LOWORD(lParam), HIWORD(lParam));
  294. break;
  295. case WM_DESTROY:
  296. TraceAssert(NULL != pThis);
  297. bResult = pThis->OnDestroy(hDlg);
  298. break;
  299. case WM_SETTINGCHANGE:
  300. case WM_SYSCOLORCHANGE:
  301. TraceAssert(NULL != pThis);
  302. bResult = pThis->OnSettingChange(hDlg, message, wParam, lParam);
  303. break;
  304. case WM_MOVE:
  305. TraceAssert(NULL != pThis);
  306. pThis->TrackEncryptionTooltip();
  307. break;
  308. case WM_HSCROLL:
  309. //
  310. // The cache-size slider generates horizontal scroll messages.
  311. //
  312. TraceAssert(NULL != pThis);
  313. pThis->OnHScroll(hDlg,
  314. (HWND)lParam, // hwndSlider
  315. (int)LOWORD(wParam), // notify code
  316. (int)HIWORD(wParam)); // thumb pos
  317. break;
  318. default:
  319. break;
  320. }
  321. return bResult;
  322. }
  323. //
  324. // Subclass window proc for the property sheet.
  325. // We intercept WM_MOVE messages and update the position of
  326. // the balloon tooltip to follow the movement of the property
  327. // page.
  328. //
  329. LRESULT
  330. COfflineFilesPage::PropSheetSubclassWndProc(
  331. HWND hwnd,
  332. UINT uMsg,
  333. WPARAM wParam,
  334. LPARAM lParam
  335. )
  336. {
  337. COfflineFilesPage *pThis = (COfflineFilesPage *)GetProp(hwnd, c_szPropThis);
  338. TraceAssert(NULL != pThis);
  339. switch(uMsg)
  340. {
  341. case WM_MOVE:
  342. if (pThis->m_hwndEncryptTooltip && IsWindowVisible(pThis->m_hwndEncryptTooltip))
  343. {
  344. pThis->TrackEncryptionTooltip();
  345. }
  346. break;
  347. default:
  348. break;
  349. }
  350. return CallWindowProc(pThis->m_pfnOldPropSheetWndProc, hwnd, uMsg, wParam, lParam);
  351. }
  352. BOOL
  353. COfflineFilesPage::OnDestroy(
  354. HWND hwnd
  355. )
  356. {
  357. //
  358. // Remove window properties and cancel subclassing set in OnInitDialog.
  359. //
  360. HWND hwndParent = GetParent(hwnd);
  361. if (NULL != m_pfnOldPropSheetWndProc)
  362. SetWindowLongPtr(hwndParent, GWLP_WNDPROC, (LONG_PTR)m_pfnOldPropSheetWndProc);
  363. RemoveProp(hwndParent, c_szPropThis);
  364. if (NULL != m_hwndEncryptTooltip)
  365. {
  366. if (NULL != m_pfnOldEncryptionTooltipWndProc)
  367. {
  368. SetWindowLongPtr(m_hwndEncryptTooltip, GWLP_WNDPROC, (LONG_PTR)m_pfnOldEncryptionTooltipWndProc);
  369. }
  370. RemoveProp(m_hwndEncryptTooltip, c_szPropThis);
  371. }
  372. return FALSE;
  373. }
  374. //
  375. // Forward all WM_SETTINGCHANGE and WM_SYSCOLORCHANGE messages
  376. // to controls that need to stay in sync with color changes.
  377. //
  378. BOOL
  379. COfflineFilesPage::OnSettingChange(
  380. HWND hDlg,
  381. UINT uMsg,
  382. WPARAM wParam,
  383. LPARAM lParam
  384. )
  385. {
  386. HWND rghwndCtls[] = { m_hwndSlider };
  387. for (int i = 0; i < ARRAYSIZE(rghwndCtls); i++)
  388. {
  389. SendMessage(rghwndCtls[i], uMsg, wParam, lParam);
  390. }
  391. return TRUE;
  392. }
  393. BOOL
  394. COfflineFilesPage::OnHelp(
  395. HWND hDlg,
  396. LPHELPINFO pHelpInfo
  397. )
  398. {
  399. if (HELPINFO_WINDOW == pHelpInfo->iContextType)
  400. {
  401. int idCtl = GetDlgCtrlID((HWND)pHelpInfo->hItemHandle);
  402. WinHelp((HWND)pHelpInfo->hItemHandle,
  403. UseWindowsHelp(idCtl) ? NULL : c_szHelpFile,
  404. HELP_WM_HELP,
  405. (DWORD_PTR)((LPTSTR)m_rgHelpIDs));
  406. }
  407. return TRUE;
  408. }
  409. BOOL
  410. COfflineFilesPage::OnContextMenu(
  411. HWND hwndItem,
  412. int xPos,
  413. int yPos
  414. )
  415. {
  416. int idCtl = GetDlgCtrlID(hwndItem);
  417. WinHelp(hwndItem,
  418. UseWindowsHelp(idCtl) ? NULL : c_szHelpFile,
  419. HELP_CONTEXTMENU,
  420. (DWORD_PTR)((LPTSTR)m_rgHelpIDs));
  421. return FALSE;
  422. }
  423. UINT CALLBACK
  424. COfflineFilesPage::PageCallback(
  425. HWND hwnd,
  426. UINT uMsg,
  427. LPPROPSHEETPAGE ppsp
  428. )
  429. {
  430. UINT uReturn = 1;
  431. COfflineFilesPage *pThis = (COfflineFilesPage *)ppsp->lParam;
  432. TraceAssert(NULL != pThis);
  433. switch(uMsg)
  434. {
  435. case PSPCB_CREATE:
  436. //
  437. // uReturn == 0 means Don't create the prop page.
  438. //
  439. uReturn = 1;
  440. break;
  441. case PSPCB_RELEASE:
  442. //
  443. // This will release the extension and call the virtual
  444. // destructor (which will destroy the prop page object).
  445. //
  446. pThis->m_pUnkOuter->Release();
  447. break;
  448. }
  449. return uReturn;
  450. }
  451. BOOL
  452. COfflineFilesPage::OnCommand(
  453. HWND hwnd,
  454. WORD wNotifyCode,
  455. WORD wID,
  456. HWND hwndCtl
  457. )
  458. {
  459. BOOL bResult = TRUE;
  460. switch(wNotifyCode)
  461. {
  462. case BN_CLICKED:
  463. switch(wID)
  464. {
  465. case IDC_CBX_ENCRYPT_CSC:
  466. //
  467. // The "Encrypt cache" checkbox is the 3-state flavor so that
  468. // we can represent the following states:
  469. //
  470. // CHECKED == "encrypted"
  471. // UNCHECKED == "decrypted"
  472. // INDETERMINATE == "partially encrypted or partially decrypted"
  473. //
  474. // We don't allow the user to set the checkbox state to
  475. // "indeterminate". It can only become "indeterminate" through
  476. // initialization in OnInitDialog. Successive selections of a
  477. // checkbox cycle through the following states:
  478. //
  479. // "checked"->"indeterminate"->"unchecked"->"checked"...
  480. //
  481. // Therefore, if the state is "indeterminate" following a user click
  482. // we force it to "unchecked". This way the checkbox can represent
  483. // three states but the user has control of only two (checked and
  484. // unchecked).
  485. //
  486. if (BST_INDETERMINATE == IsDlgButtonChecked(hwnd, wID))
  487. {
  488. CheckDlgButton(hwnd, wID, BST_UNCHECKED);
  489. }
  490. //
  491. // The encryption tooltip only appears when the checkbox is in
  492. // the INDETERMINATE state. Since we've just either checked
  493. // or unchecked it, the tooltip must disappear.
  494. //
  495. HideEncryptionTooltip();
  496. HandlePageStateChange();
  497. bResult = FALSE;
  498. break;
  499. case IDC_CBX_ENABLE_CSC:
  500. if (IsDlgButtonChecked(m_hwndDlg, IDC_CBX_ENABLE_CSC))
  501. {
  502. //
  503. // Checked the "enable CSC" checkbox.
  504. // Set the cache size slider to the default pct-used value (10%)
  505. //
  506. TrackBar_SetPos(m_hwndSlider, ThumbAtPctDiskSpace(0.10), true);
  507. SetCacheSizeDisplay(GetDlgItem(m_hwndDlg, IDC_TXT_CACHESIZE_PCT), TrackBar_GetPos(m_hwndSlider));
  508. CheckDlgButton(hwnd,
  509. IDC_CBX_LINK_ON_DESKTOP,
  510. IsLinkOnDesktop() ? BST_CHECKED : BST_UNCHECKED);
  511. }
  512. else
  513. {
  514. //
  515. // If CSC is disabled we remove the Offline Files
  516. // folder shortcut from the user's desktop.
  517. //
  518. CheckDlgButton(hwnd, IDC_CBX_LINK_ON_DESKTOP, BST_UNCHECKED);
  519. }
  520. //
  521. // Fall through...
  522. //
  523. case IDC_CBX_REMINDERS:
  524. EnableCtls(hwnd);
  525. //
  526. // Fall through...
  527. //
  528. case IDC_EDIT_REMINDERS:
  529. case IDC_CBX_FULLSYNC_AT_LOGOFF:
  530. case IDC_CBX_FULLSYNC_AT_LOGON:
  531. case IDC_SLIDER_CACHESIZE_PCT:
  532. case IDC_CBX_LINK_ON_DESKTOP:
  533. HandlePageStateChange();
  534. bResult = FALSE;
  535. break;
  536. case IDC_BTN_VIEW_CACHE:
  537. COfflineFilesFolder::Open();
  538. bResult = FALSE;
  539. break;
  540. case IDC_BTN_DELETE_CACHE:
  541. //
  542. // Ctl-Shift when pressing "Delete Files..."
  543. // is a special entry to reformatting the cache.
  544. //
  545. if ((0x8000 & GetAsyncKeyState(VK_SHIFT)) &&
  546. (0x8000 & GetAsyncKeyState(VK_CONTROL)))
  547. {
  548. OnFormatCache();
  549. }
  550. else
  551. {
  552. OnDeleteCache();
  553. }
  554. bResult = FALSE;
  555. break;
  556. case IDC_BTN_ADVANCED:
  557. {
  558. CAdvOptDlg dlg(m_hInstance, m_hwndDlg);
  559. dlg.Run();
  560. break;
  561. }
  562. default:
  563. break;
  564. }
  565. break;
  566. case EN_UPDATE:
  567. if (IDC_EDIT_REMINDERS == wID)
  568. {
  569. static bool bResetting; // prevent reentrancy.
  570. if (!bResetting)
  571. {
  572. //
  573. // The edit control is configured for a max of 4 digits and
  574. // numbers-only. Therefore the user can enter anything between
  575. // 0 and 9999. We don't want to allow 0 so we need this extra
  576. // check. The spinner has been set for a range of 0-9999.
  577. //
  578. int iValue = GetDlgItemInt(hwnd, IDC_EDIT_REMINDERS, NULL, FALSE);
  579. if (0 == iValue)
  580. {
  581. bResetting = true;
  582. SetDlgItemInt(hwnd, IDC_EDIT_REMINDERS, 1, FALSE);
  583. bResetting = false;
  584. }
  585. }
  586. HandlePageStateChange();
  587. }
  588. break;
  589. }
  590. return bResult;
  591. }
  592. //
  593. // Gather the state of the page and store it in a PgState object.
  594. //
  595. void
  596. COfflineFilesPage::GetPageState(
  597. PgState *pps
  598. )
  599. {
  600. pps->SetCscEnabled(BST_CHECKED == IsDlgButtonChecked(m_hwndDlg, IDC_CBX_ENABLE_CSC));
  601. pps->SetLinkOnDesktop(BST_CHECKED == IsDlgButtonChecked(m_hwndDlg, IDC_CBX_LINK_ON_DESKTOP));
  602. pps->SetEncrypted(IsDlgButtonChecked(m_hwndDlg, IDC_CBX_ENCRYPT_CSC));
  603. pps->SetFullSyncAtLogon(BST_CHECKED == IsDlgButtonChecked(m_hwndDlg, IDC_CBX_FULLSYNC_AT_LOGON));
  604. pps->SetFullSyncAtLogoff(BST_CHECKED == IsDlgButtonChecked(m_hwndDlg, IDC_CBX_FULLSYNC_AT_LOGOFF));
  605. pps->SetSliderPos(TrackBar_GetPos(m_hwndSlider));
  606. pps->SetRemindersEnabled(BST_CHECKED == IsDlgButtonChecked(m_hwndDlg, IDC_CBX_REMINDERS));
  607. pps->SetReminderFreq(GetDlgItemInt(m_hwndDlg, IDC_EDIT_REMINDERS, NULL, FALSE));
  608. }
  609. void
  610. COfflineFilesPage::HandlePageStateChange(
  611. void
  612. )
  613. {
  614. PgState s;
  615. GetPageState(&s);
  616. if (s == m_state)
  617. PropSheet_UnChanged(GetParent(m_hwndDlg), m_hwndDlg);
  618. else
  619. PropSheet_Changed(GetParent(m_hwndDlg), m_hwndDlg);
  620. }
  621. //
  622. // Handle horizontal scroll messages generated by the cache-size slider.
  623. //
  624. void
  625. COfflineFilesPage::OnHScroll(
  626. HWND hwndDlg,
  627. HWND hwndCtl,
  628. int iCode,
  629. int iPos
  630. )
  631. {
  632. if (TB_THUMBPOSITION != iCode && TB_THUMBTRACK != iCode)
  633. iPos = TrackBar_GetPos(hwndCtl);
  634. SetCacheSizeDisplay(GetDlgItem(hwndDlg, IDC_TXT_CACHESIZE_PCT), iPos);
  635. if (TB_ENDTRACK == iCode)
  636. HandlePageStateChange();
  637. }
  638. //
  639. // Update the cache size display "95.3 MB (23% of drive)" string.
  640. //
  641. void
  642. COfflineFilesPage::SetCacheSizeDisplay(
  643. HWND hwndCtl,
  644. int iThumbPos
  645. )
  646. {
  647. //
  648. // First convert the thumb position to a disk space value.
  649. //
  650. TCHAR szSize[40];
  651. FileSize fs(DiskSpaceAtThumb(iThumbPos));
  652. fs.GetString(szSize, ARRAYSIZE(szSize));
  653. //
  654. // Convert the thumb position to a percent-disk space value.
  655. //
  656. double x = 0.0;
  657. if (0 < iThumbPos)
  658. x = MAX(1.0, Rx(iThumbPos) * 100.00);
  659. //
  660. // Convert the percent-disk space value to a text string.
  661. //
  662. TCHAR szPct[10];
  663. wsprintf(szPct, TEXT("%d"), (DWORD)x);
  664. //
  665. // Format the result and display in the dialog.
  666. //
  667. LPTSTR pszText;
  668. if (0 < FormatStringID(&pszText, m_hInstance, IDS_FMT_CACHESIZE_DISPLAY, szSize, szPct))
  669. {
  670. SetWindowText(hwndCtl, pszText);
  671. LocalFree(pszText);
  672. }
  673. }
  674. void
  675. COfflineFilesPage::InitSlider(
  676. HWND hwndDlg,
  677. LONGLONG llSpaceMax,
  678. LONGLONG llSpaceUsed
  679. )
  680. {
  681. double pctUsed = 0.0; // Default
  682. //
  683. // Protect against:
  684. // 1. Div-by-zero
  685. // 2. Invalid FP operation. (i.e. 0.0 / 0.0)
  686. //
  687. if (0 != llSpaceMax)
  688. pctUsed = double(llSpaceUsed) / double(llSpaceMax);
  689. //
  690. // Change the resolution of the slider as drives get larger.
  691. //
  692. m_iSliderMax = 100; // < 1GB
  693. if (llSpaceMax > 0x0000010000000000i64)
  694. m_iSliderMax = 500; // >= 1TB
  695. else if (llSpaceMax > 0x0000000040000000i64)
  696. m_iSliderMax = 300; // >= 1GB
  697. m_llAvailableDiskSpace = llSpaceMax;
  698. TrackBar_SetTicFreq(m_hwndSlider, m_iSliderMax / 10);
  699. TrackBar_SetPageSize(m_hwndSlider, m_iSliderMax / 10);
  700. TrackBar_SetRange(m_hwndSlider, 0, m_iSliderMax, false);
  701. TrackBar_SetPos(m_hwndSlider, ThumbAtPctDiskSpace(pctUsed), true);
  702. SetCacheSizeDisplay(GetDlgItem(hwndDlg, IDC_TXT_CACHESIZE_PCT), TrackBar_GetPos(m_hwndSlider));
  703. }
  704. //
  705. // Enable/disable page controls.
  706. //
  707. void
  708. COfflineFilesPage::EnableCtls(
  709. HWND hwnd
  710. )
  711. {
  712. typedef bool (CConfigItems::*PBMF)(void) const;
  713. static const struct
  714. {
  715. UINT idCtl;
  716. PBMF pfnRestricted;
  717. bool bRequiresMachineAccess;
  718. } rgCtls[] = { { IDC_CBX_FULLSYNC_AT_LOGOFF, &CConfigItems::NoConfigSyncAtLogoff, false },
  719. { IDC_CBX_FULLSYNC_AT_LOGON, &CConfigItems::NoConfigSyncAtLogon, false },
  720. { IDC_CBX_REMINDERS, &CConfigItems::NoConfigReminders, false },
  721. { IDC_CBX_LINK_ON_DESKTOP, NULL, false },
  722. { IDC_CBX_ENCRYPT_CSC, &CConfigItems::NoConfigEncryptCache, true },
  723. { IDC_TXT_CACHESIZE_PCT, NULL, true },
  724. { IDC_SLIDER_CACHESIZE_PCT, &CConfigItems::NoConfigCacheSize, true },
  725. { IDC_LBL_CACHESIZE_PCT, &CConfigItems::NoConfigCacheSize, true },
  726. { IDC_BTN_VIEW_CACHE, NULL, false },
  727. { IDC_BTN_ADVANCED, NULL, false },
  728. { IDC_BTN_DELETE_CACHE, NULL, false }
  729. };
  730. bool bCscEnabled = BST_CHECKED == IsDlgButtonChecked(hwnd, IDC_CBX_ENABLE_CSC);
  731. bool bEnable;
  732. for (int i = 0; i < ARRAYSIZE(rgCtls); i++)
  733. {
  734. bEnable = bCscEnabled;
  735. if (bEnable)
  736. {
  737. if (rgCtls[i].bRequiresMachineAccess && !m_bUserHasMachineAccess)
  738. {
  739. bEnable = false;
  740. }
  741. if (bEnable)
  742. {
  743. //
  744. // Apply any policy restrictions.
  745. //
  746. PBMF pfn = rgCtls[i].pfnRestricted;
  747. if (NULL != pfn && (m_config.*pfn)())
  748. bEnable = false;
  749. if (bEnable)
  750. {
  751. //
  752. // "View..." button requires special handling as it isn't based off of a
  753. // boolean restriction function.
  754. //
  755. if ((IDC_BTN_VIEW_CACHE == rgCtls[i].idCtl || IDC_CBX_LINK_ON_DESKTOP == rgCtls[i].idCtl) && m_config.NoCacheViewer())
  756. {
  757. bEnable = false;
  758. }
  759. else if (IDC_CBX_ENCRYPT_CSC == rgCtls[i].idCtl)
  760. {
  761. //
  762. // "Encrypt offline files" checkbox requires special handling.
  763. //
  764. // Cache encryption cannot be performed with CSC disabled or
  765. // if the CSC volume doesn't support encryption or if the user
  766. // is not an administrator.
  767. //
  768. if (!bCscEnabled ||
  769. !m_bCscVolSupportsEncryption ||
  770. !IsCurrentUserAnAdminMember())
  771. {
  772. bEnable = false;
  773. }
  774. }
  775. }
  776. }
  777. }
  778. EnableDialogItem(hwnd, rgCtls[i].idCtl, bEnable);
  779. }
  780. //
  781. // Reminder controls are dependent upon several inputs.
  782. //
  783. bEnable = bCscEnabled &&
  784. (BST_CHECKED == IsDlgButtonChecked(hwnd, IDC_CBX_REMINDERS)) &&
  785. !m_config.NoConfigReminders() &&
  786. !m_config.NoConfigReminderFreqMinutes();
  787. EnableDialogItem(hwnd, IDC_TXT_REMINDERS1, bEnable);
  788. EnableDialogItem(hwnd, IDC_EDIT_REMINDERS, bEnable);
  789. EnableDialogItem(hwnd, IDC_SPIN_REMINDERS, bEnable);
  790. //
  791. // "Enabled" checkbox requires special handling.
  792. // It can't be included with the other controls because it will be disabled
  793. // when the user unchecks it. Then there's no way to re-enable it.
  794. // Disable the checkbox if any of the following is true:
  795. // 1. Admin policy has enabled/disabled CSC.
  796. // 2. User doesn't have WRITE access to HKLM.
  797. //
  798. bEnable = !m_config.NoConfigCscEnabled() && m_bUserHasMachineAccess;
  799. EnableWindow(GetDlgItem(hwnd, IDC_CBX_ENABLE_CSC), bEnable);
  800. }
  801. BOOL
  802. COfflineFilesPage::OnNotify(
  803. HWND hDlg,
  804. int idCtl,
  805. LPNMHDR pnmhdr
  806. )
  807. {
  808. BOOL bResult = TRUE;
  809. switch(pnmhdr->code)
  810. {
  811. case PSN_APPLY:
  812. //
  813. // Prevent re-entrancy. If the user changes the encryption
  814. // setting and presses "OK", the prop sheet will remain visible
  815. // during the encryption operation. Since we're displaying a progress
  816. // dialog and pumping messages, it's possible for the user to
  817. // re-select the "OK" or "Apply" buttons during the encryption.
  818. // Use a simple flag variable to prevent re-entrancy.
  819. //
  820. if (!m_bApplyingSettings)
  821. {
  822. m_bApplyingSettings = true;
  823. //
  824. // If the lParam is TRUE, the property sheet is closing.
  825. //
  826. bResult = ApplySettings(hDlg, boolify(((LPPSHNOTIFY)pnmhdr)->lParam));
  827. m_bApplyingSettings = false;
  828. }
  829. break;
  830. case PSN_KILLACTIVE:
  831. //
  832. // Hide the tooltip when the page is deactivated.
  833. //
  834. HideEncryptionTooltip();
  835. SetWindowLongPtr(hDlg, DWLP_MSGRESULT, 0);
  836. bResult = FALSE;
  837. break;
  838. case PSN_SETACTIVE:
  839. //
  840. // Enable/disable controls whenever the page becomes active.
  841. //
  842. EnableCtls(hDlg);
  843. //
  844. // Display the encryption tooltip balloon if necessary
  845. // on the FIRST page activation only.
  846. // Note that we need to do this here rather than in OnInitDialog
  847. // to prevent the balloon from 'hopping' when the property sheet
  848. // code repositions the page.
  849. //
  850. if (m_bFirstActivate)
  851. {
  852. UpdateEncryptionTooltipBalloon();
  853. m_bFirstActivate = false;
  854. }
  855. SetWindowLongPtr(hDlg, DWLP_MSGRESULT, 0);
  856. bResult = FALSE;
  857. break;
  858. case PSN_TRANSLATEACCELERATOR:
  859. //
  860. // User pressed a key.
  861. // Hide the tooltip.
  862. //
  863. HideEncryptionTooltip();
  864. break;
  865. case TTN_GETDISPINFO:
  866. OnTTN_GetDispInfo((LPNMTTDISPINFO)pnmhdr);
  867. break;
  868. default:
  869. break;
  870. }
  871. return bResult;
  872. }
  873. LRESULT
  874. COfflineFilesPage::EncryptionTooltipSubclassWndProc(
  875. HWND hwnd,
  876. UINT uMsg,
  877. WPARAM wParam,
  878. LPARAM lParam
  879. )
  880. {
  881. COfflineFilesPage *pThis = (COfflineFilesPage *)GetProp(hwnd, c_szPropThis);
  882. TraceAssert(NULL != pThis);
  883. switch(uMsg)
  884. {
  885. case WM_LBUTTONDOWN:
  886. case WM_RBUTTONDOWN:
  887. //
  888. // When the tooltip balloon is clicked, pop the balloon.
  889. //
  890. pThis->HideEncryptionTooltip();
  891. //
  892. // Fall through...
  893. //
  894. default:
  895. break;
  896. }
  897. return CallWindowProc(pThis->m_pfnOldEncryptionTooltipWndProc, hwnd, uMsg, wParam, lParam);
  898. }
  899. //
  900. // Create a tooltip for a given control.
  901. // The parent of the control is required to respond to TTN_GETDISPINFO
  902. // and provide the text.
  903. //
  904. void
  905. COfflineFilesPage::CreateEncryptionTooltip(
  906. void
  907. )
  908. {
  909. if (NULL == m_hwndEncryptTooltip)
  910. {
  911. INITCOMMONCONTROLSEX iccex;
  912. iccex.dwICC = ICC_WIN95_CLASSES;
  913. iccex.dwSize = sizeof(INITCOMMONCONTROLSEX);
  914. InitCommonControlsEx(&iccex);
  915. m_hwndEncryptTooltip = CreateWindowEx(NULL,
  916. TOOLTIPS_CLASS,
  917. NULL,
  918. WS_POPUP | TTS_NOPREFIX | TTS_BALLOON,
  919. CW_USEDEFAULT,
  920. CW_USEDEFAULT,
  921. CW_USEDEFAULT,
  922. CW_USEDEFAULT,
  923. GetDlgItem(m_hwndDlg, IDC_CBX_ENCRYPT_CSC),
  924. NULL,
  925. m_hInstance,
  926. NULL);
  927. if (NULL != m_hwndEncryptTooltip)
  928. {
  929. TOOLINFO ti;
  930. ti.cbSize = sizeof(TOOLINFO);
  931. ti.uFlags = TTF_TRACK | TTF_ABSOLUTE;
  932. ti.hwnd = m_hwndDlg;
  933. ti.uId = IDC_CBX_ENCRYPT_CSC;
  934. ti.lpszText = LPSTR_TEXTCALLBACK;
  935. ti.hinst = NULL;
  936. ti.lParam = 0;
  937. SendMessage(m_hwndEncryptTooltip, TTM_ADDTOOL, 0, (LPARAM)(LPTOOLINFO)&ti);
  938. //
  939. // Set the tooltip width to 3/4 the dialog width.
  940. //
  941. RECT rcDlg;
  942. GetClientRect(m_hwndDlg, &rcDlg);
  943. SendMessage(m_hwndEncryptTooltip, TTM_SETMAXTIPWIDTH, 0, (LPARAM)(((rcDlg.right-rcDlg.left) * 3) / 4));
  944. }
  945. }
  946. }
  947. void
  948. COfflineFilesPage::OnTTN_GetDispInfo(
  949. LPNMTTDISPINFO pttdi
  950. )
  951. {
  952. LPNMHDR pnmhdr = (LPNMHDR)pttdi;
  953. BOOL bResult = TRUE;
  954. UINT idCtl = (UINT)(UINT_PTR)pnmhdr->idFrom;
  955. if (TTF_IDISHWND & pttdi->uFlags)
  956. {
  957. idCtl = GetDlgCtrlID((HWND)pnmhdr->idFrom);
  958. }
  959. if (IDC_CBX_ENCRYPT_CSC == idCtl)
  960. {
  961. //
  962. // Provide the text and image for the encryption tooltip.
  963. //
  964. //
  965. // These constants are standard for TTM_SETTITLE.
  966. //
  967. enum TTICON { TTICON_NONE, TTICON_INFO, TTICON_WARNING, TTICON_ERROR };
  968. //
  969. // Map of state to body text.
  970. //
  971. const UINT rgBodyText[][2] = {
  972. // -------------- Decryption ------------ ---------- Encryption ------------------
  973. { IDS_TT_BODY_DECRYPTED_PARTIAL_NONADMIN, IDS_TT_BODY_ENCRYPTED_PARTIAL_NONADMIN }, // Non-admin user
  974. { IDS_TT_BODY_DECRYPTED_PARTIAL, IDS_TT_BODY_ENCRYPTED_PARTIAL } // Admin user
  975. };
  976. //
  977. // Map of state to title text and icon.
  978. //
  979. const struct
  980. {
  981. UINT idsTitle; // Title text
  982. int iIcon; // TTICON_XXXX
  983. } rgTitleAndIcon[] = {
  984. { IDS_TT_TITLE_DECRYPTED_PARTIAL, TTICON_INFO }, // Decryption
  985. { IDS_TT_TITLE_ENCRYPTED_PARTIAL, TTICON_WARNING } // Encryption
  986. };
  987. const BOOL bEncrypted = IsCacheEncrypted(NULL);
  988. //
  989. // For non-admin users, the "Encrypt CSC" checkbox is disabled.
  990. //
  991. const BOOL bCbxEncryptEnabled = IsWindowEnabled(GetDlgItem(m_hwndDlg, IDC_CBX_ENCRYPT_CSC));
  992. //
  993. // Tooltip body text.
  994. //
  995. m_szEncryptTooltipBody[0] = TEXT('\0');
  996. LoadString(m_hInstance,
  997. rgBodyText[int(bCbxEncryptEnabled)][int(bEncrypted)],
  998. m_szEncryptTooltipBody,
  999. ARRAYSIZE(m_szEncryptTooltipBody));
  1000. pttdi->lpszText = m_szEncryptTooltipBody;
  1001. //
  1002. // Tooltip title text and icon.
  1003. //
  1004. const iIcon = rgTitleAndIcon[int(bEncrypted)].iIcon;
  1005. LPTSTR pszTitle;
  1006. if (0 < FormatStringID(&pszTitle, m_hInstance, rgTitleAndIcon[int(bEncrypted)].idsTitle))
  1007. {
  1008. SendMessage(m_hwndEncryptTooltip, TTM_SETTITLE, (WPARAM)iIcon, (LPARAM)pszTitle);
  1009. LocalFree(pszTitle);
  1010. }
  1011. }
  1012. }
  1013. void
  1014. COfflineFilesPage::ShowEncryptionTooltip(
  1015. bool bEncrypted
  1016. )
  1017. {
  1018. if (NULL != m_hwndEncryptTooltip)
  1019. {
  1020. //
  1021. // Position tooltip correctly before showing
  1022. //
  1023. TrackEncryptionTooltip();
  1024. //
  1025. // Show the tooltip.
  1026. //
  1027. TOOLINFO ti;
  1028. ti.cbSize = sizeof(ti);
  1029. ti.hwnd = m_hwndDlg;
  1030. ti.uId = IDC_CBX_ENCRYPT_CSC;
  1031. SendMessage(m_hwndEncryptTooltip, TTM_TRACKACTIVATE, (WPARAM)TRUE, (LPARAM)&ti);
  1032. SendMessage(m_hwndEncryptTooltip, TTM_UPDATE, 0, 0);
  1033. }
  1034. }
  1035. void
  1036. COfflineFilesPage::HideEncryptionTooltip(
  1037. void
  1038. )
  1039. {
  1040. if (NULL != m_hwndEncryptTooltip)
  1041. {
  1042. SendMessage(m_hwndEncryptTooltip, TTM_TRACKACTIVATE, (WPARAM)FALSE, 0);
  1043. }
  1044. }
  1045. void
  1046. COfflineFilesPage::TrackEncryptionTooltip(
  1047. void
  1048. )
  1049. {
  1050. //
  1051. // Point the tip stem at center of the lower edge of the encryption
  1052. // checkbox.
  1053. // The Windows UX manual says checkboxes are 10 dialog units wide.
  1054. //
  1055. if (NULL != m_hwndEncryptTooltip)
  1056. {
  1057. const INT DialogBaseUnitsX = LOWORD(GetDialogBaseUnits());
  1058. const INT cxCbx = (DialogBaseUnitsX * 10) / 4;
  1059. RECT rc;
  1060. GetWindowRect(GetDlgItem(m_hwndDlg, IDC_CBX_ENCRYPT_CSC), &rc);
  1061. SendMessage(m_hwndEncryptTooltip,
  1062. TTM_TRACKPOSITION,
  1063. 0,
  1064. (LPARAM)(DWORD)MAKELONG(rc.left + (cxCbx / 2), rc.bottom));
  1065. }
  1066. }
  1067. //
  1068. // Set the state of the "Encrypt Cache" checkbox to reflect
  1069. // the actual state of cache encryption. Also display
  1070. // the balloon tooltip if the checkbox is in the
  1071. // indeterminate state.
  1072. //
  1073. void
  1074. COfflineFilesPage::UpdateEncryptionCheckboxOrBalloon(
  1075. bool bCheckbox
  1076. )
  1077. {
  1078. //
  1079. // "Encrypt CSC" checkbox.
  1080. // The display logic is captured in this table.
  1081. //
  1082. const UINT rgCheck[] = { BST_UNCHECKED, // 00 = Decrypted,
  1083. BST_INDETERMINATE, // 01 = Partially decrypted
  1084. BST_CHECKED, // 10 = Encrypted,
  1085. BST_INDETERMINATE // 11 = Partially encrypted
  1086. };
  1087. BOOL bPartial = FALSE;
  1088. const BOOL bEncrypted = IsCacheEncrypted(&bPartial);
  1089. const int iState = (int(bEncrypted) << 1) | int(bPartial);
  1090. if (bCheckbox)
  1091. {
  1092. //
  1093. // Update the checkbox.
  1094. //
  1095. CheckDlgButton(m_hwndDlg, IDC_CBX_ENCRYPT_CSC, rgCheck[iState]);
  1096. }
  1097. else
  1098. {
  1099. //
  1100. // Update the tooltip
  1101. //
  1102. if (BST_INDETERMINATE == rgCheck[iState])
  1103. {
  1104. ShowEncryptionTooltip(boolify(bEncrypted));
  1105. }
  1106. else
  1107. {
  1108. HideEncryptionTooltip();
  1109. }
  1110. }
  1111. }
  1112. void
  1113. COfflineFilesPage::UpdateEncryptionCheckbox(
  1114. void
  1115. )
  1116. {
  1117. UpdateEncryptionCheckboxOrBalloon(true);
  1118. }
  1119. void
  1120. COfflineFilesPage::UpdateEncryptionTooltipBalloon(
  1121. void
  1122. )
  1123. {
  1124. UpdateEncryptionCheckboxOrBalloon(false);
  1125. }
  1126. BOOL
  1127. COfflineFilesPage::ApplySettings(
  1128. HWND hwnd,
  1129. bool bPropSheetClosing
  1130. )
  1131. {
  1132. //
  1133. // Query the current state of controls on the page to see if
  1134. // anything has changed.
  1135. //
  1136. PgState s;
  1137. GetPageState(&s);
  1138. if (s != m_state)
  1139. {
  1140. //
  1141. // Something on the page has changed.
  1142. // Open the reg keys.
  1143. //
  1144. RegKey keyLM(HKEY_LOCAL_MACHINE, REGSTR_KEY_OFFLINEFILES);
  1145. HRESULT hr = keyLM.Open(KEY_WRITE, true);
  1146. if (FAILED(hr))
  1147. {
  1148. Trace((TEXT("Error 0x%08X opening NetCache machine settings key"), hr));
  1149. //
  1150. // Continue...
  1151. // Note that EnableCtls has disabled any controls that require
  1152. // WRITE access to HKLM.
  1153. //
  1154. }
  1155. RegKey keyCU(HKEY_CURRENT_USER, REGSTR_KEY_OFFLINEFILES);
  1156. hr = keyCU.Open(KEY_WRITE, true);
  1157. if (FAILED(hr))
  1158. {
  1159. //
  1160. // Failure to open HKCU is a problem. No use in proceeding.
  1161. //
  1162. Trace((TEXT("Error 0x%08X opening NetCache user settings key"), hr));
  1163. return FALSE;
  1164. }
  1165. //
  1166. // Handle encryption/decryption of the cache (part 1).
  1167. // Encryption/decryption can only be done when CSC is enabled.
  1168. // Therefore, since the user can change both the "enabled" and
  1169. // "encrypted" state from the same property page we need to be smart
  1170. // about when to do the encryption. We may need to do it before
  1171. // disabling CSC or after enabling CSC.
  1172. //
  1173. bool bEncryptOperationPerformed = false;
  1174. if (m_state.GetCscEnabled() && !s.GetCscEnabled())
  1175. {
  1176. //
  1177. // User is disabling CSC. If they also want to change the cache
  1178. // encryption state we must do it now while CSC is enabled.
  1179. //
  1180. _ApplyEncryptionSetting(keyLM, keyCU, s, bPropSheetClosing, &bEncryptOperationPerformed);
  1181. }
  1182. bool bUpdateSystrayUI = false;
  1183. _ApplyEnabledSetting(keyLM, keyCU, s, &bUpdateSystrayUI);
  1184. //
  1185. // Handle encryption/decryption of the cache (part 2).
  1186. //
  1187. if (!bEncryptOperationPerformed)
  1188. {
  1189. //
  1190. // Encryption has not yet been performed. If user wants to change encryption
  1191. // state, do it now.
  1192. // Note that if the user enabled CSC and that enabling failed, encryption
  1193. // will also fail. Not a worry since the probability that CSC will fail
  1194. // to be enabled is extrememly low (I've never seen it fail). If it does
  1195. // the encryption process will display an error message.
  1196. //
  1197. _ApplyEncryptionSetting(keyLM, keyCU, s, bPropSheetClosing, &bEncryptOperationPerformed);
  1198. }
  1199. //
  1200. // Write "sync-at-logon/logoff" (quick vs. full) settings.
  1201. //
  1202. _ApplySyncAtLogonSetting(keyLM, keyCU, s);
  1203. _ApplySyncAtLogoffSetting(keyLM, keyCU, s);
  1204. //
  1205. // Write the various "reminders" settings.
  1206. //
  1207. _ApplyReminderSettings(keyLM, keyCU, s);
  1208. //
  1209. // Create or remove the folder link on the desktop.
  1210. //
  1211. _ApplyFolderLinkSetting(keyLM, keyCU, s);
  1212. //
  1213. // Write cache size as pct * 10,000.
  1214. //
  1215. _ApplyCacheSizeSetting(keyLM, keyCU, s);
  1216. //
  1217. // Refresh the cached page state info.
  1218. //
  1219. GetPageState(&m_state);
  1220. //
  1221. // Update the SysTray icon if necessary.
  1222. //
  1223. if (bUpdateSystrayUI)
  1224. {
  1225. HWND hwndNotify = NULL;
  1226. if (!s.GetCscEnabled())
  1227. {
  1228. //
  1229. // If we're disabling CSC, refresh the shell windows BEFORE we
  1230. // destroy the SysTray CSCUI "service".
  1231. //
  1232. hwndNotify = _FindNotificationWindow();
  1233. if (IsWindow(hwndNotify))
  1234. {
  1235. SendMessage(hwndNotify, PWM_REFRESH_SHELL, 0, 0);
  1236. }
  1237. }
  1238. HWND hwndSysTray = FindWindow(SYSTRAY_CLASSNAME, NULL);
  1239. if (IsWindow(hwndSysTray))
  1240. {
  1241. SendMessage(hwndSysTray, STWM_ENABLESERVICE, STSERVICE_CSC, s.GetCscEnabled());
  1242. }
  1243. if (s.GetCscEnabled())
  1244. {
  1245. SHLoadNonloadedIconOverlayIdentifiers();
  1246. //
  1247. // If we're enabling CSC, refresh the shell windows AFTER we
  1248. // create the SysTray CSCUI "service".
  1249. //
  1250. hwndNotify = _FindNotificationWindow();
  1251. if (IsWindow(hwndNotify))
  1252. {
  1253. PostMessage(hwndNotify, PWM_REFRESH_SHELL, 0, 0);
  1254. }
  1255. }
  1256. }
  1257. }
  1258. return TRUE;
  1259. }
  1260. HRESULT
  1261. COfflineFilesPage::_ApplyEnabledSetting(
  1262. RegKey& keyLM,
  1263. RegKey& keyCU,
  1264. const PgState& pgstNow,
  1265. bool *pbUpdateSystrayUI
  1266. )
  1267. {
  1268. HRESULT hr = S_OK;
  1269. *pbUpdateSystrayUI = false;
  1270. //
  1271. // Process the "enabled" setting even if the page state hasn't
  1272. // changed. This is a special case because we initialize the
  1273. // "enabled" checkbox from IsCSCEnabled() but we change the
  1274. // enabled/disabled state by setting a registry value and
  1275. // possibly rebooting.
  1276. //
  1277. hr = keyLM.SetValue(REGSTR_VAL_CSCENABLED, DWORD(pgstNow.GetCscEnabled()));
  1278. if (FAILED(hr))
  1279. {
  1280. Trace((TEXT("Error 0x%08X setting reg value \"%s\""), hr, REGSTR_VAL_CSCENABLED));
  1281. }
  1282. //
  1283. // Handle any enabling/disabling of CSC.
  1284. //
  1285. if (pgstNow.GetCscEnabled() != boolify(IsCSCEnabled()))
  1286. {
  1287. bool bReboot = false;
  1288. DWORD dwError = ERROR_SUCCESS;
  1289. if (EnableOrDisableCsc(pgstNow.GetCscEnabled(), &bReboot, &dwError))
  1290. {
  1291. if (bReboot)
  1292. {
  1293. //
  1294. // Requires a reboot.
  1295. //
  1296. PropSheet_RebootSystem(GetParent(m_hwndDlg));
  1297. }
  1298. else
  1299. {
  1300. //
  1301. // It's dynamic (no reboot) so update the systray UI.
  1302. // Note that we want to update the systray UI AFTER we've
  1303. // made any configuration changes to the registry
  1304. // (i.e. balloon settings).
  1305. //
  1306. *pbUpdateSystrayUI = true;
  1307. }
  1308. }
  1309. else
  1310. {
  1311. //
  1312. // Error trying to enable or disable CSC.
  1313. //
  1314. CscMessageBox(m_hwndDlg,
  1315. MB_ICONERROR | MB_OK,
  1316. Win32Error(dwError),
  1317. m_hInstance,
  1318. pgstNow.GetCscEnabled() ? IDS_ERR_ENABLECSC : IDS_ERR_DISABLECSC);
  1319. }
  1320. }
  1321. return hr;
  1322. }
  1323. HRESULT
  1324. COfflineFilesPage::_ApplySyncAtLogoffSetting(
  1325. RegKey& keyLM,
  1326. RegKey& keyCU,
  1327. const PgState& pgstNow
  1328. )
  1329. {
  1330. //
  1331. // Write "sync-at-logoff" (quick vs. full) setting.
  1332. //
  1333. HRESULT hr = keyCU.SetValue(REGSTR_VAL_SYNCATLOGOFF, DWORD(pgstNow.GetFullSyncAtLogoff()));
  1334. if (SUCCEEDED(hr))
  1335. {
  1336. if (!m_state.GetFullSyncAtLogoff() && pgstNow.GetFullSyncAtLogoff())
  1337. {
  1338. //
  1339. // If the user has just turned on full sync we want to
  1340. // make sure SyncMgr is enabled for sync-at-logoff.
  1341. // There are some weirdnesses with doing this but it's the most
  1342. // consistent behavior we can offer the user given
  1343. // the current design of SyncMgr and CSC. Internal use and beta
  1344. // testing shows that users expect Sync-at-logoff to be enabled
  1345. // when this checkbox is checked.
  1346. //
  1347. RegisterSyncMgrHandler(TRUE);
  1348. RegisterForSyncAtLogonAndLogoff(SYNCMGRREGISTERFLAG_PENDINGDISCONNECT,
  1349. SYNCMGRREGISTERFLAG_PENDINGDISCONNECT);
  1350. }
  1351. }
  1352. else
  1353. {
  1354. Trace((TEXT("Error 0x%08X setting reg value \"%s\""), hr, REGSTR_VAL_SYNCATLOGOFF));
  1355. }
  1356. return hr;
  1357. }
  1358. HRESULT
  1359. COfflineFilesPage::_ApplySyncAtLogonSetting(
  1360. RegKey& keyLM,
  1361. RegKey& keyCU,
  1362. const PgState& pgstNow
  1363. )
  1364. {
  1365. //
  1366. // Write "sync-at-logon" (quick vs. full) setting.
  1367. //
  1368. HRESULT hr = keyCU.SetValue(REGSTR_VAL_SYNCATLOGON, DWORD(pgstNow.GetFullSyncAtLogon()));
  1369. if (SUCCEEDED(hr))
  1370. {
  1371. if (!m_state.GetFullSyncAtLogon() && pgstNow.GetFullSyncAtLogon())
  1372. {
  1373. //
  1374. // If the user has just turned on full sync we want to
  1375. // make sure SyncMgr is enabled for sync-at-logon.
  1376. // There are some weirdnesses with doing this but it's the most
  1377. // consistent behavior we can offer the user given
  1378. // the current design of SyncMgr and CSC. Internal use and beta
  1379. // testing shows that users expect Sync-at-logon to be enabled
  1380. // when this checkbox is checked.
  1381. //
  1382. RegisterSyncMgrHandler(TRUE);
  1383. RegisterForSyncAtLogonAndLogoff(SYNCMGRREGISTERFLAG_CONNECT,
  1384. SYNCMGRREGISTERFLAG_CONNECT);
  1385. }
  1386. }
  1387. else
  1388. {
  1389. Trace((TEXT("Error 0x%08X setting reg value \"%s\""), hr, REGSTR_VAL_SYNCATLOGON));
  1390. }
  1391. return hr;
  1392. }
  1393. HRESULT
  1394. COfflineFilesPage::_ApplyReminderSettings(
  1395. RegKey& keyLM,
  1396. RegKey& keyCU,
  1397. const PgState& pgstNow
  1398. )
  1399. {
  1400. HRESULT hr = keyCU.SetValue(REGSTR_VAL_NOREMINDERS, DWORD(!pgstNow.GetRemindersEnabled()));
  1401. if (FAILED(hr))
  1402. {
  1403. Trace((TEXT("Error 0x%08X setting reg value \"%s\""), hr, REGSTR_VAL_NOREMINDERS));
  1404. }
  1405. hr = keyCU.SetValue(REGSTR_VAL_REMINDERFREQMINUTES, DWORD(pgstNow.GetReminderFreq()));
  1406. if (FAILED(hr))
  1407. {
  1408. Trace((TEXT("Error 0x%08X setting reg value \"%s\""), hr, REGSTR_VAL_REMINDERFREQMINUTES));
  1409. }
  1410. if (m_state.GetReminderFreq() != pgstNow.GetReminderFreq())
  1411. {
  1412. PostToSystray(PWM_RESET_REMINDERTIMER, 0, 0);
  1413. }
  1414. return hr;
  1415. }
  1416. HRESULT
  1417. COfflineFilesPage::_ApplyFolderLinkSetting(
  1418. RegKey& keyLM,
  1419. RegKey& keyCU,
  1420. const PgState& pgstNow
  1421. )
  1422. {
  1423. if (m_state.GetLinkOnDesktop() != pgstNow.GetLinkOnDesktop())
  1424. {
  1425. TCHAR szLinkPath[MAX_PATH];
  1426. bool bLinkFileIsOnDesktop = IsLinkOnDesktop(szLinkPath, ARRAYSIZE(szLinkPath));
  1427. if (bLinkFileIsOnDesktop && !pgstNow.GetLinkOnDesktop())
  1428. {
  1429. DeleteOfflineFilesFolderLink(m_hwndDlg);
  1430. }
  1431. else if (!bLinkFileIsOnDesktop && pgstNow.GetLinkOnDesktop())
  1432. {
  1433. COfflineFilesFolder::CreateLinkOnDesktop(m_hwndDlg);
  1434. }
  1435. }
  1436. return S_OK;
  1437. }
  1438. HRESULT
  1439. COfflineFilesPage::_ApplyCacheSizeSetting(
  1440. RegKey& keyLM,
  1441. RegKey& keyCU,
  1442. const PgState& pgstNow
  1443. )
  1444. {
  1445. double pctCacheSize = Rx(TrackBar_GetPos(m_hwndSlider));
  1446. HRESULT hr = keyLM.SetValue(REGSTR_VAL_DEFCACHESIZE, DWORD(pctCacheSize * 10000.00));
  1447. if (FAILED(hr))
  1448. {
  1449. Trace((TEXT("Error 0x%08X setting reg value \"%s\""), hr, REGSTR_VAL_DEFCACHESIZE));
  1450. }
  1451. ULARGE_INTEGER ulCacheSize;
  1452. ulCacheSize.QuadPart = DWORDLONG(m_llAvailableDiskSpace * pctCacheSize);
  1453. if (!CSCSetMaxSpace(ulCacheSize.HighPart, ulCacheSize.LowPart))
  1454. {
  1455. Trace((TEXT("Error %d setting cache size"), GetLastError()));
  1456. }
  1457. return hr;
  1458. }
  1459. HRESULT
  1460. COfflineFilesPage::_ApplyEncryptionSetting(
  1461. RegKey& keyLM,
  1462. RegKey& keyCU,
  1463. const PgState& pgstNow,
  1464. bool bPropSheetClosing,
  1465. bool *pbPerformed
  1466. )
  1467. {
  1468. HRESULT hr = S_OK;
  1469. *pbPerformed = false;
  1470. if (m_state.GetEncrypted() != pgstNow.GetEncrypted())
  1471. {
  1472. EncryptOrDecryptCache(BST_CHECKED == pgstNow.GetEncrypted(), bPropSheetClosing);
  1473. *pbPerformed = true;
  1474. //
  1475. // Record the user's setting in the registry as a "preference". If policy
  1476. // is later applied then removed we need to know the user's previous preference.
  1477. // Note that it's a per-machine preference. Also note that if the "encrypted"
  1478. // state of the checkbox has changed, we are assured that the user has WRITE
  1479. // access to HKLM.
  1480. //
  1481. HRESULT hr = keyLM.SetValue(REGSTR_VAL_ENCRYPTCACHE, DWORD(pgstNow.GetEncrypted()));
  1482. if (FAILED(hr))
  1483. {
  1484. Trace((TEXT("Error 0x%08X setting reg value \"%s\""), hr, REGSTR_VAL_ENCRYPTCACHE));
  1485. }
  1486. }
  1487. return hr;
  1488. }
  1489. //
  1490. // Structure for communicating between the Prop Sheet code
  1491. // and the CSC progress callbacks.
  1492. //
  1493. struct ENCRYPT_PROGRESS_INFO
  1494. {
  1495. HINSTANCE hInstance; // Module containing UI text strings.
  1496. HWND hwndParentDefault; // Default parent window for error UI.
  1497. IProgressDialog *pDlg; // Progress dialog.
  1498. int cFilesTotal; // Total files to be processed.
  1499. int cFilesProcessed; // Running count of files processed.
  1500. bool bUserCancelled; // User cancelled operation?
  1501. bool bPropSheetClosing; // User pressed "OK" so the prop sheet is closing.
  1502. };
  1503. //
  1504. // Organize the parameters passed from a CSC callback function
  1505. // into a single structure. Note that not all the callback
  1506. // parameters are used here. I've commented out the ones that
  1507. // aren't. If they're needed later, uncomment them and
  1508. // fill in the value in EncryptDecryptCallback().
  1509. //
  1510. struct CSC_CALLBACK_DATA
  1511. {
  1512. LPCWSTR lpszName;
  1513. DWORD dwReason;
  1514. DWORD dwParam1;
  1515. DWORD dwParam2;
  1516. DWORD_PTR dwContext;
  1517. /* ----- Unused ------
  1518. DWORD dwStatus;
  1519. DWORD dwHintFlags;
  1520. DWORD dwPinCount;
  1521. WIN32_FIND_DATAW *pFind32;
  1522. */
  1523. };
  1524. //
  1525. // Helper to get the HWND of the progress dialog
  1526. // from the progress info block.
  1527. //
  1528. HWND
  1529. ParentWindowFromProgressInfo(
  1530. const ENCRYPT_PROGRESS_INFO &epi
  1531. )
  1532. {
  1533. const HWND hwndParent = GetProgressDialogWindow(epi.pDlg);
  1534. if (NULL != hwndParent)
  1535. return hwndParent;
  1536. return epi.hwndParentDefault;
  1537. }
  1538. //
  1539. // The progress dialog lower's the priority class of it's thread to
  1540. // THREAD_PRIORITY_BELOW_NORMAL so that it doesn't compete with the
  1541. // thread doing the real work. Unfortunately, with this encryption
  1542. // stuff this resulted in the dialog being starved of CPU time so that
  1543. // it didn't appear in some cases. To ensure proper operation of the
  1544. // progress dialog we promote it's priority back to
  1545. // THREAD_PRIORITY_NORMAL. This function is the helper to do this.
  1546. //
  1547. BOOL
  1548. SetProgressThreadPriority(
  1549. IProgressDialog *pDlg,
  1550. int iPriority,
  1551. int *piPrevPriority
  1552. )
  1553. {
  1554. BOOL bResult = FALSE;
  1555. //
  1556. // Get the thread handle for the progress dialog.
  1557. //
  1558. const DWORD dwThreadId = GetWindowThreadProcessId(GetProgressDialogWindow(pDlg), NULL);
  1559. const HANDLE hThread = OpenThread(THREAD_SET_INFORMATION, FALSE, dwThreadId);
  1560. if (NULL != hThread)
  1561. {
  1562. //
  1563. // Set the thread's priority. Return the previous thread
  1564. // priority if the caller requests it.
  1565. //
  1566. const int iPrevPriority = GetThreadPriority(hThread);
  1567. if (SetThreadPriority(hThread, iPriority))
  1568. {
  1569. if (NULL != piPrevPriority)
  1570. {
  1571. *piPrevPriority = iPrevPriority;
  1572. }
  1573. bResult = TRUE;
  1574. }
  1575. CloseHandle(hThread);
  1576. }
  1577. return bResult;
  1578. }
  1579. //
  1580. // Handler for Encryption CSCPROC_REASON_BEGIN
  1581. //
  1582. DWORD
  1583. EncryptDecrypt_BeginHandler(
  1584. const CSC_CALLBACK_DATA& cbd,
  1585. bool bEncrypting
  1586. )
  1587. {
  1588. //
  1589. // Nothing to do on "begin".
  1590. //
  1591. return CSCPROC_RETURN_CONTINUE;
  1592. }
  1593. //
  1594. // Handler for Encryption CSCPROC_REASON_MORE_DATA
  1595. //
  1596. // Returns:
  1597. // CSCPROC_RETURN_CONTINUE == Success. Continue!
  1598. // CSCPROC_RETURN_ABORT == User cancelled.
  1599. // CSCPROC_RETURN_RETRY == An error occured and user says "retry".
  1600. //
  1601. DWORD
  1602. EncryptDecrypt_MoreDataHandler(
  1603. const CSC_CALLBACK_DATA& cbd,
  1604. bool bEncrypting
  1605. )
  1606. {
  1607. const TCHAR szNull[] = TEXT("");
  1608. const DWORD dwError = cbd.dwParam2;
  1609. ENCRYPT_PROGRESS_INFO * const pepi = (ENCRYPT_PROGRESS_INFO *)cbd.dwContext;
  1610. DWORD dwResult = CSCPROC_RETURN_CONTINUE;
  1611. LPCTSTR pszName = cbd.lpszName ? cbd.lpszName : szNull;
  1612. //
  1613. // Update the progress UI with the file name and % processed.
  1614. //
  1615. pepi->pDlg->SetLine(2, pszName, TRUE, NULL);
  1616. pepi->pDlg->SetProgress(++(pepi->cFilesProcessed), pepi->cFilesTotal);
  1617. //
  1618. // Handle any errors.
  1619. //
  1620. if (ERROR_SUCCESS != dwError)
  1621. {
  1622. //
  1623. // The formatting of this message is as follows (encryption version shown):
  1624. //
  1625. // -----------------------------------------------------
  1626. // Offline Files
  1627. // -----------------------------------------------------
  1628. //
  1629. // Error encrypting offline copy of 'filename'.
  1630. //
  1631. // < error description >
  1632. //
  1633. // [Cancel][Try Again][Continue]
  1634. //
  1635. //
  1636. TCHAR szPath[MAX_PATH];
  1637. ::PathCompactPathEx(szPath, pszName, 50, 0); // Compact to 50 chars max.
  1638. LPTSTR pszError;
  1639. if (0 < FormatSystemError(&pszError, dwError))
  1640. {
  1641. INT iResponse;
  1642. if (ERROR_SHARING_VIOLATION == dwError)
  1643. {
  1644. //
  1645. // "File is open" is so common we special-case it to provide a bit more
  1646. // instruction to the user.
  1647. //
  1648. iResponse = CscMessageBox(ParentWindowFromProgressInfo(*pepi),
  1649. MB_ICONWARNING | MB_CANCELTRYCONTINUE,
  1650. pepi->hInstance,
  1651. bEncrypting ? IDS_ERR_FMT_ENCRYPTFILE_INUSE : IDS_ERR_FMT_DECRYPTFILE_INUSE,
  1652. szPath);
  1653. }
  1654. else
  1655. {
  1656. //
  1657. // Handle all other errors. This embeds the error text from winerror
  1658. // into the message.
  1659. //
  1660. iResponse = CscMessageBox(ParentWindowFromProgressInfo(*pepi),
  1661. MB_ICONWARNING | MB_CANCELTRYCONTINUE,
  1662. pepi->hInstance,
  1663. bEncrypting ? IDS_ERR_FMT_ENCRYPTFILE : IDS_ERR_FMT_DECRYPTFILE,
  1664. szPath,
  1665. pszError);
  1666. }
  1667. LocalFree(pszError);
  1668. switch(iResponse)
  1669. {
  1670. case IDCANCEL:
  1671. dwResult = CSCPROC_RETURN_ABORT;
  1672. break;
  1673. case IDTRYAGAIN:
  1674. //
  1675. // Take one file away from the progress total.
  1676. //
  1677. pepi->cFilesProcessed--;
  1678. dwResult = CSCPROC_RETURN_RETRY;
  1679. break;
  1680. case IDCONTINUE:
  1681. //
  1682. // Fall through...
  1683. //
  1684. default:
  1685. //
  1686. // Continue processing.
  1687. //
  1688. break;
  1689. }
  1690. }
  1691. }
  1692. return dwResult;
  1693. }
  1694. //
  1695. // Handler for Encryption CSCPROC_REASON_END
  1696. //
  1697. // Returns:
  1698. // CSCPROC_RETURN_CONTINUE
  1699. //
  1700. DWORD
  1701. EncryptDecrypt_EndHandler(
  1702. const CSC_CALLBACK_DATA& cbd,
  1703. bool bEncrypting
  1704. )
  1705. {
  1706. const DWORD fCompleted = cbd.dwParam1;
  1707. const DWORD dwError = cbd.dwParam2;
  1708. ENCRYPT_PROGRESS_INFO * const pepi = (ENCRYPT_PROGRESS_INFO *)cbd.dwContext;
  1709. //
  1710. // Advance progress to 100% and stop progress dialog.
  1711. //
  1712. pepi->pDlg->SetProgress(pepi->cFilesTotal, pepi->cFilesTotal);
  1713. pepi->pDlg->StopProgressDialog();
  1714. //
  1715. // Handle any errors.
  1716. //
  1717. if (!fCompleted)
  1718. {
  1719. //
  1720. // CSC says it did not complete the encryption/decryption process
  1721. // without errors.
  1722. //
  1723. if (ERROR_SUCCESS != dwError)
  1724. {
  1725. //
  1726. // This path is taken if some error occured outside of the
  1727. // file-processing code (i.e. opening the CSC database,
  1728. // recording encryption state flags in the database etc).
  1729. //
  1730. // -----------------------------------------------------
  1731. // Offline Files
  1732. // -----------------------------------------------------
  1733. //
  1734. // Error encrypting offline files.
  1735. //
  1736. // < error-specific text >
  1737. // [OK]
  1738. //
  1739. // Note that we're at the end of the operation so there's no
  1740. // reason to offer "Cancel" as a user choice.
  1741. //
  1742. LPTSTR pszError;
  1743. if (0 < FormatSystemError(&pszError, dwError))
  1744. {
  1745. CscMessageBox(ParentWindowFromProgressInfo(*pepi),
  1746. MB_ICONERROR | MB_OK,
  1747. pepi->hInstance,
  1748. bEncrypting ? IDS_ERR_FMT_ENCRYPTCSC : IDS_ERR_FMT_DECRYPTCSC,
  1749. pszError);
  1750. LocalFree(pszError);
  1751. }
  1752. }
  1753. else
  1754. {
  1755. //
  1756. // This path is taken if one or more errors were reported
  1757. // in the "more data" callback. In this case the error was
  1758. // already reported so we do nothing.
  1759. //
  1760. }
  1761. }
  1762. return CSCPROC_RETURN_CONTINUE; // Note: CSC ignores return value on CSCPROC_REASON_END.
  1763. }
  1764. //
  1765. // Encryption/Decryption callback from CSC.
  1766. //
  1767. // dwReason dwParam1 dwParam2
  1768. // ------------------------- ------------------ --------------------------
  1769. // CSCPROC_REASON_BEGIN 1 == Encrypting 0
  1770. // CSCPROC_REASON_MORE_DATA 0 Win32 error code
  1771. // CSCPROC_REASON_END 1 == Completed dwParam1 == 1 ? 0
  1772. // dwParam1 == 0 ? GetLastError()
  1773. //
  1774. DWORD CALLBACK
  1775. COfflineFilesPage::EncryptDecryptCallback(
  1776. LPCWSTR lpszName,
  1777. DWORD dwStatus,
  1778. DWORD dwHintFlags,
  1779. DWORD dwPinCount,
  1780. WIN32_FIND_DATAW *pFind32,
  1781. DWORD dwReason,
  1782. DWORD dwParam1,
  1783. DWORD dwParam2,
  1784. DWORD_PTR dwContext
  1785. )
  1786. {
  1787. static bool bEncrypting;
  1788. ENCRYPT_PROGRESS_INFO * const pepi = (ENCRYPT_PROGRESS_INFO *)dwContext;
  1789. if (pepi->bUserCancelled)
  1790. {
  1791. //
  1792. // If user has already cancelled on a previous callback
  1793. // there's no reason to process this callback. Just return
  1794. // "abort" to CSC.
  1795. //
  1796. return CSCPROC_RETURN_ABORT;
  1797. }
  1798. DWORD dwResult = CSCPROC_RETURN_CONTINUE;
  1799. //
  1800. // Package callback data into a struct we can pass to the
  1801. // handler functions. Yeah, it's a bit more expensive but
  1802. // handling the various "reasons" in separate functions sure makes
  1803. // for cleaner code than if they're all handled in a big switch
  1804. // statement.
  1805. //
  1806. CSC_CALLBACK_DATA cbd;
  1807. cbd.lpszName = lpszName;
  1808. cbd.dwReason = dwReason;
  1809. cbd.dwParam1 = dwParam1;
  1810. cbd.dwParam2 = dwParam2;
  1811. cbd.dwContext = dwContext;
  1812. //
  1813. // Call the appropriate callback reason handler.
  1814. //
  1815. switch(dwReason)
  1816. {
  1817. case CSCPROC_REASON_BEGIN:
  1818. bEncrypting = boolify(dwParam1);
  1819. dwResult = EncryptDecrypt_BeginHandler(cbd, bEncrypting);
  1820. break;
  1821. case CSCPROC_REASON_MORE_DATA:
  1822. dwResult = EncryptDecrypt_MoreDataHandler(cbd, bEncrypting);
  1823. break;
  1824. case CSCPROC_REASON_END:
  1825. dwResult = EncryptDecrypt_EndHandler(cbd, bEncrypting);
  1826. break;
  1827. default:
  1828. break;
  1829. }
  1830. //
  1831. // Detect if user has cancelled the operation in response to
  1832. // an error message or directly in the progress dialog.
  1833. //
  1834. if (CSCPROC_RETURN_ABORT == dwResult || (!pepi->bUserCancelled && pepi->pDlg->HasUserCancelled()))
  1835. {
  1836. pepi->bUserCancelled = true;
  1837. dwResult = CSCPROC_RETURN_ABORT;
  1838. }
  1839. if (pepi->bUserCancelled && pepi->bPropSheetClosing)
  1840. {
  1841. //
  1842. // Only display this cautionary message if the user has
  1843. // clicked the "OK" button. If they clicked "Apply" the prop sheet
  1844. // remains active and we'll display the encryption warning tooltip
  1845. // balloon on the page itself. If they clicked "OK" the prop sheet
  1846. // is going away so the tooltip will not be presented to the user.
  1847. // Either way we need to let the user know that cancelling
  1848. // encryption or decryption leaves the cache in a partial state.
  1849. //
  1850. // -----------------------------------------------------
  1851. // Offline Files
  1852. // -----------------------------------------------------
  1853. //
  1854. // Encryption of Offline Files is enabled but
  1855. // not all files have been encrypted.
  1856. //
  1857. //
  1858. const UINT ids = bEncrypting ? IDS_ENCRYPTCSC_CANCELLED : IDS_DECRYPTCSC_CANCELLED;
  1859. const UINT uType = MB_OK | (bEncrypting ? MB_ICONWARNING : MB_ICONINFORMATION);
  1860. CscMessageBox(ParentWindowFromProgressInfo(*pepi),
  1861. uType,
  1862. pepi->hInstance,
  1863. ids);
  1864. }
  1865. return dwResult;
  1866. }
  1867. //
  1868. // Encrypt or Decrypt the cache.
  1869. //
  1870. void
  1871. COfflineFilesPage::EncryptOrDecryptCache(
  1872. bool bEncrypt,
  1873. bool bPropSheetClosing
  1874. )
  1875. {
  1876. HANDLE hMutex = RequestPermissionToEncryptCache();
  1877. if (NULL != hMutex)
  1878. {
  1879. //
  1880. // Excellent. No one (i.e. policy) is trying to encrypt/decrypt
  1881. // the cache. We're in business.
  1882. //
  1883. CMutexAutoRelease mutex_auto_release(hMutex); // Ensure release of mutex.
  1884. IProgressDialog *ppd;
  1885. if (SUCCEEDED(CoCreateInstance(CLSID_ProgressDialog,
  1886. NULL,
  1887. CLSCTX_INPROC_SERVER,
  1888. IID_IProgressDialog,
  1889. (void **)&ppd)))
  1890. {
  1891. //
  1892. // Set up the progress dialog using the standard "encrypt file"
  1893. // animation. The dialog is modal.
  1894. //
  1895. TCHAR szText[MAX_PATH];
  1896. if (0 < LoadString(m_hInstance, IDS_APPLICATION, szText, ARRAYSIZE(szText)))
  1897. {
  1898. ppd->SetTitle(szText);
  1899. }
  1900. if (0 < LoadString(m_hInstance,
  1901. bEncrypt ? IDS_ENCRYPTING_DOTDOTDOT : IDS_DECRYPTING_DOTDOTDOT,
  1902. szText,
  1903. ARRAYSIZE(szText)))
  1904. {
  1905. ppd->SetLine(1, szText, FALSE, NULL);
  1906. }
  1907. //
  1908. // TODO: [brianau - 3/8/00] Need special encrypting/decrypting AVI.
  1909. //
  1910. ppd->SetAnimation(m_hInstance, bEncrypt ? IDA_FILEENCR : IDA_FILEDECR);
  1911. ppd->StartProgressDialog(m_hwndDlg, NULL, PROGDLG_NOTIME | PROGDLG_MODAL, NULL);
  1912. //
  1913. // Pass this info to the progress callback so we can display UI.
  1914. //
  1915. ENCRYPT_PROGRESS_INFO epi;
  1916. epi.hInstance = m_hInstance;
  1917. epi.hwndParentDefault = m_hwndDlg;
  1918. epi.pDlg = ppd;
  1919. epi.bUserCancelled = false;
  1920. epi.bPropSheetClosing = bPropSheetClosing;
  1921. CSCSPACEUSAGEINFO sui;
  1922. ZeroMemory(&sui, sizeof(sui));
  1923. GetCscSpaceUsageInfo(&sui);
  1924. epi.cFilesTotal = sui.dwNumFilesInCache;
  1925. epi.cFilesProcessed = 0;
  1926. //
  1927. // Boost the progress dialog thread's priority to "normal" priority class.
  1928. // The progress dialog sets it's priority class to "below normal" so it
  1929. // doesn't steal all of the CPU running the animation. However, that also
  1930. // means that the progress dialog doesn't work so well when displaying progress
  1931. // for a higher-priority compute-bound thread like encryption.
  1932. //
  1933. SetProgressThreadPriority(ppd, THREAD_PRIORITY_NORMAL, NULL);
  1934. //
  1935. // Encrypt/Decrypt the cache files. Will provide progress info through
  1936. // the callback EncryptDecryptCallback. Errors are handled in the callback
  1937. // reason handlers.
  1938. //
  1939. CSCEncryptDecryptDatabase(bEncrypt, EncryptDecryptCallback, (DWORD_PTR)&epi);
  1940. ppd->StopProgressDialog();
  1941. ppd->Release();
  1942. }
  1943. }
  1944. else
  1945. {
  1946. //
  1947. // Let the user know an encryption/decryption operation is already
  1948. // in progress for system policy.
  1949. //
  1950. CscMessageBox(m_hwndDlg,
  1951. MB_ICONINFORMATION | MB_OK,
  1952. m_hInstance,
  1953. IDS_ENCRYPTCSC_INPROGFORPOLICY);
  1954. }
  1955. //
  1956. // Make sure the encryption checkbox reflects the encryption state of
  1957. // the cache when we're done. We don't do it if the prop sheet is closing
  1958. // because that may flash the warning tooltip just as the sheet is going
  1959. // away. Looks bad.
  1960. //
  1961. if (!bPropSheetClosing)
  1962. {
  1963. UpdateEncryptionCheckbox();
  1964. UpdateEncryptionTooltipBalloon();
  1965. }
  1966. }
  1967. //
  1968. // Enables or disables CSC according to the bEnable arg.
  1969. //
  1970. // Returns:
  1971. //
  1972. // TRUE == Operation successful (reboot may be required).
  1973. // FALSE == Operation failed. See *pdwError for cause.
  1974. //
  1975. // *pbReboot indicates if a reboot is required.
  1976. // *pdwError returns any error code.
  1977. //
  1978. bool
  1979. COfflineFilesPage::EnableOrDisableCsc(
  1980. bool bEnable,
  1981. bool *pbReboot,
  1982. DWORD *pdwError
  1983. )
  1984. {
  1985. DWORD dwError = ERROR_SUCCESS;
  1986. //
  1987. // We'll assume no reboot required.
  1988. //
  1989. if (NULL != pbReboot)
  1990. *pbReboot = false;
  1991. if (!CSCDoEnableDisable(bEnable))
  1992. {
  1993. //
  1994. // Tried to enable or disable but failed.
  1995. // If enabling, it's just a failure and we return.
  1996. // If disabling, it may have failed because there are open files.
  1997. //
  1998. dwError = GetLastError();
  1999. if (!bEnable && ERROR_BUSY == dwError)
  2000. {
  2001. //
  2002. // Failed to disable and there are open files.
  2003. // Tell the user to close all open files then try again.
  2004. //
  2005. CscMessageBox(m_hwndDlg,
  2006. MB_ICONINFORMATION | MB_OK,
  2007. m_hInstance,
  2008. IDS_OPENFILESONDISABLE);
  2009. if (!CSCDoEnableDisable(bEnable))
  2010. {
  2011. dwError = GetLastError();
  2012. if (ERROR_BUSY == dwError)
  2013. {
  2014. //
  2015. // Still can't disable CSC because there are open files.
  2016. // This means we'll have to reboot.
  2017. //
  2018. if (NULL != pbReboot)
  2019. *pbReboot = true;
  2020. }
  2021. }
  2022. }
  2023. }
  2024. //
  2025. // Return error code to caller.
  2026. //
  2027. if (NULL != *pdwError)
  2028. *pdwError = dwError;
  2029. return ERROR_SUCCESS == dwError || ERROR_BUSY == dwError;
  2030. }
  2031. //
  2032. // UI info passed to PurgeCache then returned to PurgeCacheCallback.
  2033. //
  2034. struct PROGRESS_UI_INFO
  2035. {
  2036. HINSTANCE hInstance; // Module containing UI text strings.
  2037. HWND hwndParent; // Parent window for error dialog.
  2038. IProgressDialog *pDlg; // Progress dialog.
  2039. };
  2040. //
  2041. // This callback updates the progress UI for deleting cached items.
  2042. //
  2043. //
  2044. BOOL
  2045. COfflineFilesPage::PurgeCacheCallback(
  2046. CCachePurger *pPurger
  2047. )
  2048. {
  2049. BOOL bContinue = TRUE;
  2050. PROGRESS_UI_INFO *ppui = (PROGRESS_UI_INFO *)pPurger->CallbackData();
  2051. IProgressDialog *pDlg = ppui->pDlg;
  2052. const DWORD dwPhase = pPurger->Phase();
  2053. //
  2054. // Adjust dialog appearance at start of each phase.
  2055. //
  2056. if (0 == pPurger->FileOrdinal())
  2057. {
  2058. TCHAR szText[MAX_PATH];
  2059. if (0 < LoadString(ppui->hInstance,
  2060. PURGE_PHASE_SCAN == dwPhase ? IDS_SCANNING_DOTDOTDOT : IDS_DELETING_DOTDOTDOT,
  2061. szText,
  2062. ARRAYSIZE(szText)))
  2063. {
  2064. pDlg->SetLine(2, szText, FALSE, NULL);
  2065. }
  2066. //
  2067. // We don't start registering progress until the "delete" phase.
  2068. // To keep this code simple we just set the progress bar at 0% at the beginning
  2069. // of each phase. This way it will be 0% throughout the scanning phase and then
  2070. // during the delete phase we'll increment it. The scanning phase is very fast.
  2071. //
  2072. pDlg->SetProgress(0, 100);
  2073. }
  2074. if (PURGE_PHASE_SCAN == dwPhase)
  2075. {
  2076. //
  2077. // Do nothing. We've already set the "Scanning..." text above at the
  2078. // start of the phase.
  2079. //
  2080. }
  2081. else if (PURGE_PHASE_DELETE == dwPhase)
  2082. {
  2083. DWORD dwResult = pPurger->FileDeleteResult();
  2084. //
  2085. // Divide each value by 1,000 so that our numbers aren't so large. This
  2086. // means that if you're deleting less than 1,000 bytes of files, progress won't register.
  2087. // I don't think that's a very likely scenario. The DWORD() casts are required because
  2088. // IProgressDialog::SetProgress only accepts DWORDs. Dividing by 1,000 makes the
  2089. // likelihood of DWORD overflow very low. To overflow the DWORD you'd need to be deleting
  2090. // 4.294 e12 bytes from the cache. The current limit on cache size is 4GB so that's
  2091. // not going to happen in Win2000.
  2092. //
  2093. pDlg->SetProgress(DWORD(pPurger->BytesDeleted() / 1000), DWORD(pPurger->BytesToDelete() / 1000));
  2094. if (ERROR_SUCCESS != dwResult)
  2095. {
  2096. //
  2097. // The purger won't call us for directory entries. Only files.
  2098. //
  2099. INT iUserResponse = IDOK;
  2100. if (ERROR_BUSY == dwResult)
  2101. {
  2102. //
  2103. // Special case for ERROR_BUSY. This means that the
  2104. // file is currently open for use by some process.
  2105. // Most likely the network redirector. I don't like the
  2106. // standard text for ERROR_BUSY from winerror.
  2107. //
  2108. iUserResponse = CscMessageBox(ppui->hwndParent,
  2109. MB_OKCANCEL | MB_ICONERROR,
  2110. ppui->hInstance,
  2111. IDS_FMT_ERR_DELFROMCACHE_BUSY,
  2112. pPurger->FileName());
  2113. }
  2114. else
  2115. {
  2116. iUserResponse = CscMessageBox(ppui->hwndParent,
  2117. MB_OKCANCEL | MB_ICONERROR,
  2118. Win32Error(dwResult),
  2119. ppui->hInstance,
  2120. IDS_FMT_ERR_DELFROMCACHE,
  2121. pPurger->FileName());
  2122. }
  2123. if (IDCANCEL == iUserResponse)
  2124. {
  2125. bContinue = FALSE; // User cancelled through error dialog.
  2126. }
  2127. }
  2128. }
  2129. if (pDlg->HasUserCancelled())
  2130. bContinue = FALSE; // User cancelled through progress dialog.
  2131. return bContinue;
  2132. }
  2133. //
  2134. // This feature has been included for use by PSS when there's no other
  2135. // way of fixing CSC operation. Note this is only a last resort.
  2136. // It will wipe out all the contents of the CSC cache including the
  2137. // notion of which files are pinned. It does not affect any files
  2138. // on the network. It does require a system reboot. Again, use only
  2139. // as a last resort when CSC cache corruption is suspected.
  2140. //
  2141. void
  2142. COfflineFilesPage::OnFormatCache(
  2143. void
  2144. )
  2145. {
  2146. if (IDYES == CscMessageBox(m_hwndDlg,
  2147. MB_YESNO | MB_ICONWARNING,
  2148. m_hInstance,
  2149. IDS_REFORMAT_CACHE))
  2150. {
  2151. RegKey key(HKEY_LOCAL_MACHINE, REGSTR_KEY_OFFLINEFILES);
  2152. HRESULT hr = key.Open(KEY_WRITE, true);
  2153. if (SUCCEEDED(hr))
  2154. {
  2155. hr = key.SetValue(REGSTR_VAL_FORMATCSCDB, 1);
  2156. if (SUCCEEDED(hr))
  2157. {
  2158. //
  2159. // Tell prop sheet to return ID_PSREBOOTSYSTEM from PropertySheet().
  2160. // Caller of PropertySheet is responsible for prompting user if they
  2161. // want to reboot now or not.
  2162. //
  2163. PropSheet_RebootSystem(GetParent(m_hwndDlg));
  2164. }
  2165. }
  2166. if (FAILED(hr))
  2167. {
  2168. Trace((TEXT("Format failed with error %d"), HRESULT_CODE(hr)));
  2169. CscWin32Message(m_hwndDlg, HRESULT_CODE(hr), CSCUI::SEV_ERROR);
  2170. }
  2171. }
  2172. }
  2173. //
  2174. // Invoked when user selects "Delete Files..." button in the CSC
  2175. // options dialog.
  2176. //
  2177. void
  2178. COfflineFilesPage::OnDeleteCache(
  2179. void
  2180. )
  2181. {
  2182. //
  2183. // Ask the user if they want to delete only temporary files
  2184. // from the cache or both temp and pinned files. Also gives
  2185. // them an opportunity to cancel before beginning the deletion
  2186. // operation.
  2187. //
  2188. CCachePurgerSel sel;
  2189. CCachePurger::AskUserWhatToPurge(m_hwndDlg, &sel);
  2190. if (PURGE_FLAG_NONE != sel.Flags())
  2191. {
  2192. CCoInit coinit;
  2193. if (SUCCEEDED(coinit.Result()))
  2194. {
  2195. //
  2196. // Use the shell's standard progress dialog.
  2197. //
  2198. IProgressDialog *ppd;
  2199. if (SUCCEEDED(CoCreateInstance(CLSID_ProgressDialog,
  2200. NULL,
  2201. CLSCTX_INPROC_SERVER,
  2202. IID_IProgressDialog,
  2203. (void **)&ppd)))
  2204. {
  2205. //
  2206. // Set up the progress dialog using the standard shell "file delete"
  2207. // animation (the one without the recycle bin). The dialog
  2208. // is modal.
  2209. //
  2210. TCHAR szText[MAX_PATH];
  2211. if (0 < LoadString(m_hInstance, IDS_APPLICATION, szText, ARRAYSIZE(szText)))
  2212. {
  2213. ppd->SetTitle(szText);
  2214. }
  2215. ppd->SetAnimation(m_hInstance, IDA_FILEDEL);
  2216. ppd->StartProgressDialog(m_hwndDlg, NULL, PROGDLG_AUTOTIME | PROGDLG_MODAL, NULL);
  2217. //
  2218. // Pass this info to the progress callback so we can display UI.
  2219. //
  2220. PROGRESS_UI_INFO pui;
  2221. pui.hInstance = m_hInstance;
  2222. pui.hwndParent = m_hwndDlg;
  2223. pui.pDlg = ppd;
  2224. //
  2225. // Purge the cache files. Will provide progress info through
  2226. // the callback PurgeCacheCallback.
  2227. //
  2228. CCachePurger purger(sel, PurgeCacheCallback, &pui);
  2229. purger.Scan();
  2230. purger.Delete();
  2231. ppd->StopProgressDialog();
  2232. //
  2233. // Display message to user.
  2234. // "Deleted 10 files (2.5 MB)."
  2235. //
  2236. FileSize fs(purger.BytesDeleted());
  2237. fs.GetString(szText, ARRAYSIZE(szText));
  2238. if (0 < purger.FilesDeleted())
  2239. {
  2240. CscMessageBox(m_hwndDlg,
  2241. MB_OK | MB_ICONINFORMATION,
  2242. m_hInstance,
  2243. 1 == purger.FilesDeleted() ? IDS_FMT_DELCACHE_FILEDELETED :
  2244. IDS_FMT_DELCACHE_FILESDELETED,
  2245. purger.FilesDeleted(),
  2246. szText);
  2247. }
  2248. else
  2249. {
  2250. CscMessageBox(m_hwndDlg,
  2251. MB_OK | MB_ICONINFORMATION,
  2252. m_hInstance,
  2253. IDS_DELCACHE_NOFILESDELETED);
  2254. }
  2255. ppd->Release();
  2256. }
  2257. }
  2258. }
  2259. }
  2260. //
  2261. // Determine if there's a shortcut to the offline files folder
  2262. // on the desktop.
  2263. //
  2264. bool
  2265. COfflineFilesPage::IsLinkOnDesktop(
  2266. LPTSTR pszPathOut,
  2267. UINT cchPathOut
  2268. )
  2269. {
  2270. return S_OK == COfflineFilesFolder::IsLinkOnDesktop(m_hwndDlg, pszPathOut, cchPathOut);
  2271. }
  2272. //
  2273. // This function checks to see if CSC is compatible with Windows Terminal
  2274. // Server. If it is not, it hides all of the dialog's normal controls
  2275. // and replaces them with a block of text explaining the current mode
  2276. // of Terminal Server and that the user needs to reconfigure Terminal
  2277. // Server in order to use CSC.
  2278. //
  2279. // Returns:
  2280. // true - Dialog controls have been disabled.
  2281. // false - Dialog controls not disabled. CSC is compatible with TS mode.
  2282. //
  2283. bool
  2284. COfflineFilesPage::DisableForTerminalServer(
  2285. void
  2286. )
  2287. {
  2288. bool bDisabled = false;
  2289. DWORD dwTsMode;
  2290. HRESULT hr = CSCUIIsTerminalServerCompatibleWithCSC(&dwTsMode);
  2291. if (S_FALSE == hr)
  2292. {
  2293. RECT rcCbxEnable;
  2294. RECT rcBtnAdvanced;
  2295. RECT rcDlg;
  2296. RECT rcText;
  2297. //
  2298. // Base the size and position of the text rectangle off
  2299. // of existing controls in the dialog.
  2300. //
  2301. // ISSUE-2001/1/22-brianau Any bi-di issues here?
  2302. //
  2303. GetWindowRect(GetDlgItem(m_hwndDlg, IDC_CBX_ENABLE_CSC), &rcCbxEnable);
  2304. GetWindowRect(GetDlgItem(m_hwndDlg, IDC_BTN_ADVANCED), &rcBtnAdvanced);
  2305. GetWindowRect(m_hwndDlg, &rcDlg);
  2306. rcText.left = rcCbxEnable.left - rcDlg.left;
  2307. rcText.top = rcCbxEnable.top - rcDlg.top;
  2308. rcText.right = rcBtnAdvanced.right - rcDlg.left;
  2309. rcText.bottom = rcBtnAdvanced.bottom - rcDlg.top;
  2310. //
  2311. // Create a static text control for the text block.
  2312. //
  2313. HWND hwndText = CreateWindow(TEXT("static"),
  2314. TEXT(""),
  2315. WS_CHILD | WS_VISIBLE,
  2316. rcText.left,
  2317. rcText.top,
  2318. rcText.right - rcText.left,
  2319. rcText.bottom - rcText.top,
  2320. m_hwndDlg,
  2321. NULL,
  2322. NULL,
  2323. NULL);
  2324. if (NULL != hwndText)
  2325. {
  2326. //
  2327. // Load and display the text explaining what the user needs
  2328. // to do to enable CSC.
  2329. //
  2330. LPTSTR pszText;
  2331. if (SUCCEEDED(TS_GetIncompatibilityReasonText(dwTsMode, &pszText)))
  2332. {
  2333. HFONT hFont = (HFONT)SendMessage(m_hwndDlg, WM_GETFONT, 0, 0);
  2334. SendMessage(hwndText, WM_SETFONT, (WPARAM)hFont, FALSE);
  2335. SetWindowText(hwndText, pszText);
  2336. LocalFree(pszText);
  2337. }
  2338. //
  2339. // Hide all of the controls on the page.
  2340. //
  2341. static const UINT rgCtls[] = {
  2342. IDC_CBX_FULLSYNC_AT_LOGOFF,
  2343. IDC_CBX_FULLSYNC_AT_LOGON,
  2344. IDC_CBX_REMINDERS,
  2345. IDC_CBX_LINK_ON_DESKTOP,
  2346. IDC_CBX_ENCRYPT_CSC,
  2347. IDC_TXT_CACHESIZE_PCT,
  2348. IDC_SLIDER_CACHESIZE_PCT,
  2349. IDC_LBL_CACHESIZE_PCT,
  2350. IDC_BTN_VIEW_CACHE,
  2351. IDC_BTN_ADVANCED,
  2352. IDC_BTN_DELETE_CACHE,
  2353. IDC_TXT_REMINDERS1,
  2354. IDC_EDIT_REMINDERS,
  2355. IDC_SPIN_REMINDERS,
  2356. IDC_CBX_ENABLE_CSC
  2357. };
  2358. for (int iCtl = 0; iCtl < ARRAYSIZE(rgCtls); iCtl++)
  2359. {
  2360. ShowWindow(GetDlgItem(m_hwndDlg, rgCtls[iCtl]), SW_HIDE);
  2361. }
  2362. }
  2363. bDisabled = true;
  2364. }
  2365. return bDisabled;
  2366. }
  2367. //-----------------------------------------------------------------------------
  2368. // CAdvOptDlg
  2369. //-----------------------------------------------------------------------------
  2370. const CAdvOptDlg::CtlActions CAdvOptDlg::m_rgCtlActions[CConfig::eNumOfflineActions] = {
  2371. { IDC_RBN_GOOFFLINE_SILENT, CConfig::eGoOfflineSilent },
  2372. { IDC_RBN_GOOFFLINE_FAIL, CConfig::eGoOfflineFail }
  2373. };
  2374. const DWORD CAdvOptDlg::m_rgHelpIDs[] = {
  2375. IDOK, IDH_OK,
  2376. IDCANCEL, IDH_CANCEL,
  2377. IDC_RBN_GOOFFLINE_SILENT, HIDC_RBN_GOOFFLINE_SILENT,
  2378. IDC_RBN_GOOFFLINE_FAIL, HIDC_RBN_GOOFFLINE_FAIL,
  2379. IDC_GRP_CUSTGOOFFLINE, HIDC_LV_CUSTGOOFFLINE,
  2380. IDC_LV_CUSTGOOFFLINE, HIDC_LV_CUSTGOOFFLINE,
  2381. IDC_BTN_ADD_CUSTGOOFFLINE, HIDC_BTN_ADD_CUSTGOOFFLINE,
  2382. IDC_BTN_EDIT_CUSTGOOFFLINE, HIDC_BTN_EDIT_CUSTGOOFFLINE,
  2383. IDC_BTN_DELETE_CUSTGOOFFLINE, HIDC_BTN_DELETE_CUSTGOOFFLINE,
  2384. IDC_STATIC2, DWORD(-1), // Icon
  2385. IDC_STATIC3, DWORD(-1), // Icon's text
  2386. IDC_STATIC4, DWORD(-1), // Grp box #1
  2387. 0, 0
  2388. };
  2389. int
  2390. CAdvOptDlg::Run(
  2391. void
  2392. )
  2393. {
  2394. int iResult = (int)DialogBoxParam(m_hInstance,
  2395. MAKEINTRESOURCE(IDD_CSC_ADVOPTIONS),
  2396. m_hwndParent,
  2397. DlgProc,
  2398. (LPARAM)this);
  2399. if (-1 == iResult || 0 == iResult)
  2400. {
  2401. Trace((TEXT("Error %d creating CSC advanced options dialog"),
  2402. GetLastError()));
  2403. }
  2404. return iResult;
  2405. }
  2406. INT_PTR CALLBACK
  2407. CAdvOptDlg::DlgProc(
  2408. HWND hDlg,
  2409. UINT message,
  2410. WPARAM wParam,
  2411. LPARAM lParam
  2412. )
  2413. {
  2414. BOOL bResult = FALSE;
  2415. //
  2416. // Retrieve the "this" pointer from the dialog's userdata.
  2417. // It was placed there in OnInitDialog().
  2418. //
  2419. CAdvOptDlg *pThis = (CAdvOptDlg *)GetWindowLongPtr(hDlg, DWLP_USER);
  2420. switch(message)
  2421. {
  2422. case WM_INITDIALOG:
  2423. {
  2424. pThis = reinterpret_cast<CAdvOptDlg *>(lParam);
  2425. SetWindowLongPtr(hDlg, DWLP_USER, (INT_PTR)pThis);
  2426. bResult = pThis->OnInitDialog(hDlg, (HWND)wParam, lParam);
  2427. break;
  2428. }
  2429. case WM_NOTIFY:
  2430. TraceAssert(NULL != pThis);
  2431. bResult = pThis->OnNotify(hDlg, (int)wParam, (LPNMHDR)lParam);
  2432. break;
  2433. case WM_COMMAND:
  2434. TraceAssert(NULL != pThis);
  2435. bResult = pThis->OnCommand(hDlg, HIWORD(wParam), LOWORD(wParam), (HWND)lParam);
  2436. break;
  2437. case WM_HELP:
  2438. TraceAssert(NULL != pThis);
  2439. bResult = pThis->OnHelp(hDlg, (LPHELPINFO)lParam);
  2440. break;
  2441. case WM_CONTEXTMENU:
  2442. TraceAssert(NULL != pThis);
  2443. bResult = pThis->OnContextMenu(wParam, lParam);
  2444. break;
  2445. case WM_DESTROY:
  2446. TraceAssert(NULL != pThis);
  2447. bResult = pThis->OnDestroy(hDlg);
  2448. break;
  2449. default:
  2450. break;
  2451. }
  2452. return bResult;
  2453. }
  2454. BOOL
  2455. CAdvOptDlg::OnInitDialog(
  2456. HWND hwnd,
  2457. HWND hwndFocus,
  2458. LPARAM lInitParam
  2459. )
  2460. {
  2461. CConfig& config = CConfig::GetSingleton();
  2462. m_hwndDlg = hwnd;
  2463. m_hwndLV = GetDlgItem(hwnd, IDC_LV_CUSTGOOFFLINE);
  2464. CreateListColumns(m_hwndLV);
  2465. ListView_SetExtendedListViewStyle(m_hwndLV, LVS_EX_FULLROWSELECT);
  2466. //
  2467. // Set the default go-offline action radio buttons.
  2468. //
  2469. CConfig::OfflineAction action = (CConfig::OfflineAction)config.GoOfflineAction(&m_bNoConfigGoOfflineAction);
  2470. for (int i = 0; i < ARRAYSIZE(m_rgCtlActions); i++)
  2471. {
  2472. CheckDlgButton(hwnd,
  2473. m_rgCtlActions[i].idRbn,
  2474. m_rgCtlActions[i].action == action ? BST_CHECKED : BST_UNCHECKED);
  2475. }
  2476. //
  2477. // Fill the custom go-offline actions listview.
  2478. //
  2479. HDPA hdpaCustomGOA = DPA_Create(4);
  2480. if (NULL != hdpaCustomGOA)
  2481. {
  2482. config.GetCustomGoOfflineActions(hdpaCustomGOA);
  2483. const int cGOA = DPA_GetPtrCount(hdpaCustomGOA);
  2484. for (i = 0; i < cGOA; i++)
  2485. {
  2486. CConfig::CustomGOA *pGOA = (CConfig::CustomGOA *)DPA_GetPtr(hdpaCustomGOA, i);
  2487. if (NULL != pGOA)
  2488. {
  2489. AddGOAToListView(m_hwndLV, i, *pGOA);
  2490. }
  2491. }
  2492. CConfig::ClearCustomGoOfflineActions(hdpaCustomGOA);
  2493. DPA_Destroy(hdpaCustomGOA);
  2494. }
  2495. //
  2496. // Adjust "enabledness" of controls for system policy.
  2497. //
  2498. EnableCtls(m_hwndDlg);
  2499. //
  2500. // Remember the initial page state so we can intelligently apply changes.
  2501. //
  2502. GetPageState(&m_state);
  2503. return TRUE;
  2504. }
  2505. BOOL
  2506. CAdvOptDlg::OnHelp(
  2507. HWND hDlg,
  2508. LPHELPINFO pHelpInfo
  2509. )
  2510. {
  2511. if (HELPINFO_WINDOW == pHelpInfo->iContextType)
  2512. {
  2513. int idCtl = GetDlgCtrlID((HWND)pHelpInfo->hItemHandle);
  2514. WinHelp((HWND)pHelpInfo->hItemHandle,
  2515. UseWindowsHelp(idCtl) ? NULL : c_szHelpFile,
  2516. HELP_WM_HELP,
  2517. (DWORD_PTR)((LPTSTR)m_rgHelpIDs));
  2518. }
  2519. return TRUE;
  2520. }
  2521. void
  2522. CAdvOptDlg::CreateListColumns(
  2523. HWND hwndList
  2524. )
  2525. {
  2526. //
  2527. // Create the header titles.
  2528. //
  2529. TCHAR szServer[40] = {0};
  2530. TCHAR szAction[40] = {0};
  2531. LoadString(m_hInstance, IDS_TITLE_COL_SERVER, szServer, ARRAYSIZE(szServer));
  2532. LoadString(m_hInstance, IDS_TITLE_COL_ACTION, szAction, ARRAYSIZE(szAction));
  2533. RECT rcList;
  2534. GetClientRect(hwndList, &rcList);
  2535. int cxList = rcList.right - rcList.left - GetSystemMetrics(SM_CXVSCROLL);
  2536. #define LVCOLMASK (LVCF_FMT | LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM)
  2537. LV_COLUMN rgCols[] = {
  2538. { LVCOLMASK, LVCFMT_LEFT, (2 * cxList)/3, szServer, 0, iLVSUBITEM_SERVER },
  2539. { LVCOLMASK, LVCFMT_LEFT, (1 * cxList)/3, szAction, 0, iLVSUBITEM_ACTION }
  2540. };
  2541. //
  2542. // Add the columns to the listview.
  2543. //
  2544. for (INT i = 0; i < ARRAYSIZE(rgCols); i++)
  2545. {
  2546. if (-1 == ListView_InsertColumn(hwndList, i, &rgCols[i]))
  2547. {
  2548. Trace((TEXT("CAdvOptDlg::CreateListColumns failed to add column %d"), i));
  2549. }
  2550. }
  2551. }
  2552. int
  2553. CAdvOptDlg::GetFirstSelectedLVItemRect(
  2554. RECT *prc
  2555. )
  2556. {
  2557. int iSel = ListView_GetNextItem(m_hwndLV, -1, LVNI_SELECTED);
  2558. if (-1 != iSel)
  2559. {
  2560. if (ListView_GetItemRect(m_hwndLV, iSel, prc, LVIR_SELECTBOUNDS))
  2561. {
  2562. ClientToScreen(m_hwndLV, (LPPOINT)&prc->left);
  2563. ClientToScreen(m_hwndLV, (LPPOINT)&prc->right);
  2564. return iSel;
  2565. }
  2566. }
  2567. return -1;
  2568. }
  2569. BOOL
  2570. CAdvOptDlg::OnContextMenu(
  2571. WPARAM wParam,
  2572. LPARAM lParam
  2573. )
  2574. {
  2575. HWND hwndItem = (HWND)wParam;
  2576. INT xPos = -1;
  2577. INT yPos = -1;
  2578. INT iHit = -1;
  2579. BOOL bResult = FALSE;
  2580. if (-1 == lParam)
  2581. {
  2582. //
  2583. // Not invoked with mouse click. Probably Shift F10.
  2584. // Pretend mouse was clicked in center of first selected item.
  2585. //
  2586. RECT rc;
  2587. iHit = GetFirstSelectedLVItemRect(&rc);
  2588. if (-1 != iHit)
  2589. {
  2590. xPos = rc.left + ((rc.right - rc.left) / 2);
  2591. yPos = rc.top + ((rc.bottom - rc.top) / 2);
  2592. }
  2593. }
  2594. else
  2595. {
  2596. //
  2597. // Invoked with mouse click. Now find out if a LV item was
  2598. // selected directly.
  2599. //
  2600. xPos = LOWORD(lParam);
  2601. yPos = HIWORD(lParam);
  2602. LVHITTESTINFO hti;
  2603. hti.pt.x = xPos;
  2604. hti.pt.y = yPos;
  2605. hti.flags = LVHT_ONITEM;
  2606. ScreenToClient(m_hwndLV, &hti.pt);
  2607. iHit = (INT)SendMessage(m_hwndLV, LVM_HITTEST, 0, (LPARAM)&hti);
  2608. }
  2609. if (-1 == iHit)
  2610. {
  2611. //
  2612. // LV item not selected directly or Shift-F10 was not pressed.
  2613. // Display "what's this" help for the listview control.
  2614. //
  2615. WinHelp(hwndItem,
  2616. UseWindowsHelp(GetDlgCtrlID(hwndItem)) ? NULL : c_szHelpFile,
  2617. HELP_CONTEXTMENU,
  2618. (DWORD_PTR)((LPTSTR)m_rgHelpIDs));
  2619. }
  2620. else
  2621. {
  2622. //
  2623. // LV item selected directly or Shift F10 pressed. Display context menu for item.
  2624. //
  2625. if (0 < ListView_GetSelectedCount(m_hwndLV) && IsCustomActionListviewEnabled())
  2626. {
  2627. HMENU hMenu = LoadMenu(m_hInstance, MAKEINTRESOURCE(IDR_ADVOPTIONS_CONTEXTMENU));
  2628. if (NULL != hMenu)
  2629. {
  2630. HMENU hmenuTrackPopup = GetSubMenu(hMenu, 0);
  2631. int cSetByPolicy = 0;
  2632. CountSelectedListviewItems(&cSetByPolicy);
  2633. if (0 < cSetByPolicy)
  2634. {
  2635. //
  2636. // Disable menu items if any item in selection was set by policy.
  2637. //
  2638. int cItems = GetMenuItemCount(hmenuTrackPopup);
  2639. for (int i = 0; i < cItems; i++)
  2640. {
  2641. EnableMenuItem(hmenuTrackPopup, i, MF_GRAYED | MF_BYPOSITION);
  2642. }
  2643. }
  2644. else
  2645. {
  2646. //
  2647. // Build a mask indicating which actions are present in the selected
  2648. // listview items.
  2649. //
  2650. int iItem = -1;
  2651. const DWORD fSilent = 0x00000001;
  2652. const DWORD fFail = 0x00000002;
  2653. DWORD fActions = 0;
  2654. CConfig::CustomGOA *pGOA = NULL;
  2655. while(-1 != (iItem = ListView_GetNextItem(m_hwndLV, iItem, LVNI_SELECTED)))
  2656. {
  2657. pGOA = GetListviewObject(m_hwndLV, iItem);
  2658. if (NULL != pGOA)
  2659. {
  2660. TraceAssert(!pGOA->SetByPolicy());
  2661. switch(pGOA->GetAction())
  2662. {
  2663. case CConfig::eGoOfflineSilent: fActions |= fSilent; break;
  2664. case CConfig::eGoOfflineFail: fActions |= fFail; break;
  2665. default: break;
  2666. }
  2667. }
  2668. }
  2669. //
  2670. // Calculate how many bits are set in the action mask.
  2671. // If there's only one action set, we check that item in the menu.
  2672. // Otherwise, we leave them all unchecked to indicate a heterogeneous
  2673. // set.
  2674. //
  2675. int c = 0; // Count of bits set.
  2676. DWORD dw = fActions;
  2677. for (c = 0; 0 != dw; c++)
  2678. dw &= dw - 1;
  2679. //
  2680. // If the selection is homogeneous with respect to the action,
  2681. // check the menu item corresponding to the action. Otherwise
  2682. // leave all items unchecked.
  2683. //
  2684. if (1 == c)
  2685. {
  2686. const struct
  2687. {
  2688. DWORD fMask;
  2689. UINT idCmd;
  2690. } rgCmds[] = { { fSilent, IDM_ACTION_WORKOFFLINE },
  2691. { fFail, IDM_ACTION_FAIL }
  2692. };
  2693. for (int i = 0; i < ARRAYSIZE(rgCmds); i++)
  2694. {
  2695. if ((fActions & rgCmds[i].fMask) == rgCmds[i].fMask)
  2696. {
  2697. CheckMenuRadioItem(hmenuTrackPopup,
  2698. IDM_ACTION_WORKOFFLINE,
  2699. IDM_ACTION_FAIL,
  2700. rgCmds[i].idCmd,
  2701. MF_BYCOMMAND);
  2702. break;
  2703. }
  2704. }
  2705. }
  2706. }
  2707. TrackPopupMenu(hmenuTrackPopup,
  2708. TPM_LEFTALIGN | TPM_RIGHTBUTTON,
  2709. xPos,
  2710. yPos,
  2711. 0,
  2712. GetParent(hwndItem),
  2713. NULL);
  2714. }
  2715. DestroyMenu(hMenu);
  2716. }
  2717. bResult = TRUE;
  2718. }
  2719. return bResult;
  2720. }
  2721. //
  2722. // Return the offline action code associated with the currently-checked
  2723. // offline-action radio button.
  2724. //
  2725. CConfig::OfflineAction
  2726. CAdvOptDlg::GetCurrentGoOfflineAction(
  2727. void
  2728. ) const
  2729. {
  2730. CConfig::OfflineAction action = CConfig::eNumOfflineActions;
  2731. for (int i = 0; i < ARRAYSIZE(m_rgCtlActions); i++)
  2732. {
  2733. if (BST_CHECKED == IsDlgButtonChecked(m_hwndDlg, m_rgCtlActions[i].idRbn))
  2734. {
  2735. action = m_rgCtlActions[i].action;
  2736. break;
  2737. }
  2738. }
  2739. TraceAssert(CConfig::eNumOfflineActions != action);
  2740. return action;
  2741. }
  2742. BOOL
  2743. CAdvOptDlg::OnCommand(
  2744. HWND hwnd,
  2745. WORD wNotifyCode,
  2746. WORD wID,
  2747. HWND hwndCtl
  2748. )
  2749. {
  2750. BOOL bResult = TRUE;
  2751. if (BN_CLICKED == wNotifyCode)
  2752. {
  2753. switch(wID)
  2754. {
  2755. case IDOK:
  2756. ApplySettings();
  2757. //
  2758. // Fall through...
  2759. //
  2760. case IDCANCEL:
  2761. EndDialog(hwnd, wID);
  2762. break;
  2763. case IDC_BTN_ADD_CUSTGOOFFLINE:
  2764. OnAddCustomGoOfflineAction();
  2765. bResult = FALSE;
  2766. break;
  2767. case IDC_BTN_EDIT_CUSTGOOFFLINE:
  2768. OnEditCustomGoOfflineAction();
  2769. bResult = FALSE;
  2770. break;
  2771. case IDC_BTN_DELETE_CUSTGOOFFLINE:
  2772. OnDeleteCustomGoOfflineAction();
  2773. FocusOnSomethingInListview();
  2774. if (0 < ListView_GetItemCount(m_hwndLV))
  2775. SetFocus(GetDlgItem(hwnd, IDC_BTN_DELETE_CUSTGOOFFLINE));
  2776. bResult = FALSE;
  2777. break;
  2778. case IDM_ACTION_WORKOFFLINE:
  2779. case IDM_ACTION_FAIL:
  2780. case IDM_ACTION_DELETE:
  2781. OnContextMenuItemSelected(wID);
  2782. break;
  2783. default:
  2784. break;
  2785. }
  2786. }
  2787. return bResult;
  2788. }
  2789. void
  2790. CAdvOptDlg::ApplySettings(
  2791. void
  2792. )
  2793. {
  2794. //
  2795. // Now store changes from the "Advanced" dialog.
  2796. //
  2797. PgState s;
  2798. GetPageState(&s);
  2799. if (m_state != s)
  2800. {
  2801. RegKey keyCU(HKEY_CURRENT_USER, REGSTR_KEY_OFFLINEFILES);
  2802. HRESULT hr = keyCU.Open(KEY_WRITE, true);
  2803. if (SUCCEEDED(hr))
  2804. {
  2805. hr = keyCU.SetValue(REGSTR_VAL_GOOFFLINEACTION,
  2806. (DWORD)s.GetDefGoOfflineAction());
  2807. if (FAILED(hr))
  2808. {
  2809. Trace((TEXT("Error 0x%08X setting reg value \"%s\""), hr, REGSTR_VAL_GOOFFLINEACTION));
  2810. }
  2811. //
  2812. // Need "query" access because SaveCustomGoOfflineActions needs to
  2813. // delete all of the existing values before saving the new ones.
  2814. //
  2815. RegKey key(keyCU, REGSTR_SUBKEY_CUSTOMGOOFFLINEACTIONS);
  2816. hr = key.Open(KEY_WRITE | KEY_QUERY_VALUE, true);
  2817. if (SUCCEEDED(hr))
  2818. {
  2819. hr = CConfig::SaveCustomGoOfflineActions(key,
  2820. s.GetCustomGoOfflineActions());
  2821. if (FAILED(hr))
  2822. {
  2823. Trace((TEXT("Error 0x%08X setting custom offline actions"), hr));
  2824. }
  2825. }
  2826. }
  2827. else
  2828. {
  2829. Trace((TEXT("Error 0x%08X opening advanced settings user key"), hr));
  2830. }
  2831. }
  2832. }
  2833. void
  2834. CAdvOptDlg::DeleteSelectedListviewItems(
  2835. void
  2836. )
  2837. {
  2838. int iItem = -1;
  2839. CConfig::CustomGOA *pGOA = NULL;
  2840. CAutoSetRedraw autoredraw(m_hwndLV, false);
  2841. while(-1 != (iItem = ListView_GetNextItem(m_hwndLV, -1, LVNI_SELECTED)))
  2842. {
  2843. pGOA = GetListviewObject(m_hwndLV, iItem);
  2844. if (pGOA)
  2845. {
  2846. TraceAssert(!pGOA->SetByPolicy());
  2847. ListView_DeleteItem(m_hwndLV, iItem);
  2848. delete pGOA;
  2849. }
  2850. }
  2851. //
  2852. // If the list is empty, this will disable the "Delete" and
  2853. // "Edit" buttons.
  2854. //
  2855. EnableCtls(m_hwndDlg);
  2856. }
  2857. void
  2858. CAdvOptDlg::SetSelectedListviewItemsAction(
  2859. CConfig::OfflineAction action
  2860. )
  2861. {
  2862. int iItem = -1;
  2863. CConfig::CustomGOA *pGOA = NULL;
  2864. CAutoSetRedraw autoredraw(m_hwndLV, false);
  2865. while(-1 != (iItem = ListView_GetNextItem(m_hwndLV, iItem, LVNI_SELECTED)))
  2866. {
  2867. pGOA = GetListviewObject(m_hwndLV, iItem);
  2868. if (pGOA)
  2869. {
  2870. TraceAssert(!pGOA->SetByPolicy());
  2871. pGOA->SetAction(action);
  2872. ListView_RedrawItems(m_hwndLV, iItem, iItem);
  2873. }
  2874. }
  2875. }
  2876. int
  2877. CAdvOptDlg::CountSelectedListviewItems(
  2878. int *pcSetByPolicy
  2879. )
  2880. {
  2881. TraceAssert(NULL != pcSetByPolicy);
  2882. int iItem = -1;
  2883. int cSelected = 0;
  2884. CConfig::CustomGOA *pGOA = NULL;
  2885. while(-1 != (iItem = ListView_GetNextItem(m_hwndLV, iItem, LVNI_SELECTED)))
  2886. {
  2887. cSelected++;
  2888. pGOA = GetListviewObject(m_hwndLV, iItem);
  2889. if (pGOA && pGOA->SetByPolicy())
  2890. (*pcSetByPolicy)++;
  2891. }
  2892. return cSelected;
  2893. }
  2894. void
  2895. CAdvOptDlg::OnContextMenuItemSelected(
  2896. int idMenuItem
  2897. )
  2898. {
  2899. if (IDM_ACTION_DELETE == idMenuItem)
  2900. {
  2901. DeleteSelectedListviewItems();
  2902. }
  2903. else
  2904. {
  2905. CConfig::OfflineAction action = CConfig::eNumOfflineActions;
  2906. switch(idMenuItem)
  2907. {
  2908. case IDM_ACTION_WORKOFFLINE: action = CConfig::eGoOfflineSilent; break;
  2909. case IDM_ACTION_FAIL: action = CConfig::eGoOfflineFail; break;
  2910. default: break;
  2911. }
  2912. TraceAssert(CConfig::eNumOfflineActions != action);
  2913. SetSelectedListviewItemsAction(action);
  2914. }
  2915. }
  2916. //
  2917. // Responds to the user pressing the "Add..." button.
  2918. //
  2919. void
  2920. CAdvOptDlg::OnAddCustomGoOfflineAction(
  2921. void
  2922. )
  2923. {
  2924. CConfig::OfflineAction action = GetCurrentGoOfflineAction();
  2925. TCHAR szServer[MAX_PATH] = {0};
  2926. bool bDone = false;
  2927. while(!bDone)
  2928. {
  2929. //
  2930. // Run the "Add custom go-offline action" dialog.
  2931. // User enters a server name and selects an action
  2932. // from a set of radio buttons.
  2933. //
  2934. CustomGOAAddDlg dlg(m_hInstance, m_hwndDlg, szServer, ARRAYSIZE(szServer), &action);
  2935. int iResult = dlg.Run();
  2936. if (IDCANCEL == iResult || TEXT('\0') == szServer[0])
  2937. {
  2938. //
  2939. // User cancelled or didn't enter anything.
  2940. //
  2941. bDone = true;
  2942. }
  2943. else
  2944. {
  2945. //
  2946. // User entered a server name. Check if it's already in
  2947. // our list.
  2948. //
  2949. int iItem = -1;
  2950. CConfig::CustomGOA *pObj = FindGOAInListView(m_hwndLV, szServer, &iItem);
  2951. if (NULL != pObj)
  2952. {
  2953. //
  2954. // Already an entry in list for this server.
  2955. // If not set by policy, can replace it if desired.
  2956. // If set by policy, can't change or delete it.
  2957. //
  2958. bool bSetByPolicy = pObj->SetByPolicy();
  2959. DWORD idMsg = bSetByPolicy ? IDS_ERR_GOOFFLINE_DUPACTION_NOCHG : IDS_ERR_GOOFFLINE_DUPACTION;
  2960. DWORD dwFlags = bSetByPolicy ? MB_OK : MB_YESNO;
  2961. if (IDYES == CscMessageBox(m_hwndDlg,
  2962. dwFlags | MB_ICONWARNING,
  2963. m_hInstance,
  2964. idMsg,
  2965. szServer))
  2966. {
  2967. ReplaceCustomGoOfflineAction(pObj, iItem, action);
  2968. bDone = true;
  2969. }
  2970. }
  2971. else
  2972. {
  2973. //
  2974. // Server doesn't already exist in list.
  2975. // Check if it's available on the net.
  2976. //
  2977. CAutoWaitCursor waitcursor;
  2978. DWORD dwNetErr = CheckNetServer(szServer);
  2979. switch(dwNetErr)
  2980. {
  2981. case ERROR_SUCCESS:
  2982. //
  2983. // Server is available. Add the entry to the listview.
  2984. //
  2985. AddCustomGoOfflineAction(szServer, action);
  2986. bDone = true;
  2987. break;
  2988. default:
  2989. {
  2990. LPTSTR pszNetMsg = NULL;
  2991. if (ERROR_EXTENDED_ERROR == dwNetErr)
  2992. {
  2993. const DWORD cchNetMsg = MAX_PATH;
  2994. pszNetMsg = (LPTSTR)LocalAlloc(LPTR, cchNetMsg * sizeof(*pszNetMsg));
  2995. if (NULL != pszNetMsg)
  2996. {
  2997. TCHAR szNetProvider[MAX_PATH];
  2998. WNetGetLastError(&dwNetErr,
  2999. pszNetMsg,
  3000. cchNetMsg,
  3001. szNetProvider,
  3002. ARRAYSIZE(szNetProvider));
  3003. }
  3004. }
  3005. else
  3006. {
  3007. FormatSystemError(&pszNetMsg, dwNetErr);
  3008. }
  3009. if (NULL != pszNetMsg)
  3010. {
  3011. //
  3012. // "The server 'servername' is either invalid
  3013. // or cannot be verified at this time. Add anyway?"
  3014. // [Yes] [No] [Cancel].
  3015. //
  3016. switch(CscMessageBox(m_hwndDlg,
  3017. MB_YESNOCANCEL | MB_ICONWARNING,
  3018. m_hInstance,
  3019. IDS_ERR_INVALIDSERVER,
  3020. szServer,
  3021. pszNetMsg))
  3022. {
  3023. case IDYES:
  3024. AddCustomGoOfflineAction(szServer, action);
  3025. //
  3026. // Fall through...
  3027. //
  3028. case IDCANCEL:
  3029. bDone = true;
  3030. //
  3031. // Fall through...
  3032. //
  3033. case IDNO:
  3034. break;
  3035. }
  3036. LocalFree(pszNetMsg);
  3037. }
  3038. break;
  3039. }
  3040. }
  3041. }
  3042. }
  3043. }
  3044. }
  3045. //
  3046. // Verify a server by going out to the net.
  3047. // Assumes pszServer points to a properly-formatted
  3048. // server name. (i.e. "Server" or "\\Server")
  3049. //
  3050. DWORD
  3051. CAdvOptDlg::CheckNetServer(
  3052. LPCTSTR pszServer
  3053. )
  3054. {
  3055. TraceAssert(NULL != pszServer);
  3056. TCHAR rgchResult[MAX_PATH];
  3057. DWORD cbResult = sizeof(rgchResult);
  3058. LPTSTR pszSystem = NULL;
  3059. //
  3060. // Ensure the server name has a preceding "\\" for the
  3061. // call to WNetGetResourceInformation.
  3062. //
  3063. TCHAR szServer[MAX_PATH];
  3064. while(*pszServer && TEXT('\\') == *pszServer)
  3065. pszServer++;
  3066. wnsprintf(szServer, ARRAYSIZE(szServer), TEXT("\\\\%s"), pszServer);
  3067. NETRESOURCE nr;
  3068. nr.dwScope = RESOURCE_GLOBALNET;
  3069. nr.dwType = RESOURCETYPE_ANY;
  3070. nr.dwDisplayType = 0;
  3071. nr.dwUsage = 0;
  3072. nr.lpLocalName = NULL;
  3073. nr.lpRemoteName = szServer;
  3074. nr.lpComment = NULL;
  3075. nr.lpProvider = NULL;
  3076. return WNetGetResourceInformation(&nr, rgchResult, &cbResult, &pszSystem);
  3077. }
  3078. //
  3079. // Adds a CustomGOA object to the listview.
  3080. //
  3081. void
  3082. CAdvOptDlg::AddCustomGoOfflineAction(
  3083. LPCTSTR pszServer,
  3084. CConfig::OfflineAction action
  3085. )
  3086. {
  3087. AddGOAToListView(m_hwndLV, 0, CConfig::CustomGOA(pszServer, action, false));
  3088. }
  3089. //
  3090. // Replaces the action for an object in the listview.
  3091. //
  3092. void
  3093. CAdvOptDlg::ReplaceCustomGoOfflineAction(
  3094. CConfig::CustomGOA *pGOA,
  3095. int iItem,
  3096. CConfig::OfflineAction action
  3097. )
  3098. {
  3099. pGOA->SetAction(action);
  3100. ListView_RedrawItems(m_hwndLV, iItem, iItem);
  3101. }
  3102. //
  3103. // Create and add a CustomGOA object to the listview.
  3104. //
  3105. int
  3106. CAdvOptDlg::AddGOAToListView(
  3107. HWND hwndLV,
  3108. int iItem,
  3109. const CConfig::CustomGOA& goa
  3110. )
  3111. {
  3112. int iItemResult = -1;
  3113. CConfig::CustomGOA *pGOA = new CConfig::CustomGOA(goa);
  3114. if (NULL != pGOA)
  3115. {
  3116. LVITEM item;
  3117. item.iSubItem = 0;
  3118. item.mask = LVIF_PARAM | LVIF_TEXT;
  3119. item.pszText = LPSTR_TEXTCALLBACK;
  3120. item.iItem = -1 == iItem ? ListView_GetItemCount(hwndLV) : iItem;
  3121. item.lParam = (LPARAM)pGOA;
  3122. iItemResult = ListView_InsertItem(hwndLV, &item);
  3123. if (-1 == iItemResult)
  3124. {
  3125. delete pGOA;
  3126. }
  3127. }
  3128. return iItemResult;
  3129. }
  3130. //
  3131. // Find the matching CustomGOA object in the listview for a given
  3132. // server.
  3133. //
  3134. CConfig::CustomGOA *
  3135. CAdvOptDlg::FindGOAInListView(
  3136. HWND hwndLV,
  3137. LPCTSTR pszServer,
  3138. int *piItem
  3139. )
  3140. {
  3141. int cItems = ListView_GetItemCount(hwndLV);
  3142. for (int iItem = 0; iItem < cItems; iItem++)
  3143. {
  3144. CConfig::CustomGOA *pObj = GetListviewObject(hwndLV, iItem);
  3145. if (pObj)
  3146. {
  3147. if (0 == lstrcmpi(pObj->GetServerName(), pszServer))
  3148. {
  3149. if (piItem)
  3150. *piItem = iItem;
  3151. return pObj;
  3152. }
  3153. }
  3154. }
  3155. return NULL;
  3156. }
  3157. void
  3158. CAdvOptDlg::OnEditCustomGoOfflineAction(
  3159. void
  3160. )
  3161. {
  3162. int cSetByPolicy = 0;
  3163. int cSelected = CountSelectedListviewItems(&cSetByPolicy);
  3164. if (0 < cSelected && 0 == cSetByPolicy)
  3165. {
  3166. TraceAssert(0 == cSetByPolicy);
  3167. CConfig::OfflineAction action = GetCurrentGoOfflineAction();
  3168. TCHAR szServer[MAX_PATH] = {0};
  3169. CConfig::CustomGOA *pGOA = NULL;
  3170. int iItem = -1;
  3171. //
  3172. // At least one selected item wasn't set by policy.
  3173. //
  3174. if (1 == cSelected)
  3175. {
  3176. //
  3177. // Only one item selected so we can display a server name
  3178. // in the dialog and indicate it's currently-set action.
  3179. //
  3180. iItem = ListView_GetNextItem(m_hwndLV, -1, LVNI_SELECTED);
  3181. pGOA = GetListviewObject(m_hwndLV, iItem);
  3182. if (pGOA)
  3183. {
  3184. action = pGOA->GetAction();
  3185. pGOA->GetServerName(szServer, ARRAYSIZE(szServer));
  3186. }
  3187. }
  3188. //
  3189. // Display the "edit" dialog and let the user select a new action.
  3190. //
  3191. CustomGOAEditDlg dlg(m_hInstance, m_hwndDlg, szServer, &action);
  3192. if (IDOK == dlg.Run())
  3193. {
  3194. SetSelectedListviewItemsAction(action);
  3195. }
  3196. }
  3197. }
  3198. void
  3199. CAdvOptDlg::OnDeleteCustomGoOfflineAction(
  3200. void
  3201. )
  3202. {
  3203. int cSetByPolicy = 0;
  3204. int cSelected = CountSelectedListviewItems(&cSetByPolicy);
  3205. if (0 < cSelected)
  3206. {
  3207. DeleteSelectedListviewItems();
  3208. }
  3209. }
  3210. BOOL
  3211. CAdvOptDlg::OnNotify(
  3212. HWND hDlg,
  3213. int idCtl,
  3214. LPNMHDR pnmhdr
  3215. )
  3216. {
  3217. BOOL bResult = TRUE;
  3218. switch(pnmhdr->code)
  3219. {
  3220. case NM_SETFOCUS:
  3221. if (IDC_LV_CUSTGOOFFLINE == idCtl)
  3222. FocusOnSomethingInListview();
  3223. break;
  3224. case LVN_GETDISPINFO:
  3225. OnLVN_GetDispInfo((LV_DISPINFO *)pnmhdr);
  3226. break;
  3227. case LVN_COLUMNCLICK:
  3228. OnLVN_ColumnClick((NM_LISTVIEW *)pnmhdr);
  3229. break;
  3230. case LVN_ITEMCHANGED:
  3231. OnLVN_ItemChanged((NM_LISTVIEW *)pnmhdr);
  3232. break;
  3233. case LVN_ITEMACTIVATE:
  3234. OnEditCustomGoOfflineAction();
  3235. break;
  3236. case LVN_KEYDOWN:
  3237. OnLVN_KeyDown((NMLVKEYDOWN *)pnmhdr);
  3238. break;
  3239. }
  3240. return bResult;
  3241. }
  3242. void
  3243. CAdvOptDlg::FocusOnSomethingInListview(
  3244. void
  3245. )
  3246. {
  3247. //
  3248. // Focus on something.
  3249. //
  3250. int iFocus = ListView_GetNextItem(m_hwndLV, -1, LVNI_FOCUSED);
  3251. if (-1 == iFocus)
  3252. iFocus = 0;
  3253. ListView_SetItemState(m_hwndLV, iFocus, LVIS_FOCUSED | LVIS_SELECTED,
  3254. LVIS_FOCUSED | LVIS_SELECTED);
  3255. }
  3256. int CALLBACK
  3257. CAdvOptDlg::CompareLVItems(
  3258. LPARAM lParam1,
  3259. LPARAM lParam2,
  3260. LPARAM lParamSort
  3261. )
  3262. {
  3263. CAdvOptDlg *pdlg = reinterpret_cast<CAdvOptDlg *>(lParamSort);
  3264. int diff = 0;
  3265. CConfig::CustomGOA *pGOA1 = (CConfig::CustomGOA *)lParam1;
  3266. CConfig::CustomGOA *pGOA2 = (CConfig::CustomGOA *)lParam2;
  3267. TCHAR szText[2][MAX_PATH];
  3268. //
  3269. // This array controls the comparison column IDs used when
  3270. // values for the selected column are equal. These should
  3271. // remain in order of the iLVSUBITEM_xxxxx enumeration with
  3272. // respect to the first element in each row.
  3273. //
  3274. static const int rgColComp[2][2] = {
  3275. { iLVSUBITEM_SERVER, iLVSUBITEM_ACTION },
  3276. { iLVSUBITEM_ACTION, iLVSUBITEM_SERVER }
  3277. };
  3278. int iCompare = 0;
  3279. while(0 == diff && iCompare < ARRAYSIZE(rgColComp))
  3280. {
  3281. switch(rgColComp[pdlg->m_iLastColSorted][iCompare++])
  3282. {
  3283. case iLVSUBITEM_SERVER:
  3284. diff = lstrcmpi(pGOA1->GetServerName(), pGOA2->GetServerName());
  3285. break;
  3286. case iLVSUBITEM_ACTION:
  3287. if (0 < LoadString(pdlg->m_hInstance,
  3288. IDS_GOOFFLINE_ACTION_FIRST + (DWORD)pGOA1->GetAction(),
  3289. szText[0],
  3290. ARRAYSIZE(szText[0])))
  3291. {
  3292. if (0 < LoadString(pdlg->m_hInstance,
  3293. IDS_GOOFFLINE_ACTION_FIRST + (DWORD)pGOA2->GetAction(),
  3294. szText[1],
  3295. ARRAYSIZE(szText[1])))
  3296. {
  3297. diff = lstrcmpi(szText[0], szText[1]);
  3298. }
  3299. }
  3300. break;
  3301. default:
  3302. //
  3303. // If you hit this, you need to update this function
  3304. // to handle the new column you've added to the listview.
  3305. //
  3306. TraceAssert(false);
  3307. break;
  3308. }
  3309. }
  3310. return pdlg->m_bSortAscending ? diff : -1 * diff;
  3311. }
  3312. void
  3313. CAdvOptDlg::OnLVN_ItemChanged(
  3314. NM_LISTVIEW *pnmlv
  3315. )
  3316. {
  3317. static const int rgBtns[] = { IDC_BTN_EDIT_CUSTGOOFFLINE,
  3318. IDC_BTN_DELETE_CUSTGOOFFLINE };
  3319. //
  3320. // LVN_ITEMCHANGED is sent multiple times when you move the
  3321. // highlight bar in a listview.
  3322. // Only run this code when the "focused" state bit is set
  3323. // for the "new state". This should be the last call in
  3324. // the series.
  3325. //
  3326. if (LVIS_FOCUSED & pnmlv->uNewState && IsCustomActionListviewEnabled())
  3327. {
  3328. bool bEnable = 0 < ListView_GetSelectedCount(m_hwndLV);
  3329. if (bEnable)
  3330. {
  3331. //
  3332. // Only enable if all items weren't set by policy.
  3333. //
  3334. int cSetByPolicy = 0;
  3335. CountSelectedListviewItems(&cSetByPolicy);
  3336. bEnable = (0 == cSetByPolicy);
  3337. }
  3338. for (int i = 0; i < ARRAYSIZE(rgBtns); i++)
  3339. {
  3340. HWND hwnd = GetDlgItem(m_hwndDlg, rgBtns[i]);
  3341. if (bEnable != boolify(IsWindowEnabled(hwnd)))
  3342. {
  3343. EnableWindow(hwnd, bEnable);
  3344. }
  3345. }
  3346. }
  3347. }
  3348. void
  3349. CAdvOptDlg::OnLVN_ColumnClick(
  3350. NM_LISTVIEW *pnmlv
  3351. )
  3352. {
  3353. if (m_iLastColSorted != pnmlv->iSubItem)
  3354. {
  3355. m_bSortAscending = true;
  3356. m_iLastColSorted = pnmlv->iSubItem;
  3357. }
  3358. else
  3359. {
  3360. m_bSortAscending = !m_bSortAscending;
  3361. }
  3362. ListView_SortItems(m_hwndLV, CompareLVItems, LPARAM(this));
  3363. }
  3364. void
  3365. CAdvOptDlg::OnLVN_KeyDown(
  3366. NMLVKEYDOWN *plvkd
  3367. )
  3368. {
  3369. if (VK_DELETE == plvkd->wVKey && IsCustomActionListviewEnabled())
  3370. {
  3371. int cSetByPolicy = 0;
  3372. CountSelectedListviewItems(&cSetByPolicy);
  3373. if (0 == cSetByPolicy)
  3374. {
  3375. OnDeleteCustomGoOfflineAction();
  3376. FocusOnSomethingInListview();
  3377. }
  3378. else
  3379. {
  3380. //
  3381. // Provide feedback that deleting things set by policy
  3382. // isn't allowed. Visual feedback is that the "Remove"
  3383. // button and context menu item are disabled. That
  3384. // doesn't help when user hits the [Delete] key.
  3385. //
  3386. MessageBeep(MB_OK);
  3387. }
  3388. }
  3389. }
  3390. void
  3391. CAdvOptDlg::OnLVN_GetDispInfo(
  3392. LV_DISPINFO *plvdi
  3393. )
  3394. {
  3395. static TCHAR szText[MAX_PATH];
  3396. CConfig::CustomGOA* pObj = (CConfig::CustomGOA *)plvdi->item.lParam;
  3397. szText[0] = TEXT('\0');
  3398. if (LVIF_TEXT & plvdi->item.mask)
  3399. {
  3400. switch(plvdi->item.iSubItem)
  3401. {
  3402. case iLVSUBITEM_SERVER:
  3403. if (pObj->SetByPolicy())
  3404. {
  3405. //
  3406. // Format as "server ( policy )"
  3407. //
  3408. TCHAR szFmt[80];
  3409. if (0 < LoadString(m_hInstance,
  3410. IDS_FMT_GOOFFLINE_SERVER_POLICY,
  3411. szFmt,
  3412. ARRAYSIZE(szFmt)))
  3413. {
  3414. wnsprintf(szText, ARRAYSIZE(szText), szFmt, pObj->GetServerName());
  3415. }
  3416. }
  3417. else
  3418. {
  3419. //
  3420. // Just plain 'ol "server".
  3421. //
  3422. pObj->GetServerName(szText, ARRAYSIZE(szText));
  3423. }
  3424. break;
  3425. case iLVSUBITEM_ACTION:
  3426. LoadString(m_hInstance,
  3427. IDS_GOOFFLINE_ACTION_FIRST + (DWORD)pObj->GetAction(),
  3428. szText,
  3429. ARRAYSIZE(szText));
  3430. break;
  3431. default:
  3432. break;
  3433. }
  3434. plvdi->item.pszText = szText;
  3435. }
  3436. if (LVIF_IMAGE & plvdi->item.mask)
  3437. {
  3438. //
  3439. // Not displaying any images. This is just a placeholder.
  3440. // Should be optimized out by compiler.
  3441. //
  3442. }
  3443. }
  3444. CConfig::CustomGOA *
  3445. CAdvOptDlg::GetListviewObject(
  3446. HWND hwndLV,
  3447. int iItem
  3448. )
  3449. {
  3450. LVITEM item;
  3451. item.iItem = iItem;
  3452. item.iSubItem = 0;
  3453. item.mask = LVIF_PARAM;
  3454. if (-1 != ListView_GetItem(hwndLV, &item))
  3455. {
  3456. return (CConfig::CustomGOA *)item.lParam;
  3457. }
  3458. return NULL;
  3459. }
  3460. BOOL
  3461. CAdvOptDlg::OnDestroy(
  3462. HWND hwnd
  3463. )
  3464. {
  3465. if (NULL != m_hwndLV)
  3466. {
  3467. int cItems = ListView_GetItemCount(m_hwndLV);
  3468. for (int i = 0; i < cItems; i++)
  3469. {
  3470. CConfig::CustomGOA *pObj = GetListviewObject(m_hwndLV, i);
  3471. delete pObj;
  3472. }
  3473. ListView_DeleteAllItems(m_hwndLV);
  3474. }
  3475. return FALSE;
  3476. }
  3477. void
  3478. CAdvOptDlg::EnableCtls(
  3479. HWND hwnd
  3480. )
  3481. {
  3482. static const struct
  3483. {
  3484. UINT idCtl;
  3485. bool bRestricted;
  3486. } rgCtls[] = { { IDC_RBN_GOOFFLINE_SILENT, m_bNoConfigGoOfflineAction },
  3487. { IDC_RBN_GOOFFLINE_FAIL, m_bNoConfigGoOfflineAction },
  3488. { IDC_GRP_GOOFFLINE_DEFAULTS, m_bNoConfigGoOfflineAction },
  3489. { IDC_GRP_CUSTGOOFFLINE, false },
  3490. { IDC_BTN_ADD_CUSTGOOFFLINE, false },
  3491. { IDC_BTN_EDIT_CUSTGOOFFLINE, false },
  3492. { IDC_BTN_DELETE_CUSTGOOFFLINE, false }
  3493. };
  3494. //
  3495. // bCscEnabled is always true here. The logic in the options prop page won't
  3496. // let us display the "Advanced" dialog if it isn't.
  3497. //
  3498. bool bCscEnabled = true;
  3499. for (int i = 0; i < ARRAYSIZE(rgCtls); i++)
  3500. {
  3501. bool bEnable = bCscEnabled;
  3502. HWND hwndCtl = GetDlgItem(hwnd, rgCtls[i].idCtl);
  3503. if (bEnable)
  3504. {
  3505. //
  3506. // May be some further policy restrictions.
  3507. //
  3508. if (rgCtls[i].bRestricted)
  3509. bEnable = false;
  3510. if (bEnable)
  3511. {
  3512. if (IDC_BTN_EDIT_CUSTGOOFFLINE == rgCtls[i].idCtl ||
  3513. IDC_BTN_DELETE_CUSTGOOFFLINE == rgCtls[i].idCtl)
  3514. {
  3515. //
  3516. // Only enable "Edit" and "Delete" buttons if there's an active
  3517. // selection in the listview.
  3518. //
  3519. bEnable = (0 < ListView_GetSelectedCount(m_hwndLV));
  3520. }
  3521. }
  3522. }
  3523. if (!bEnable)
  3524. {
  3525. if (GetFocus() == hwndCtl)
  3526. {
  3527. //
  3528. // If disabling a control that has focus, advance the
  3529. // focus to the next control in the tab order before
  3530. // disabling the current control. Otherwise, it will
  3531. // be stuck with focus and tabbing is busted.
  3532. //
  3533. SetFocus(GetNextDlgTabItem(hwnd, hwndCtl, FALSE));
  3534. }
  3535. }
  3536. EnableWindow(GetDlgItem(hwnd, rgCtls[i].idCtl), bEnable);
  3537. }
  3538. }
  3539. void
  3540. CAdvOptDlg::GetPageState(
  3541. PgState *pps
  3542. )
  3543. {
  3544. pps->SetDefGoOfflineAction(GetCurrentGoOfflineAction());
  3545. pps->SetCustomGoOfflineActions(m_hwndLV);
  3546. }
  3547. CAdvOptDlg::PgState::~PgState(
  3548. void
  3549. )
  3550. {
  3551. if (NULL != m_hdpaCustomGoOfflineActions)
  3552. {
  3553. CConfig::ClearCustomGoOfflineActions(m_hdpaCustomGoOfflineActions);
  3554. DPA_Destroy(m_hdpaCustomGoOfflineActions);
  3555. }
  3556. }
  3557. //
  3558. // Retrieve the records from the "custom go-offline actions" listview and place them
  3559. // into a member array, sorted by server name.
  3560. //
  3561. void
  3562. CAdvOptDlg::PgState::SetCustomGoOfflineActions(
  3563. HWND hwndLV
  3564. )
  3565. {
  3566. int iItem = -1;
  3567. LVITEM item;
  3568. item.iSubItem = 0;
  3569. item.mask = LVIF_PARAM;
  3570. if (NULL != m_hdpaCustomGoOfflineActions)
  3571. {
  3572. CConfig::ClearCustomGoOfflineActions(m_hdpaCustomGoOfflineActions);
  3573. int cLvItems = ListView_GetItemCount(hwndLV);
  3574. for (int iLvItem = 0; iLvItem < cLvItems; iLvItem++)
  3575. {
  3576. CConfig::CustomGOA *pGOA = CAdvOptDlg::GetListviewObject(hwndLV, iLvItem);
  3577. if (NULL != pGOA)
  3578. {
  3579. if (!pGOA->SetByPolicy())
  3580. {
  3581. int cArrayItems = DPA_GetPtrCount(m_hdpaCustomGoOfflineActions);
  3582. int iArrayItem;
  3583. for (iArrayItem = 0; iArrayItem < cArrayItems; iArrayItem++)
  3584. {
  3585. CConfig::CustomGOA *pCustomGOA = (CConfig::CustomGOA *)DPA_GetPtr(m_hdpaCustomGoOfflineActions, iArrayItem);
  3586. if (NULL != pCustomGOA)
  3587. {
  3588. if (0 > lstrcmpi(pGOA->GetServerName(), pCustomGOA->GetServerName()))
  3589. break;
  3590. }
  3591. }
  3592. CConfig::CustomGOA *pCopy = new CConfig::CustomGOA(*pGOA);
  3593. if (NULL != pCopy)
  3594. {
  3595. if (iArrayItem < cArrayItems)
  3596. {
  3597. if (-1 != DPA_InsertPtr(m_hdpaCustomGoOfflineActions,
  3598. iArrayItem,
  3599. pCopy))
  3600. {
  3601. pCopy = NULL;
  3602. }
  3603. }
  3604. else
  3605. {
  3606. if (-1 != DPA_AppendPtr(m_hdpaCustomGoOfflineActions, pCopy))
  3607. {
  3608. pCopy = NULL;
  3609. }
  3610. }
  3611. if (NULL != pCopy)
  3612. {
  3613. delete pCopy;
  3614. }
  3615. }
  3616. }
  3617. }
  3618. }
  3619. }
  3620. }
  3621. //
  3622. // Two page states are equal if their Default go-offline actions are equal and their
  3623. // customized go-offline actions are equal. Action is tested first because it's a
  3624. // faster test.
  3625. //
  3626. bool
  3627. CAdvOptDlg::PgState::operator == (
  3628. const CAdvOptDlg::PgState& rhs
  3629. ) const
  3630. {
  3631. bool bMatch = false;
  3632. if (m_DefaultGoOfflineAction == rhs.m_DefaultGoOfflineAction)
  3633. {
  3634. if (NULL != m_hdpaCustomGoOfflineActions && NULL != rhs.m_hdpaCustomGoOfflineActions)
  3635. {
  3636. const int cLhs = DPA_GetPtrCount(m_hdpaCustomGoOfflineActions);
  3637. const int cRhs = DPA_GetPtrCount(rhs.m_hdpaCustomGoOfflineActions);
  3638. if (cLhs == cRhs)
  3639. {
  3640. for (int i = 0; i < cLhs; i++)
  3641. {
  3642. CConfig::CustomGOA *pGoaLhs = (CConfig::CustomGOA *)DPA_GetPtr(m_hdpaCustomGoOfflineActions, i);
  3643. CConfig::CustomGOA *pGoaRhs = (CConfig::CustomGOA *)DPA_GetPtr(rhs.m_hdpaCustomGoOfflineActions, i);
  3644. if (pGoaLhs->GetAction() != pGoaRhs->GetAction())
  3645. break;
  3646. if (0 != lstrcmpi(pGoaLhs->GetServerName(), pGoaRhs->GetServerName()))
  3647. break;
  3648. }
  3649. bMatch = (i == cLhs);
  3650. }
  3651. }
  3652. }
  3653. return bMatch;
  3654. }
  3655. //-----------------------------------------------------------------------------
  3656. // CustomGOAAddDlg
  3657. // "GOA" == Go Offline Action
  3658. // This dialog is for adding customized offline actions for particular
  3659. // network servers.
  3660. // It is invoked via the "Add..." button on the "Advanced" dialog.
  3661. //-----------------------------------------------------------------------------
  3662. const CustomGOAAddDlg::CtlActions CustomGOAAddDlg::m_rgCtlActions[CConfig::eNumOfflineActions] = {
  3663. { IDC_RBN_GOOFFLINE_SILENT, CConfig::eGoOfflineSilent },
  3664. { IDC_RBN_GOOFFLINE_FAIL, CConfig::eGoOfflineFail }
  3665. };
  3666. const DWORD CustomGOAAddDlg::m_rgHelpIDs[] = {
  3667. IDOK, IDH_OK,
  3668. IDCANCEL, IDH_CANCEL,
  3669. IDC_EDIT_GOOFFLINE_SERVER, HIDC_EDIT_GOOFFLINE_SERVER,
  3670. IDC_STATIC4, HIDC_EDIT_GOOFFLINE_SERVER, // "Computer:"
  3671. IDC_RBN_GOOFFLINE_SILENT, HIDC_RBN_GOOFFLINE_SILENT,
  3672. IDC_RBN_GOOFFLINE_FAIL, HIDC_RBN_GOOFFLINE_FAIL,
  3673. IDC_BTN_BROWSEFORSERVER, HIDC_BTN_BROWSEFORSERVER,
  3674. IDC_GRP_GOOFFLINE_DEFAULTS, DWORD(-1),
  3675. IDC_STATIC2, DWORD(-1), // icon
  3676. IDC_STATIC3, DWORD(-1), // icon's text
  3677. 0, 0
  3678. };
  3679. CustomGOAAddDlg::CustomGOAAddDlg(
  3680. HINSTANCE hInstance,
  3681. HWND hwndParent,
  3682. LPTSTR pszServer,
  3683. UINT cchServer,
  3684. CConfig::OfflineAction *pAction
  3685. ) : m_hInstance(hInstance),
  3686. m_hwndParent(hwndParent),
  3687. m_hwndDlg(NULL),
  3688. m_hwndEdit(NULL),
  3689. m_pszServer(pszServer),
  3690. m_cchServer(cchServer),
  3691. m_pAction(pAction)
  3692. {
  3693. TraceAssert(NULL != pAction);
  3694. }
  3695. int
  3696. CustomGOAAddDlg::Run(
  3697. void
  3698. )
  3699. {
  3700. return (int)DialogBoxParam(m_hInstance,
  3701. MAKEINTRESOURCE(IDD_CSC_ADVOPTIONS_ADD),
  3702. m_hwndParent,
  3703. DlgProc,
  3704. (LPARAM)this);
  3705. }
  3706. INT_PTR CALLBACK
  3707. CustomGOAAddDlg::DlgProc(
  3708. HWND hDlg,
  3709. UINT message,
  3710. WPARAM wParam,
  3711. LPARAM lParam
  3712. )
  3713. {
  3714. BOOL bResult = FALSE;
  3715. CustomGOAAddDlg *pThis = (CustomGOAAddDlg *)GetWindowLongPtr(hDlg, DWLP_USER);
  3716. switch(message)
  3717. {
  3718. case WM_INITDIALOG:
  3719. {
  3720. pThis = (CustomGOAAddDlg *)lParam;
  3721. TraceAssert(NULL != pThis);
  3722. SetWindowLongPtr(hDlg, DWLP_USER, (LONG_PTR)pThis);
  3723. bResult = pThis->OnInitDialog(hDlg, (HWND)wParam, lParam);
  3724. break;
  3725. }
  3726. case WM_COMMAND:
  3727. TraceAssert(NULL != pThis);
  3728. bResult = pThis->OnCommand(hDlg, HIWORD(wParam), LOWORD(wParam), (HWND)lParam);
  3729. break;
  3730. case WM_HELP:
  3731. TraceAssert(NULL != pThis);
  3732. bResult = pThis->OnHelp(hDlg, (LPHELPINFO)lParam);
  3733. break;
  3734. case WM_CONTEXTMENU:
  3735. TraceAssert(NULL != pThis);
  3736. bResult = pThis->OnContextMenu((HWND)wParam, LOWORD(lParam), HIWORD(lParam));
  3737. break;
  3738. case WM_DESTROY:
  3739. TraceAssert(NULL != pThis);
  3740. bResult = pThis->OnDestroy(hDlg);
  3741. break;
  3742. default:
  3743. break;
  3744. }
  3745. return bResult;
  3746. }
  3747. BOOL
  3748. CustomGOAAddDlg::OnInitDialog(
  3749. HWND hDlg,
  3750. HWND hwndFocus,
  3751. LPARAM lInitParam
  3752. )
  3753. {
  3754. m_hwndDlg = hDlg;
  3755. //
  3756. // Set the default go-offline action radio buttons.
  3757. //
  3758. for (int i = 0; i < ARRAYSIZE(m_rgCtlActions); i++)
  3759. {
  3760. CheckDlgButton(hDlg,
  3761. m_rgCtlActions[i].idRbn,
  3762. m_rgCtlActions[i].action == *m_pAction ? BST_CHECKED : BST_UNCHECKED);
  3763. }
  3764. m_hwndEdit = GetDlgItem(hDlg, IDC_EDIT_GOOFFLINE_SERVER);
  3765. SetWindowText(m_hwndEdit, m_pszServer);
  3766. return TRUE;
  3767. }
  3768. void
  3769. CustomGOAAddDlg::GetEnteredServerName(
  3770. LPTSTR pszServer,
  3771. UINT cchServer,
  3772. bool bTrimLeadingJunk
  3773. )
  3774. {
  3775. //
  3776. // Get the server name.
  3777. //
  3778. GetWindowText(m_hwndEdit, pszServer, cchServer);
  3779. if (bTrimLeadingJunk)
  3780. {
  3781. //
  3782. // Remove any leading "\\" or space user might have entered.
  3783. //
  3784. LPTSTR pszLookahead = pszServer;
  3785. while(*pszLookahead && (TEXT('\\') == *pszLookahead || TEXT(' ') == *pszLookahead))
  3786. pszLookahead++;
  3787. if (pszLookahead > pszServer)
  3788. {
  3789. lstrcpyn(pszServer, pszLookahead, cchServer);
  3790. }
  3791. }
  3792. }
  3793. //
  3794. // Query the dialog and return the state through pointer args.
  3795. //
  3796. void
  3797. CustomGOAAddDlg::GetActionInfo(
  3798. LPTSTR pszServer,
  3799. UINT cchServer,
  3800. CConfig::OfflineAction *pAction
  3801. )
  3802. {
  3803. TraceAssert(NULL != pszServer);
  3804. TraceAssert(NULL != pAction);
  3805. //
  3806. // Get the action radio button setting.
  3807. //
  3808. *pAction = CConfig::eNumOfflineActions;
  3809. for(int i = 0; i < ARRAYSIZE(m_rgCtlActions); i++)
  3810. {
  3811. if (BST_CHECKED == IsDlgButtonChecked(m_hwndDlg, m_rgCtlActions[i].idRbn))
  3812. {
  3813. *pAction = m_rgCtlActions[i].action;
  3814. break;
  3815. }
  3816. }
  3817. TraceAssert(CConfig::eNumOfflineActions != *pAction);
  3818. //
  3819. // Retrieve the entered server name with leading junk removed.
  3820. // Should have just a bare server name (i.e. "worf").
  3821. //
  3822. GetEnteredServerName(pszServer, cchServer, true);
  3823. }
  3824. //
  3825. // See if server name entered could be valid.
  3826. // This weeds out things like entering a UNC share name
  3827. // instead of a server name.
  3828. //
  3829. // "\\rastaman" or "rastaman" is good.
  3830. // "\\rastaman\ntwin" is bad.
  3831. //
  3832. // This function will display error UI if the name is not valid.
  3833. // Returns:
  3834. // true = Name is a UNC server name.
  3835. // false = Name is not a UNC server name.
  3836. //
  3837. bool
  3838. CustomGOAAddDlg::CheckServerNameEntered(
  3839. void
  3840. )
  3841. {
  3842. TCHAR szRaw[MAX_PATH]; // Name "as entered".
  3843. GetEnteredServerName(szRaw, ARRAYSIZE(szRaw), false);
  3844. TCHAR szClean[MAX_PATH]; // Name with leading "\\" and any spaces removed.
  3845. GetEnteredServerName(szClean, ARRAYSIZE(szClean), true);
  3846. TCHAR szPath[MAX_PATH];
  3847. wnsprintf(szPath, ARRAYSIZE(szPath), TEXT("\\\\%s"), szClean);
  3848. if (!::PathIsUNCServer(szPath))
  3849. {
  3850. //
  3851. // Name provided is not a UNC server name.
  3852. //
  3853. CscMessageBox(m_hwndDlg,
  3854. MB_OK | MB_ICONERROR,
  3855. m_hInstance,
  3856. IDS_ERR_NOTSERVERNAME,
  3857. szRaw);
  3858. return false;
  3859. }
  3860. return true;
  3861. }
  3862. BOOL
  3863. CustomGOAAddDlg::OnCommand(
  3864. HWND hDlg,
  3865. WORD wNotifyCode,
  3866. WORD wID,
  3867. HWND hwndCtl
  3868. )
  3869. {
  3870. switch(wID)
  3871. {
  3872. case IDOK:
  3873. //
  3874. // First see if server name entered could be valid.
  3875. // This weeds out things like entering a UNC share name
  3876. // instead of a server name.
  3877. //
  3878. // "\\rastaman" or "rastaman" is good.
  3879. // "\\rastaman\ntwin" is bad.
  3880. //
  3881. // This function will display error UI if the name is not valid.
  3882. //
  3883. if (!CheckServerNameEntered())
  3884. {
  3885. //
  3886. // Invalid name. Return focus to the server name edit control.
  3887. //
  3888. SetFocus(GetDlgItem(hDlg, IDC_EDIT_GOOFFLINE_SERVER));
  3889. break;
  3890. }
  3891. GetActionInfo(m_pszServer, m_cchServer, m_pAction);
  3892. //
  3893. // Fall through...
  3894. //
  3895. case IDCANCEL:
  3896. EndDialog(hDlg, wID);
  3897. break;
  3898. case IDC_BTN_BROWSEFORSERVER:
  3899. {
  3900. TCHAR szServer[MAX_PATH];
  3901. szServer[0] = TEXT('\0');
  3902. BrowseForServer(hDlg, szServer, ARRAYSIZE(szServer));
  3903. if (TEXT('\0') != szServer[0])
  3904. {
  3905. SetWindowText(GetDlgItem(hDlg, IDC_EDIT_GOOFFLINE_SERVER), szServer);
  3906. }
  3907. break;
  3908. }
  3909. }
  3910. return FALSE;
  3911. }
  3912. //
  3913. // Use the SHBrowseForFolder dialog to find a server.
  3914. //
  3915. void
  3916. CustomGOAAddDlg::BrowseForServer(
  3917. HWND hDlg,
  3918. LPTSTR pszServer,
  3919. UINT cchServer
  3920. )
  3921. {
  3922. TraceAssert(NULL != pszServer);
  3923. LPTSTR pszTitle;
  3924. if (0 < FormatStringID(&pszTitle, m_hInstance, IDS_BROWSEFORSERVER))
  3925. {
  3926. BROWSEINFO bi;
  3927. ZeroMemory(&bi, sizeof(bi));
  3928. //
  3929. // Start browsing in the network folder.
  3930. //
  3931. LPITEMIDLIST pidlRoot = NULL;
  3932. HRESULT hr = SHGetSpecialFolderLocation(hDlg, CSIDL_NETWORK, &pidlRoot);
  3933. if (SUCCEEDED(hr))
  3934. {
  3935. TCHAR szServer[MAX_PATH];
  3936. bi.hwndOwner = hDlg;
  3937. bi.pidlRoot = pidlRoot;
  3938. bi.pszDisplayName = szServer;
  3939. bi.lpszTitle = pszTitle;
  3940. bi.ulFlags = BIF_BROWSEFORCOMPUTER;
  3941. bi.lpfn = NULL;
  3942. bi.lParam = NULL;
  3943. bi.iImage = 0;
  3944. LPITEMIDLIST pidlFolder = SHBrowseForFolder(&bi);
  3945. ILFree(pidlRoot);
  3946. if (NULL != pidlFolder)
  3947. {
  3948. ILFree(pidlFolder);
  3949. lstrcpyn(pszServer, szServer, cchServer);
  3950. }
  3951. }
  3952. else
  3953. {
  3954. CscMessageBox(hDlg, MB_OK, Win32Error(HRESULT_CODE(hr)));
  3955. }
  3956. LocalFree(pszTitle);
  3957. }
  3958. }
  3959. BOOL
  3960. CustomGOAAddDlg::OnHelp(
  3961. HWND hDlg,
  3962. LPHELPINFO pHelpInfo
  3963. )
  3964. {
  3965. if (HELPINFO_WINDOW == pHelpInfo->iContextType)
  3966. {
  3967. int idCtl = GetDlgCtrlID((HWND)pHelpInfo->hItemHandle);
  3968. WinHelp((HWND)pHelpInfo->hItemHandle,
  3969. UseWindowsHelp(idCtl) ? NULL : c_szHelpFile,
  3970. HELP_WM_HELP,
  3971. (DWORD_PTR)((LPTSTR)m_rgHelpIDs));
  3972. }
  3973. return FALSE;
  3974. }
  3975. BOOL
  3976. CustomGOAAddDlg::OnContextMenu(
  3977. HWND hwndItem,
  3978. int xPos,
  3979. int yPos
  3980. )
  3981. {
  3982. int idCtl = GetDlgCtrlID(hwndItem);
  3983. WinHelp(hwndItem,
  3984. UseWindowsHelp(idCtl) ? NULL : c_szHelpFile,
  3985. HELP_CONTEXTMENU,
  3986. (DWORD_PTR)((LPTSTR)m_rgHelpIDs));
  3987. return FALSE;
  3988. }
  3989. BOOL
  3990. CustomGOAAddDlg::OnDestroy(
  3991. HWND hDlg
  3992. )
  3993. {
  3994. return FALSE;
  3995. }
  3996. //-----------------------------------------------------------------------------
  3997. // CustomGOAEditDlg
  3998. // "GOA" == Go Offline Action
  3999. // This dialog is for editing customized offline actions for particular
  4000. // network servers.
  4001. // It is invoked via the "Edit..." button on the "Advanced" dialog.
  4002. //-----------------------------------------------------------------------------
  4003. const CustomGOAEditDlg::CtlActions CustomGOAEditDlg::m_rgCtlActions[CConfig::eNumOfflineActions] = {
  4004. { IDC_RBN_GOOFFLINE_SILENT, CConfig::eGoOfflineSilent },
  4005. { IDC_RBN_GOOFFLINE_FAIL, CConfig::eGoOfflineFail },
  4006. };
  4007. const DWORD CustomGOAEditDlg::m_rgHelpIDs[] = {
  4008. IDOK, IDH_OK,
  4009. IDCANCEL, IDH_CANCEL,
  4010. IDC_TXT_GOOFFLINE_SERVER, HIDC_TXT_GOOFFLINE_SERVER,
  4011. IDC_STATIC4, HIDC_TXT_GOOFFLINE_SERVER, // "Computer:"
  4012. IDC_RBN_GOOFFLINE_SILENT, HIDC_RBN_GOOFFLINE_SILENT,
  4013. IDC_RBN_GOOFFLINE_FAIL, HIDC_RBN_GOOFFLINE_FAIL,
  4014. IDC_GRP_GOOFFLINE_DEFAULTS, DWORD(-1),
  4015. IDC_STATIC2, DWORD(-1), // icon
  4016. IDC_STATIC3, DWORD(-1), // icon's text.
  4017. 0, 0
  4018. };
  4019. CustomGOAEditDlg::CustomGOAEditDlg(
  4020. HINSTANCE hInstance,
  4021. HWND hwndParent,
  4022. LPCTSTR pszServer, // NULL == multi-server.
  4023. CConfig::OfflineAction *pAction
  4024. ) : m_hInstance(hInstance),
  4025. m_hwndParent(hwndParent),
  4026. m_hwndDlg(NULL),
  4027. m_pAction(pAction)
  4028. {
  4029. TraceAssert(NULL != pAction);
  4030. if (NULL != pszServer && TEXT('\0') != *pszServer)
  4031. {
  4032. lstrcpyn(m_szServer, pszServer, ARRAYSIZE(m_szServer));
  4033. }
  4034. else
  4035. {
  4036. m_szServer[0] = TEXT('\0');
  4037. LoadString(m_hInstance, IDS_GOOFFLINE_MULTISERVER, m_szServer, ARRAYSIZE(m_szServer));
  4038. }
  4039. }
  4040. int
  4041. CustomGOAEditDlg::Run(
  4042. void
  4043. )
  4044. {
  4045. return (int)DialogBoxParam(m_hInstance,
  4046. MAKEINTRESOURCE(IDD_CSC_ADVOPTIONS_EDIT),
  4047. m_hwndParent,
  4048. DlgProc,
  4049. (LPARAM)this);
  4050. }
  4051. INT_PTR CALLBACK
  4052. CustomGOAEditDlg::DlgProc(
  4053. HWND hDlg,
  4054. UINT message,
  4055. WPARAM wParam,
  4056. LPARAM lParam
  4057. )
  4058. {
  4059. BOOL bResult = FALSE;
  4060. CustomGOAEditDlg *pThis = (CustomGOAEditDlg *)GetWindowLongPtr(hDlg, DWLP_USER);
  4061. switch(message)
  4062. {
  4063. case WM_INITDIALOG:
  4064. {
  4065. pThis = (CustomGOAEditDlg *)lParam;
  4066. TraceAssert(NULL != pThis);
  4067. SetWindowLongPtr(hDlg, DWLP_USER, (LONG_PTR)pThis);
  4068. bResult = pThis->OnInitDialog(hDlg, (HWND)wParam, lParam);
  4069. break;
  4070. }
  4071. case WM_COMMAND:
  4072. TraceAssert(NULL != pThis);
  4073. bResult = pThis->OnCommand(hDlg, HIWORD(wParam), LOWORD(wParam), (HWND)lParam);
  4074. break;
  4075. case WM_HELP:
  4076. TraceAssert(NULL != pThis);
  4077. bResult = pThis->OnHelp(hDlg, (LPHELPINFO)lParam);
  4078. break;
  4079. case WM_CONTEXTMENU:
  4080. TraceAssert(NULL != pThis);
  4081. bResult = pThis->OnContextMenu((HWND)wParam, LOWORD(lParam), HIWORD(lParam));
  4082. break;
  4083. case WM_DESTROY:
  4084. TraceAssert(NULL != pThis);
  4085. bResult = pThis->OnDestroy(hDlg);
  4086. break;
  4087. default:
  4088. break;
  4089. }
  4090. return bResult;
  4091. }
  4092. BOOL
  4093. CustomGOAEditDlg::OnInitDialog(
  4094. HWND hDlg,
  4095. HWND hwndFocus,
  4096. LPARAM lInitParam
  4097. )
  4098. {
  4099. m_hwndDlg = hDlg;
  4100. //
  4101. // Set the default go-offline action radio buttons.
  4102. //
  4103. for (int i = 0; i < ARRAYSIZE(m_rgCtlActions); i++)
  4104. {
  4105. CheckDlgButton(hDlg,
  4106. m_rgCtlActions[i].idRbn,
  4107. m_rgCtlActions[i].action == *m_pAction ? BST_CHECKED : BST_UNCHECKED);
  4108. }
  4109. SetWindowText(GetDlgItem(hDlg, IDC_TXT_GOOFFLINE_SERVER), m_szServer);
  4110. return TRUE;
  4111. }
  4112. //
  4113. // Query the dialog and return the state through pointer args.
  4114. //
  4115. void
  4116. CustomGOAEditDlg::GetActionInfo(
  4117. CConfig::OfflineAction *pAction
  4118. )
  4119. {
  4120. TraceAssert(NULL != pAction);
  4121. //
  4122. // Get the action radio button setting.
  4123. //
  4124. *pAction = CConfig::eNumOfflineActions;
  4125. for(int i = 0; i < ARRAYSIZE(m_rgCtlActions); i++)
  4126. {
  4127. if (BST_CHECKED == IsDlgButtonChecked(m_hwndDlg, m_rgCtlActions[i].idRbn))
  4128. {
  4129. *pAction = m_rgCtlActions[i].action;
  4130. break;
  4131. }
  4132. }
  4133. TraceAssert(CConfig::eNumOfflineActions != *pAction);
  4134. }
  4135. BOOL
  4136. CustomGOAEditDlg::OnCommand(
  4137. HWND hDlg,
  4138. WORD wNotifyCode,
  4139. WORD wID,
  4140. HWND hwndCtl
  4141. )
  4142. {
  4143. switch(wID)
  4144. {
  4145. case IDOK:
  4146. GetActionInfo(m_pAction);
  4147. //
  4148. // Fall through...
  4149. //
  4150. case IDCANCEL:
  4151. EndDialog(hDlg, wID);
  4152. break;
  4153. }
  4154. return FALSE;
  4155. }
  4156. BOOL
  4157. CustomGOAEditDlg::OnHelp(
  4158. HWND hDlg,
  4159. LPHELPINFO pHelpInfo
  4160. )
  4161. {
  4162. if (HELPINFO_WINDOW == pHelpInfo->iContextType)
  4163. {
  4164. int idCtl = GetDlgCtrlID((HWND)pHelpInfo->hItemHandle);
  4165. WinHelp((HWND)pHelpInfo->hItemHandle,
  4166. UseWindowsHelp(idCtl) ? NULL : c_szHelpFile,
  4167. HELP_WM_HELP,
  4168. (DWORD_PTR)((LPTSTR)m_rgHelpIDs));
  4169. }
  4170. return FALSE;
  4171. }
  4172. BOOL
  4173. CustomGOAEditDlg::OnContextMenu(
  4174. HWND hwndItem,
  4175. int xPos,
  4176. int yPos
  4177. )
  4178. {
  4179. int idCtl = GetDlgCtrlID(hwndItem);
  4180. WinHelp(hwndItem,
  4181. UseWindowsHelp(idCtl) ? NULL : c_szHelpFile,
  4182. HELP_CONTEXTMENU,
  4183. (DWORD_PTR)((LPTSTR)m_rgHelpIDs));
  4184. return FALSE;
  4185. }
  4186. BOOL
  4187. CustomGOAEditDlg::OnDestroy(
  4188. HWND hDlg
  4189. )
  4190. {
  4191. return FALSE;
  4192. }
  4193. //-----------------------------------------------------------------------------
  4194. // COfflineFilesSheet
  4195. //-----------------------------------------------------------------------------
  4196. COfflineFilesSheet::COfflineFilesSheet(
  4197. HINSTANCE hInstance,
  4198. LONG *pDllRefCount,
  4199. HWND hwndParent
  4200. ) : m_hInstance(hInstance),
  4201. m_pDllRefCount(pDllRefCount),
  4202. m_hwndParent(hwndParent)
  4203. {
  4204. InterlockedIncrement(m_pDllRefCount);
  4205. }
  4206. COfflineFilesSheet::~COfflineFilesSheet(
  4207. void
  4208. )
  4209. {
  4210. InterlockedDecrement(m_pDllRefCount);
  4211. }
  4212. BOOL CALLBACK
  4213. COfflineFilesSheet::AddPropSheetPage(
  4214. HPROPSHEETPAGE hpage,
  4215. LPARAM lParam
  4216. )
  4217. {
  4218. PROPSHEETHEADER * ppsh = (PROPSHEETHEADER *)lParam;
  4219. if (ppsh->nPages < COfflineFilesSheet::MAXPAGES)
  4220. {
  4221. ppsh->phpage[ppsh->nPages++] = hpage;
  4222. return TRUE;
  4223. }
  4224. return FALSE;
  4225. }
  4226. //
  4227. // Static function for creating and running an instance of the
  4228. // CSCUI options dialog. This is the ONLY function callable
  4229. // by non-member code to create and run an options dialog.
  4230. //
  4231. DWORD
  4232. COfflineFilesSheet::CreateAndRun(
  4233. HINSTANCE hInstance,
  4234. HWND hwndParent,
  4235. LONG *pDllRefCount,
  4236. BOOL bAsync
  4237. )
  4238. {
  4239. //
  4240. // First try to activate an existing instance of the prop sheet.
  4241. //
  4242. TCHAR szSheetTitle[MAX_PATH] = {0};
  4243. LoadString(hInstance, IDS_CSCOPT_PROPSHEET_TITLE, szSheetTitle, ARRAYSIZE(szSheetTitle));
  4244. HWND hwnd = FindWindowEx(NULL, NULL, WC_DIALOG, szSheetTitle);
  4245. if (NULL == hwnd || !SetForegroundWindow(hwnd))
  4246. {
  4247. //
  4248. // This thread param buffer will be deleted by the
  4249. // thread proc.
  4250. //
  4251. ThreadParams *ptp = new ThreadParams(hwndParent, pDllRefCount);
  4252. if (NULL != ptp)
  4253. {
  4254. if (bAsync)
  4255. {
  4256. //
  4257. // LoadLibrary on ourselves so that we stay in memory even
  4258. // if the caller calls FreeLibrary. We'll call FreeLibrary
  4259. // when the thread proc exits.
  4260. //
  4261. ptp->SetModuleHandle(LoadLibrary(TEXT("cscui.dll")));
  4262. DWORD idThread;
  4263. HANDLE hThread = (HANDLE)_beginthreadex(NULL,
  4264. 0,
  4265. ThreadProc,
  4266. (LPVOID)ptp,
  4267. 0,
  4268. (UINT *)&idThread);
  4269. if (INVALID_HANDLE_VALUE != hThread)
  4270. {
  4271. CloseHandle(hThread);
  4272. }
  4273. else
  4274. {
  4275. //
  4276. // Thread creation failed. Delete thread param buffer.
  4277. //
  4278. delete ptp;
  4279. }
  4280. }
  4281. else
  4282. {
  4283. ThreadProc(ptp);
  4284. }
  4285. }
  4286. }
  4287. return 0;
  4288. }
  4289. //
  4290. // The share dialog's thread proc.
  4291. //
  4292. UINT WINAPI
  4293. COfflineFilesSheet::ThreadProc(
  4294. LPVOID pvParam
  4295. )
  4296. {
  4297. ThreadParams *ptp = reinterpret_cast<ThreadParams *>(pvParam);
  4298. TraceAssert(NULL != ptp);
  4299. HINSTANCE hInstance = ptp->m_hInstance; // Save local copy.
  4300. COfflineFilesSheet dlg(ptp->m_hInstance ? ptp->m_hInstance : g_hInstance,
  4301. ptp->m_pDllRefCount,
  4302. ptp->m_hwndParent);
  4303. dlg.Run();
  4304. delete ptp;
  4305. if (NULL != hInstance)
  4306. FreeLibraryAndExitThread(hInstance, 0);
  4307. return 0;
  4308. }
  4309. DWORD
  4310. COfflineFilesSheet::Run(
  4311. void
  4312. )
  4313. {
  4314. DWORD dwError = ERROR_SUCCESS;
  4315. if (CConfig::GetSingleton().NoConfigCache())
  4316. {
  4317. Trace((TEXT("System policy restricts configuration of Offline Files cache")));
  4318. return ERROR_SUCCESS;
  4319. }
  4320. TCHAR szSheetTitle[MAX_PATH] = {0};
  4321. LoadString(m_hInstance, IDS_CSCOPT_PROPSHEET_TITLE, szSheetTitle, ARRAYSIZE(szSheetTitle));
  4322. HPROPSHEETPAGE rghPages[COfflineFilesSheet::MAXPAGES];
  4323. PROPSHEETHEADER psh;
  4324. ZeroMemory(&psh, sizeof(psh));
  4325. //
  4326. // Define sheet.
  4327. //
  4328. psh.dwSize = sizeof(PROPSHEETHEADER);
  4329. psh.dwFlags = 0;
  4330. psh.hInstance = m_hInstance;
  4331. psh.hwndParent = m_hwndParent;
  4332. psh.pszIcon = MAKEINTRESOURCE(IDI_CSCUI_ICON);
  4333. psh.pszCaption = szSheetTitle;
  4334. psh.nPages = 0;
  4335. psh.nStartPage = 0;
  4336. psh.phpage = rghPages;
  4337. //
  4338. // Policy doesn't prevent user from configuring CSC cache.
  4339. // Add the dynamic page(s).
  4340. //
  4341. CCoInit coinit;
  4342. HRESULT hr = coinit.Result();
  4343. if (SUCCEEDED(hr))
  4344. {
  4345. IShellExtInit *psei;
  4346. hr = CoCreateInstance(CLSID_OfflineFilesOptions,
  4347. NULL,
  4348. CLSCTX_INPROC_SERVER,
  4349. IID_IShellExtInit,
  4350. (void **)&psei);
  4351. if (SUCCEEDED(hr))
  4352. {
  4353. IShellPropSheetExt *pspse;
  4354. hr = psei->QueryInterface(IID_IShellPropSheetExt, (void **)&pspse);
  4355. if (SUCCEEDED(hr))
  4356. {
  4357. hr = pspse->AddPages(AddPropSheetPage, (LPARAM)&psh);
  4358. pspse->Release();
  4359. pspse = NULL;
  4360. }
  4361. switch(PropertySheet(&psh))
  4362. {
  4363. case ID_PSREBOOTSYSTEM:
  4364. //
  4365. // User wants to change enabled state of CSC. Requires reboot.
  4366. //
  4367. if (IDYES == CscMessageBox(m_hwndParent,
  4368. MB_YESNO | MB_ICONINFORMATION,
  4369. m_hInstance,
  4370. IDS_REBOOTSYSTEM))
  4371. {
  4372. dwError = CSCUIRebootSystem();
  4373. if (ERROR_SUCCESS != dwError)
  4374. {
  4375. Trace((TEXT("Reboot failed with error %d"), dwError));
  4376. CscMessageBox(m_hwndParent,
  4377. MB_ICONWARNING | MB_OK,
  4378. Win32Error(dwError),
  4379. m_hInstance,
  4380. IDS_ERR_REBOOTFAILED);
  4381. }
  4382. }
  4383. dwError = ERROR_SUCCESS; // Run() succeeded.
  4384. break;
  4385. case -1:
  4386. {
  4387. dwError = GetLastError();
  4388. Trace((TEXT("PropertySheet failed with error %d"), dwError));
  4389. CscWin32Message(m_hwndParent, dwError, CSCUI::SEV_ERROR);
  4390. break;
  4391. }
  4392. default:
  4393. break;
  4394. }
  4395. psei->Release();
  4396. psei = NULL;
  4397. }
  4398. else
  4399. {
  4400. Trace((TEXT("CoCreateInstance failed with result 0x%08X"), hr));
  4401. }
  4402. }
  4403. else
  4404. {
  4405. Trace((TEXT("CoInitialize failed with result 0x%08X"), hr));
  4406. }
  4407. return dwError;
  4408. }
  4409. //
  4410. // Exported API for launching the CSC Options property sheet.
  4411. // If policy disallows configuration of CSC, we display a messagebox
  4412. // with an error message.
  4413. //
  4414. DWORD CSCUIOptionsPropertySheetEx(HWND hwndParent, BOOL bAsync)
  4415. {
  4416. DWORD dwResult = ERROR_SUCCESS;
  4417. if (!CConfig::GetSingleton().NoConfigCache())
  4418. {
  4419. dwResult = COfflineFilesSheet::CreateAndRun(g_hInstance,
  4420. hwndParent,
  4421. &g_cRefCount,
  4422. bAsync);
  4423. }
  4424. else
  4425. {
  4426. CscMessageBox(hwndParent,
  4427. MB_OK,
  4428. g_hInstance,
  4429. IDS_ERR_POLICY_NOCONFIGCSC);
  4430. }
  4431. return dwResult;
  4432. }
  4433. DWORD CSCUIOptionsPropertySheet(HWND hwndParent)
  4434. {
  4435. return CSCUIOptionsPropertySheetEx(hwndParent, TRUE);
  4436. }
  4437. STDAPI_(void) CSCOptions_RunDLLW(HWND hwndStub, HINSTANCE /*hInst*/, LPWSTR pszCmdLine, int /*nCmdShow*/)
  4438. {
  4439. DllAddRef();
  4440. HWND hwndParent = FindWindowW(NULL, pszCmdLine);
  4441. CSCUIOptionsPropertySheetEx(hwndParent ? hwndParent : hwndStub, FALSE);
  4442. DllRelease();
  4443. }
  4444. STDAPI_(void) CSCOptions_RunDLLA(HWND hwndStub, HINSTANCE hInst, LPSTR pszCmdLine, int nCmdShow)
  4445. {
  4446. WCHAR wszCmdLine[MAX_PATH];
  4447. DllAddRef();
  4448. SHAnsiToUnicode(pszCmdLine, wszCmdLine, ARRAYSIZE(wszCmdLine));
  4449. CSCOptions_RunDLLW(hwndStub, hInst, wszCmdLine, nCmdShow);
  4450. DllRelease();
  4451. }
  4452. //-----------------------------------------------------------------------------
  4453. // CscOptPropSheetExt
  4454. // This is the shell prop sheet extension implementation for creating
  4455. // the "Offline Folders" property page.
  4456. //-----------------------------------------------------------------------------
  4457. CscOptPropSheetExt::CscOptPropSheetExt(
  4458. HINSTANCE hInstance,
  4459. LONG *pDllRefCnt
  4460. ) : m_cRef(0),
  4461. m_pDllRefCnt(pDllRefCnt),
  4462. m_hInstance(hInstance),
  4463. m_pOfflineFoldersPg(NULL)
  4464. {
  4465. InterlockedIncrement(m_pDllRefCnt);
  4466. }
  4467. CscOptPropSheetExt::~CscOptPropSheetExt(
  4468. void
  4469. )
  4470. {
  4471. delete m_pOfflineFoldersPg;
  4472. InterlockedDecrement(m_pDllRefCnt);
  4473. }
  4474. HRESULT
  4475. CscOptPropSheetExt::QueryInterface(
  4476. REFIID riid,
  4477. void **ppvOut
  4478. )
  4479. {
  4480. HRESULT hr = E_NOINTERFACE;
  4481. if (NULL == ppvOut)
  4482. return E_INVALIDARG;
  4483. *ppvOut = NULL;
  4484. if (IID_IUnknown == riid ||
  4485. IID_IShellExtInit == riid)
  4486. {
  4487. *ppvOut = static_cast<IShellExtInit *>(this);
  4488. }
  4489. else if (IID_IShellPropSheetExt == riid)
  4490. {
  4491. *ppvOut = static_cast<IShellPropSheetExt *>(this);
  4492. }
  4493. if (NULL != *ppvOut)
  4494. {
  4495. ((LPUNKNOWN)*ppvOut)->AddRef();
  4496. hr = NOERROR;
  4497. }
  4498. return hr;
  4499. }
  4500. ULONG
  4501. CscOptPropSheetExt::AddRef(
  4502. void
  4503. )
  4504. {
  4505. ULONG ulReturn = m_cRef + 1;
  4506. InterlockedIncrement(&m_cRef);
  4507. return ulReturn;
  4508. }
  4509. ULONG
  4510. CscOptPropSheetExt::Release(
  4511. void
  4512. )
  4513. {
  4514. ULONG ulReturn = m_cRef - 1;
  4515. if (InterlockedDecrement(&m_cRef) == 0)
  4516. {
  4517. delete this;
  4518. ulReturn = 0;
  4519. }
  4520. return ulReturn;
  4521. }
  4522. HRESULT
  4523. CscOptPropSheetExt::Initialize(
  4524. LPCITEMIDLIST pidlFolder,
  4525. LPDATAOBJECT pdtobj,
  4526. HKEY hkeyProgID
  4527. )
  4528. {
  4529. return NOERROR;
  4530. }
  4531. HRESULT
  4532. CscOptPropSheetExt::AddPages(
  4533. LPFNADDPROPSHEETPAGE lpfnAddPage,
  4534. LPARAM lParam
  4535. )
  4536. {
  4537. TraceAssert(NULL != lpfnAddPage);
  4538. TraceAssert(NULL == m_pOfflineFoldersPg);
  4539. HRESULT hr = E_FAIL; // Assume failure.
  4540. if (!CConfig::GetSingleton().NoConfigCache())
  4541. {
  4542. hr = E_OUTOFMEMORY;
  4543. HPROPSHEETPAGE hOfflineFoldersPg = NULL;
  4544. m_pOfflineFoldersPg = new COfflineFilesPage(m_hInstance,
  4545. static_cast<IShellPropSheetExt *>(this));
  4546. if (NULL != m_pOfflineFoldersPg)
  4547. {
  4548. hr = AddPage(lpfnAddPage, lParam, *m_pOfflineFoldersPg, &hOfflineFoldersPg);
  4549. }
  4550. }
  4551. return hr;
  4552. }
  4553. HRESULT
  4554. CscOptPropSheetExt::AddPage(
  4555. LPFNADDPROPSHEETPAGE lpfnAddPage,
  4556. LPARAM lParam,
  4557. const COfflineFilesPage& pg,
  4558. HPROPSHEETPAGE *phPage
  4559. )
  4560. {
  4561. TraceAssert(NULL != lpfnAddPage);
  4562. TraceAssert(NULL != phPage);
  4563. HRESULT hr = E_FAIL;
  4564. PROPSHEETPAGE psp;
  4565. psp.dwSize = sizeof(psp);
  4566. psp.dwFlags = PSP_USECALLBACK | PSP_USEREFPARENT;
  4567. psp.hInstance = m_hInstance;
  4568. psp.pszTemplate = MAKEINTRESOURCE(pg.GetDlgTemplateID());
  4569. psp.hIcon = NULL;
  4570. psp.pszTitle = NULL;
  4571. psp.pfnDlgProc = pg.GetDlgProcPtr();
  4572. psp.lParam = (LPARAM)&pg;
  4573. psp.pcRefParent = (UINT *)m_pDllRefCnt;
  4574. psp.pfnCallback = (LPFNPSPCALLBACK)pg.GetCallbackFuncPtr();
  4575. *phPage = CreatePropertySheetPage(&psp);
  4576. if (NULL != *phPage)
  4577. {
  4578. if (!lpfnAddPage(*phPage, lParam))
  4579. {
  4580. Trace((TEXT("AddPage Failed to add page.")));
  4581. DestroyPropertySheetPage(*phPage);
  4582. *phPage = NULL;
  4583. }
  4584. }
  4585. else
  4586. {
  4587. Trace((TEXT("CreatePropertySheetPage failed.")));
  4588. }
  4589. if (NULL != *phPage)
  4590. {
  4591. AddRef();
  4592. hr = NOERROR;
  4593. }
  4594. return hr;
  4595. }
  4596. STDAPI
  4597. COfflineFilesOptions_CreateInstance(
  4598. REFIID riid,
  4599. void **ppv
  4600. )
  4601. {
  4602. HRESULT hr = E_NOINTERFACE;
  4603. if (IsEqualIID(riid, IID_IUnknown) ||
  4604. IsEqualIID(riid, IID_IShellPropSheetExt) ||
  4605. IsEqualIID(riid, IID_IShellExtInit))
  4606. {
  4607. //
  4608. // Create the property sheet extension to handle the CSC options property
  4609. // pages.
  4610. //
  4611. CscOptPropSheetExt *pse = new CscOptPropSheetExt(g_hInstance, &g_cRefCount);
  4612. if (NULL != pse)
  4613. {
  4614. pse->AddRef();
  4615. hr = pse->QueryInterface(riid, ppv);
  4616. pse->Release();
  4617. }
  4618. else
  4619. hr = E_OUTOFMEMORY;
  4620. }
  4621. if (FAILED(hr))
  4622. {
  4623. *ppv = NULL;
  4624. }
  4625. return hr;
  4626. }
  4627. //
  4628. // Initialize the "config items" object.
  4629. // This loads all of the user preference/policy information for the page when
  4630. // the page is first created.
  4631. //
  4632. void
  4633. COfflineFilesPage::CConfigItems::Load(
  4634. void
  4635. )
  4636. {
  4637. #define LOADCFG(i, f) m_rgItems[i].dwValue = DWORD(c.f(&m_rgItems[i].bSetByPolicy))
  4638. CConfig& c = CConfig::GetSingleton();
  4639. LOADCFG(iCFG_NOCONFIGCACHE, NoConfigCache);
  4640. LOADCFG(iCFG_SYNCATLOGOFF, SyncAtLogoff);
  4641. LOADCFG(iCFG_SYNCATLOGON, SyncAtLogon);
  4642. LOADCFG(iCFG_NOREMINDERS, NoReminders);
  4643. LOADCFG(iCFG_REMINDERFREQMINUTES, ReminderFreqMinutes);
  4644. LOADCFG(iCFG_DEFCACHESIZE, DefaultCacheSize);
  4645. LOADCFG(iCFG_NOCACHEVIEWER, NoCacheViewer);
  4646. LOADCFG(iCFG_CSCENABLED, CscEnabled);
  4647. LOADCFG(iCFG_ENCRYPTCACHE, EncryptCache);
  4648. #undef LOADCFG
  4649. }