Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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