|
|
//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation, 1997 - 1999
//
// File: options.cpp
//
//--------------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////////
/* File: options.cpp
Description: Displays a property-sheet-like dialog containing optional settings for CSC.
Classes: COfflineFilesPage - Contains basic CSC settings. Designed to be dynamically added to the shell's View->Folder Options property sheet.
CustomGOAAddDlg - Dialog for adding custom go-offline actions to the "advanced" dialog.
CustomGOAEditDlg - Dialog for editing custom go-offline actions in the "advanced" dialog.
CscOptPropSheetExt - Shell property sheet extension object for adding the COfflineFilesPage to the shell's View->Folder Options property sheet.
Revision History:
Date Description Programmer -------- --------------------------------------------------- ---------- 12/03/97 Initial creation. BrianAu 05/28/97 Removed CscOptPropSheet class. Obsolete. BrianAu Renamed AdvancedPage to CAdvOptDlg. This better reflects the new behavior of the "advanced" dlg as a dialog rather than a property page as first designed. 07/29/98 Removed CscOptPropPage class. Now we only have BrianAu a single prop page so there was no reason for a common base class implementation. All base class functionality has been moved up into the COfflineFilesPage class. Renamed "GeneralPage" class to "COfflineFilesPage" to reflect the current naming in the UI. 08/21/98 Added PurgeCache and PurgeCacheCallback. BrianAu 08/27/98 Options dialog re-layout per PM changes. BrianAu - Replaced part/full sync radio buttons with cbx. - Added reminder balloon controls. 03/30/00 Added support for cache encryption. BrianAu */ ///////////////////////////////////////////////////////////////////////////////
#include "pch.h"
#pragma hdrstop
#include <math.h>
#include <prsht.h>
#include <resource.h>
#include <winnetwk.h>
#include <shlguidp.h>
#include <process.h>
#include <mobsyncp.h>
#include <htmlhelp.h>
#include "options.h"
#include "ccinline.h"
#include "msgbox.h"
#include "filesize.h"
#include "uuid.h"
#include "config.h"
#include "uihelp.h"
#include "cscst.h" // For PWM_SETREMINDERTIMER
#include "util.h" // Utils from "dll" directory.
#include "folder.h"
#include "purge.h"
#include "security.h"
#include "syncmgr.h"
#include "strings.h"
#include "termserv.h"
//
// Simple inline helper. Why this isn't this a Win32 macro?
//
inline void EnableDialogItem(HWND hwnd, UINT idCtl, bool bEnable) { EnableWindow(GetDlgItem(hwnd, idCtl), bEnable); }
//
// This is for assisting the context help functions.
// Determine if the control has it's help text in windows.hlp or
// in our cscui.hlp.
//
bool UseWindowsHelp(int idCtl) { bool bUseWindowsHelp = false; switch(idCtl) { case IDOK: case IDCANCEL: case IDC_STATIC: bUseWindowsHelp = true; break;
default: break; } return bUseWindowsHelp; }
//-----------------------------------------------------------------------------
// COfflineFilesPage
//-----------------------------------------------------------------------------
const DWORD COfflineFilesPage::m_rgHelpIDs[] = { IDC_CBX_ENABLE_CSC, HIDC_CBX_ENABLE_CSC, IDC_CBX_FULLSYNC_AT_LOGON, HIDC_CBX_FULLSYNC_AT_LOGON, IDC_CBX_FULLSYNC_AT_LOGOFF, HIDC_CBX_FULLSYNC_AT_LOGOFF, IDC_CBX_LINK_ON_DESKTOP, HIDC_CBX_LINK_ON_DESKTOP, IDC_CBX_ENCRYPT_CSC, HIDC_CBX_ENCRYPT_CSC, IDC_CBX_REMINDERS, HIDC_REMINDERS_ENABLE, IDC_SPIN_REMINDERS, HIDC_REMINDERS_PERIOD, IDC_TXT_REMINDERS1, DWORD(-1), // "minutes."
IDC_LBL_CACHESIZE_PCT, DWORD(-1), IDC_SLIDER_CACHESIZE_PCT, HIDC_CACHESIZE_PCT, IDC_TXT_CACHESIZE_PCT, DWORD(-1), IDC_BTN_DELETE_CACHE, HIDC_BTN_DELETE_CACHE, IDC_BTN_VIEW_CACHE, HIDC_BTN_VIEW_CACHE, IDC_BTN_ADVANCED, HIDC_BTN_ADVANCED, IDC_STATIC2, DWORD(-1), // Icon
IDC_STATIC3, DWORD(-1), // Icon's text.
0, 0 };
//
// This function is called in response to WM_INITDIALOG. It is also
// called at other times to "reinitialize" the dialog controls to match
// the current CSC configuration. This is why you see several checks
// for uninitialized values throughout the function.
//
BOOL COfflineFilesPage::OnInitDialog( HWND hwnd, HWND hwndFocus, LPARAM lInitParam ) { if (NULL == m_hwndDlg) { m_hwndDlg = hwnd; }
//
// Determine if the user has WRITE access to HKLM.
//
HKEY hkeyLM; DWORD disposition = 0; if (ERROR_SUCCESS == RegCreateKeyEx(HKEY_LOCAL_MACHINE, REGSTR_KEY_OFFLINEFILES, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hkeyLM, &disposition)) { m_bUserHasMachineAccess = true; RegCloseKey(hkeyLM); hkeyLM = NULL; }
m_config.Load();
if (!DisableForTerminalServer()) { //
// "Enable" checkbox. This reflects the true state of CSC.
// Not the state of a registry setting.
//
CheckDlgButton(hwnd, IDC_CBX_ENABLE_CSC, IsCSCEnabled() ? BST_CHECKED : BST_UNCHECKED);
//
// "Sync at logon/logoff action checkboxes.
//
CheckDlgButton(hwnd, IDC_CBX_FULLSYNC_AT_LOGON, CConfig::eSyncFull == m_config.SyncAtLogon() ? BST_CHECKED : BST_UNCHECKED);
CheckDlgButton(hwnd, IDC_CBX_FULLSYNC_AT_LOGOFF, CConfig::eSyncFull == m_config.SyncAtLogoff() ? BST_CHECKED : BST_UNCHECKED); //
// Configure the "reminder" controls.
//
HWND hwndSpin = GetDlgItem(hwnd, IDC_SPIN_REMINDERS); HWND hwndEdit = GetDlgItem(hwnd, IDC_EDIT_REMINDERS); SendMessage(hwndSpin, UDM_SETRANGE, 0, MAKELONG((short)9999, (short)1)); SendMessage(hwndSpin, UDM_SETBASE, 10, 0);
UDACCEL rgAccel[] = {{ 2, 1 }, { 4, 10 }, 6, 100};
SendMessage(hwndSpin, UDM_SETACCEL, (WPARAM)ARRAYSIZE(rgAccel), (LPARAM)rgAccel);
SendMessage(hwndEdit, EM_SETLIMITTEXT, 4, 0);
CheckDlgButton(hwnd, IDC_CBX_REMINDERS, m_config.NoReminders() ? BST_UNCHECKED : BST_CHECKED);
SetDlgItemInt(hwnd, IDC_EDIT_REMINDERS, m_config.ReminderFreqMinutes(), FALSE);
if (IsLinkOnDesktop()) { CheckDlgButton(hwnd, IDC_CBX_LINK_ON_DESKTOP, BST_CHECKED); } //
// Create tooltip for "Encrypt cache" checkbox.
// If it should be initially visible, that is done
// in response to PSN_SETACTIVE.
//
CreateEncryptionTooltip(); //
// Update the "Encrypt" checkbox.
//
UpdateEncryptionCheckbox(); //
// "Cache Size" slider
//
CSCSPACEUSAGEINFO sui; GetCscSpaceUsageInfo(&sui);
m_hwndSlider = GetDlgItem(hwnd, IDC_SLIDER_CACHESIZE_PCT); InitSlider(hwnd, sui.llBytesOnVolume, sui.llBytesTotalInCache);
//
// Determine if the volume hosting the CSC database supports encryption.
//
m_bCscVolSupportsEncryption = CscVolumeSupportsEncryption(sui.szVolume);
HWND hwndParent = GetParent(hwnd); if (NULL == m_pfnOldPropSheetWndProc) { //
// Subclass the propsheet itself so we can intercept move messages
// and adjust the balloon tip position when the dialog is moved.
//
m_pfnOldPropSheetWndProc = (WNDPROC)SetWindowLongPtr(hwndParent, GWLP_WNDPROC, (LONG_PTR)PropSheetSubclassWndProc); SetProp(hwndParent, c_szPropThis, (HANDLE)this); } if (NULL == m_pfnOldEncryptionTooltipWndProc) { //
// Subclass the tooltip balloon so we can make it pop when selected.
// Tracking tooltips don't pop themselves when clicked. You have
// to do it for them.
//
m_pfnOldEncryptionTooltipWndProc = (WNDPROC)SetWindowLongPtr(m_hwndEncryptTooltip, GWLP_WNDPROC, (LONG_PTR)EncryptionTooltipSubclassWndProc); SetProp(m_hwndEncryptTooltip, c_szPropThis, (HANDLE)this); } } //
// Save away the initial page state. This will be used to
// determine when to enable the "Apply" button. See
// HandlePageStateChange().
//
GetPageState(&m_state); return TRUE; }
INT_PTR CALLBACK COfflineFilesPage::DlgProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam ) { BOOL bResult = FALSE;
//
// Retrieve the "this" pointer from the dialog's userdata.
// It was placed there in OnInitDialog().
//
COfflineFilesPage *pThis = (COfflineFilesPage *)GetWindowLongPtr(hDlg, DWLP_USER);
switch(message) { case WM_INITDIALOG: { PROPSHEETPAGE *pPage = (PROPSHEETPAGE *)lParam; pThis = (COfflineFilesPage *)pPage->lParam;
TraceAssert(NULL != pThis); SetWindowLongPtr(hDlg, DWLP_USER, (LONG_PTR)pThis); bResult = pThis->OnInitDialog(hDlg, (HWND)wParam, lParam); break; }
case WM_NOTIFY: TraceAssert(NULL != pThis); bResult = pThis->OnNotify(hDlg, (int)wParam, (LPNMHDR)lParam); break;
case WM_COMMAND: if (NULL != pThis) bResult = pThis->OnCommand(hDlg, HIWORD(wParam), LOWORD(wParam), (HWND)lParam); break;
case WM_HELP: TraceAssert(NULL != pThis); bResult = pThis->OnHelp(hDlg, (LPHELPINFO)lParam); break;
case WM_CONTEXTMENU: TraceAssert(NULL != pThis); bResult = pThis->OnContextMenu((HWND)wParam, LOWORD(lParam), HIWORD(lParam)); break;
case WM_DESTROY: TraceAssert(NULL != pThis); bResult = pThis->OnDestroy(hDlg); break;
case WM_SETTINGCHANGE: case WM_SYSCOLORCHANGE: TraceAssert(NULL != pThis); bResult = pThis->OnSettingChange(hDlg, message, wParam, lParam); break;
case WM_MOVE: TraceAssert(NULL != pThis); pThis->TrackEncryptionTooltip(); break;
case WM_HSCROLL: //
// The cache-size slider generates horizontal scroll messages.
//
TraceAssert(NULL != pThis); pThis->OnHScroll(hDlg, (HWND)lParam, // hwndSlider
(int)LOWORD(wParam), // notify code
(int)HIWORD(wParam)); // thumb pos
break;
default: break; } return bResult; }
//
// Subclass window proc for the property sheet.
// We intercept WM_MOVE messages and update the position of
// the balloon tooltip to follow the movement of the property
// page.
//
LRESULT COfflineFilesPage::PropSheetSubclassWndProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { COfflineFilesPage *pThis = (COfflineFilesPage *)GetProp(hwnd, c_szPropThis); TraceAssert(NULL != pThis); switch(uMsg) { case WM_MOVE: if (pThis->m_hwndEncryptTooltip && IsWindowVisible(pThis->m_hwndEncryptTooltip)) { pThis->TrackEncryptionTooltip(); } break;
default: break; } return CallWindowProc(pThis->m_pfnOldPropSheetWndProc, hwnd, uMsg, wParam, lParam); }
BOOL COfflineFilesPage::OnDestroy( HWND hwnd ) { //
// Remove window properties and cancel subclassing set in OnInitDialog.
//
HWND hwndParent = GetParent(hwnd); if (NULL != m_pfnOldPropSheetWndProc) SetWindowLongPtr(hwndParent, GWLP_WNDPROC, (LONG_PTR)m_pfnOldPropSheetWndProc);
RemoveProp(hwndParent, c_szPropThis);
if (NULL != m_hwndEncryptTooltip) { if (NULL != m_pfnOldEncryptionTooltipWndProc) { SetWindowLongPtr(m_hwndEncryptTooltip, GWLP_WNDPROC, (LONG_PTR)m_pfnOldEncryptionTooltipWndProc); } RemoveProp(m_hwndEncryptTooltip, c_szPropThis); } return FALSE; }
//
// Forward all WM_SETTINGCHANGE and WM_SYSCOLORCHANGE messages
// to controls that need to stay in sync with color changes.
//
BOOL COfflineFilesPage::OnSettingChange( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ) { HWND rghwndCtls[] = { m_hwndSlider };
for (int i = 0; i < ARRAYSIZE(rghwndCtls); i++) { SendMessage(rghwndCtls[i], uMsg, wParam, lParam); } return TRUE; }
BOOL COfflineFilesPage::OnHelp( HWND hDlg, LPHELPINFO pHelpInfo ) { if (HELPINFO_WINDOW == pHelpInfo->iContextType) { int idCtl = GetDlgCtrlID((HWND)pHelpInfo->hItemHandle); WinHelp((HWND)pHelpInfo->hItemHandle, UseWindowsHelp(idCtl) ? NULL : c_szHelpFile, HELP_WM_HELP, (DWORD_PTR)((LPTSTR)m_rgHelpIDs)); } return TRUE; }
BOOL COfflineFilesPage::OnContextMenu( HWND hwndItem, int xPos, int yPos ) { int idCtl = GetDlgCtrlID(hwndItem); WinHelp(hwndItem, UseWindowsHelp(idCtl) ? NULL : c_szHelpFile, HELP_CONTEXTMENU, (DWORD_PTR)((LPTSTR)m_rgHelpIDs));
return FALSE; }
UINT CALLBACK COfflineFilesPage::PageCallback( HWND hwnd, UINT uMsg, LPPROPSHEETPAGE ppsp ) { UINT uReturn = 1; COfflineFilesPage *pThis = (COfflineFilesPage *)ppsp->lParam; TraceAssert(NULL != pThis);
switch(uMsg) { case PSPCB_CREATE: //
// uReturn == 0 means Don't create the prop page.
//
uReturn = 1; break;
case PSPCB_RELEASE: //
// This will release the extension and call the virtual
// destructor (which will destroy the prop page object).
//
pThis->m_pUnkOuter->Release(); break; } return uReturn; }
BOOL COfflineFilesPage::OnCommand( HWND hwnd, WORD wNotifyCode, WORD wID, HWND hwndCtl ) { BOOL bResult = TRUE; switch(wNotifyCode) { case BN_CLICKED: switch(wID) { case IDC_CBX_ENCRYPT_CSC: //
// The "Encrypt cache" checkbox is the 3-state flavor so that
// we can represent the following states:
//
// CHECKED == "encrypted"
// UNCHECKED == "decrypted"
// INDETERMINATE == "partially encrypted or partially decrypted"
//
// We don't allow the user to set the checkbox state to
// "indeterminate". It can only become "indeterminate" through
// initialization in OnInitDialog. Successive selections of a
// checkbox cycle through the following states:
//
// "checked"->"indeterminate"->"unchecked"->"checked"...
//
// Therefore, if the state is "indeterminate" following a user click
// we force it to "unchecked". This way the checkbox can represent
// three states but the user has control of only two (checked and
// unchecked).
//
if (BST_INDETERMINATE == IsDlgButtonChecked(hwnd, wID)) { CheckDlgButton(hwnd, wID, BST_UNCHECKED); } //
// The encryption tooltip only appears when the checkbox is in
// the INDETERMINATE state. Since we've just either checked
// or unchecked it, the tooltip must disappear.
//
HideEncryptionTooltip();
HandlePageStateChange(); bResult = FALSE; break;
case IDC_CBX_ENABLE_CSC: if (IsDlgButtonChecked(m_hwndDlg, IDC_CBX_ENABLE_CSC)) { //
// Checked the "enable CSC" checkbox.
// Set the cache size slider to the default pct-used value (10%)
//
TrackBar_SetPos(m_hwndSlider, ThumbAtPctDiskSpace(0.10), true); SetCacheSizeDisplay(GetDlgItem(m_hwndDlg, IDC_TXT_CACHESIZE_PCT), TrackBar_GetPos(m_hwndSlider)); CheckDlgButton(hwnd, IDC_CBX_LINK_ON_DESKTOP, IsLinkOnDesktop() ? BST_CHECKED : BST_UNCHECKED); } else { //
// If CSC is disabled we remove the Offline Files
// folder shortcut from the user's desktop.
//
CheckDlgButton(hwnd, IDC_CBX_LINK_ON_DESKTOP, BST_UNCHECKED); } //
// Fall through...
//
case IDC_CBX_REMINDERS: EnableCtls(hwnd); //
// Fall through...
//
case IDC_EDIT_REMINDERS: case IDC_CBX_FULLSYNC_AT_LOGOFF: case IDC_CBX_FULLSYNC_AT_LOGON: case IDC_SLIDER_CACHESIZE_PCT: case IDC_CBX_LINK_ON_DESKTOP: HandlePageStateChange(); bResult = FALSE; break;
case IDC_BTN_VIEW_CACHE: COfflineFilesFolder::Open(); bResult = FALSE; break;
case IDC_BTN_DELETE_CACHE: //
// Ctl-Shift when pressing "Delete Files..."
// is a special entry to reformatting the cache.
//
if ((0x8000 & GetAsyncKeyState(VK_SHIFT)) && (0x8000 & GetAsyncKeyState(VK_CONTROL))) { OnFormatCache(); } else { OnDeleteCache(); } bResult = FALSE; break;
case IDC_BTN_ADVANCED: { CAdvOptDlg dlg(m_hInstance, m_hwndDlg); dlg.Run(); break; } default: break; } break; case EN_UPDATE: if (IDC_EDIT_REMINDERS == wID) { static bool bResetting; // prevent reentrancy.
if (!bResetting) { //
// The edit control is configured for a max of 4 digits and
// numbers-only. Therefore the user can enter anything between
// 0 and 9999. We don't want to allow 0 so we need this extra
// check. The spinner has been set for a range of 0-9999.
//
int iValue = GetDlgItemInt(hwnd, IDC_EDIT_REMINDERS, NULL, FALSE); if (0 == iValue) { bResetting = true; SetDlgItemInt(hwnd, IDC_EDIT_REMINDERS, 1, FALSE); bResetting = false; } } HandlePageStateChange(); } break; } return bResult; }
//
// Gather the state of the page and store it in a PgState object.
//
void COfflineFilesPage::GetPageState( PgState *pps ) { pps->SetCscEnabled(BST_CHECKED == IsDlgButtonChecked(m_hwndDlg, IDC_CBX_ENABLE_CSC)); pps->SetLinkOnDesktop(BST_CHECKED == IsDlgButtonChecked(m_hwndDlg, IDC_CBX_LINK_ON_DESKTOP)); pps->SetEncrypted(IsDlgButtonChecked(m_hwndDlg, IDC_CBX_ENCRYPT_CSC)); pps->SetFullSyncAtLogon(BST_CHECKED == IsDlgButtonChecked(m_hwndDlg, IDC_CBX_FULLSYNC_AT_LOGON)); pps->SetFullSyncAtLogoff(BST_CHECKED == IsDlgButtonChecked(m_hwndDlg, IDC_CBX_FULLSYNC_AT_LOGOFF)); pps->SetSliderPos(TrackBar_GetPos(m_hwndSlider)); pps->SetRemindersEnabled(BST_CHECKED == IsDlgButtonChecked(m_hwndDlg, IDC_CBX_REMINDERS)); pps->SetReminderFreq(GetDlgItemInt(m_hwndDlg, IDC_EDIT_REMINDERS, NULL, FALSE)); }
void COfflineFilesPage::HandlePageStateChange( void ) { PgState s; GetPageState(&s); if (s == m_state) PropSheet_UnChanged(GetParent(m_hwndDlg), m_hwndDlg); else PropSheet_Changed(GetParent(m_hwndDlg), m_hwndDlg); }
//
// Handle horizontal scroll messages generated by the cache-size slider.
//
void COfflineFilesPage::OnHScroll( HWND hwndDlg, HWND hwndCtl, int iCode, int iPos ) { if (TB_THUMBPOSITION != iCode && TB_THUMBTRACK != iCode) iPos = TrackBar_GetPos(hwndCtl);
SetCacheSizeDisplay(GetDlgItem(hwndDlg, IDC_TXT_CACHESIZE_PCT), iPos); if (TB_ENDTRACK == iCode) HandlePageStateChange(); }
//
// Update the cache size display "95.3 MB (23% of drive)" string.
//
void COfflineFilesPage::SetCacheSizeDisplay( HWND hwndCtl, int iThumbPos ) { //
// First convert the thumb position to a disk space value.
//
TCHAR szSize[40]; FileSize fs(DiskSpaceAtThumb(iThumbPos)); fs.GetString(szSize, ARRAYSIZE(szSize)); //
// Convert the thumb position to a percent-disk space value.
//
double x = 0.0; if (0 < iThumbPos) x = MAX(1.0, Rx(iThumbPos) * 100.00); //
// Convert the percent-disk space value to a text string.
//
TCHAR szPct[10]; wnsprintf(szPct, ARRAYSIZE(szPct), TEXT("%d"), (DWORD)x); //
// Format the result and display in the dialog.
//
LPTSTR pszText; if (0 < FormatStringID(&pszText, m_hInstance, IDS_FMT_CACHESIZE_DISPLAY, szSize, szPct)) { SetWindowText(hwndCtl, pszText); LocalFree(pszText); } }
void COfflineFilesPage::InitSlider( HWND hwndDlg, LONGLONG llSpaceMax, LONGLONG llSpaceUsed ) { double pctUsed = 0.0; // Default
//
// Protect against:
// 1. Div-by-zero
// 2. Invalid FP operation. (i.e. 0.0 / 0.0)
//
if (0 != llSpaceMax) pctUsed = double(llSpaceUsed) / double(llSpaceMax);
//
// Change the resolution of the slider as drives get larger.
//
m_iSliderMax = 100; // < 1GB
if (llSpaceMax > 0x0000010000000000i64) m_iSliderMax = 500; // >= 1TB
else if (llSpaceMax > 0x0000000040000000i64) m_iSliderMax = 300; // >= 1GB
m_llAvailableDiskSpace = llSpaceMax;
TrackBar_SetTicFreq(m_hwndSlider, m_iSliderMax / 10); TrackBar_SetPageSize(m_hwndSlider, m_iSliderMax / 10); TrackBar_SetRange(m_hwndSlider, 0, m_iSliderMax, false); TrackBar_SetPos(m_hwndSlider, ThumbAtPctDiskSpace(pctUsed), true); SetCacheSizeDisplay(GetDlgItem(hwndDlg, IDC_TXT_CACHESIZE_PCT), TrackBar_GetPos(m_hwndSlider)); }
//
// Enable/disable page controls.
//
void COfflineFilesPage::EnableCtls( HWND hwnd ) {
typedef bool (CConfigItems::*PBMF)(void) const;
static const struct { UINT idCtl; PBMF pfnRestricted; bool bRequiresMachineAccess;
} rgCtls[] = { { IDC_CBX_FULLSYNC_AT_LOGOFF, &CConfigItems::NoConfigSyncAtLogoff, false }, { IDC_CBX_FULLSYNC_AT_LOGON, &CConfigItems::NoConfigSyncAtLogon, false }, { IDC_CBX_REMINDERS, &CConfigItems::NoConfigReminders, false }, { IDC_CBX_LINK_ON_DESKTOP, NULL, false }, { IDC_CBX_ENCRYPT_CSC, &CConfigItems::NoConfigEncryptCache, true }, { IDC_TXT_CACHESIZE_PCT, NULL, true }, { IDC_SLIDER_CACHESIZE_PCT, &CConfigItems::NoConfigCacheSize, true }, { IDC_LBL_CACHESIZE_PCT, &CConfigItems::NoConfigCacheSize, true }, { IDC_BTN_VIEW_CACHE, NULL, false }, { IDC_BTN_ADVANCED, NULL, false }, { IDC_BTN_DELETE_CACHE, NULL, false } };
bool bCscEnabled = BST_CHECKED == IsDlgButtonChecked(hwnd, IDC_CBX_ENABLE_CSC); bool bEnable; for (int i = 0; i < ARRAYSIZE(rgCtls); i++) { bEnable = bCscEnabled; if (bEnable) { if (rgCtls[i].bRequiresMachineAccess && !m_bUserHasMachineAccess) { bEnable = false; } if (bEnable) { //
// Apply any policy restrictions.
//
PBMF pfn = rgCtls[i].pfnRestricted; if (NULL != pfn && (m_config.*pfn)()) bEnable = false;
if (bEnable) { //
// "View..." button requires special handling as it isn't based off of a
// boolean restriction function.
//
if ((IDC_BTN_VIEW_CACHE == rgCtls[i].idCtl || IDC_CBX_LINK_ON_DESKTOP == rgCtls[i].idCtl) && m_config.NoCacheViewer()) { bEnable = false; } else if (IDC_CBX_ENCRYPT_CSC == rgCtls[i].idCtl) { //
// "Encrypt offline files" checkbox requires special handling.
//
// Cache encryption cannot be performed with CSC disabled or
// if the CSC volume doesn't support encryption or if the user
// is not an administrator.
//
if (!bCscEnabled || !m_bCscVolSupportsEncryption || !IsCurrentUserAnAdminMember()) { bEnable = false; } } } } } EnableDialogItem(hwnd, rgCtls[i].idCtl, bEnable); }
//
// Reminder controls are dependent upon several inputs.
//
bEnable = bCscEnabled && (BST_CHECKED == IsDlgButtonChecked(hwnd, IDC_CBX_REMINDERS)) && !m_config.NoConfigReminders() && !m_config.NoConfigReminderFreqMinutes();
EnableDialogItem(hwnd, IDC_TXT_REMINDERS1, bEnable); EnableDialogItem(hwnd, IDC_EDIT_REMINDERS, bEnable); EnableDialogItem(hwnd, IDC_SPIN_REMINDERS, bEnable); //
// "Enabled" checkbox requires special handling.
// It can't be included with the other controls because it will be disabled
// when the user unchecks it. Then there's no way to re-enable it.
// Disable the checkbox if any of the following is true:
// 1. Admin policy has enabled/disabled CSC.
// 2. User doesn't have WRITE access to HKLM.
//
bEnable = !m_config.NoConfigCscEnabled() && m_bUserHasMachineAccess;
EnableWindow(GetDlgItem(hwnd, IDC_CBX_ENABLE_CSC), bEnable); }
BOOL COfflineFilesPage::OnNotify( HWND hDlg, int idCtl, LPNMHDR pnmhdr ) { BOOL bResult = TRUE;
switch(pnmhdr->code) { case PSN_APPLY: //
// Prevent re-entrancy. If the user changes the encryption
// setting and presses "OK", the prop sheet will remain visible
// during the encryption operation. Since we're displaying a progress
// dialog and pumping messages, it's possible for the user to
// re-select the "OK" or "Apply" buttons during the encryption.
// Use a simple flag variable to prevent re-entrancy.
//
if (!m_bApplyingSettings) { m_bApplyingSettings = true; //
// If the lParam is TRUE, the property sheet is closing.
//
bResult = ApplySettings(hDlg, boolify(((LPPSHNOTIFY)pnmhdr)->lParam)); m_bApplyingSettings = false; } break;
case PSN_KILLACTIVE: //
// Hide the tooltip when the page is deactivated.
//
HideEncryptionTooltip(); SetWindowLongPtr(hDlg, DWLP_MSGRESULT, 0); bResult = FALSE; break;
case PSN_SETACTIVE: //
// Enable/disable controls whenever the page becomes active.
//
EnableCtls(hDlg); //
// Display the encryption tooltip balloon if necessary
// on the FIRST page activation only.
// Note that we need to do this here rather than in OnInitDialog
// to prevent the balloon from 'hopping' when the property sheet
// code repositions the page.
//
if (m_bFirstActivate) { UpdateEncryptionTooltipBalloon(); m_bFirstActivate = false; } SetWindowLongPtr(hDlg, DWLP_MSGRESULT, 0); bResult = FALSE; break;
case PSN_TRANSLATEACCELERATOR: //
// User pressed a key.
// Hide the tooltip.
//
HideEncryptionTooltip(); break;
case TTN_GETDISPINFO: OnTTN_GetDispInfo((LPNMTTDISPINFO)pnmhdr); break;
default: break;
} return bResult; }
LRESULT COfflineFilesPage::EncryptionTooltipSubclassWndProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { COfflineFilesPage *pThis = (COfflineFilesPage *)GetProp(hwnd, c_szPropThis); TraceAssert(NULL != pThis);
switch(uMsg) { case WM_LBUTTONDOWN: case WM_RBUTTONDOWN: //
// When the tooltip balloon is clicked, pop the balloon.
//
pThis->HideEncryptionTooltip(); //
// Fall through...
//
default: break; } return CallWindowProc(pThis->m_pfnOldEncryptionTooltipWndProc, hwnd, uMsg, wParam, lParam); }
//
// Create a tooltip for a given control.
// The parent of the control is required to respond to TTN_GETDISPINFO
// and provide the text.
//
void COfflineFilesPage::CreateEncryptionTooltip( void ) { if (NULL == m_hwndEncryptTooltip) { INITCOMMONCONTROLSEX iccex; iccex.dwICC = ICC_WIN95_CLASSES; iccex.dwSize = sizeof(INITCOMMONCONTROLSEX); InitCommonControlsEx(&iccex);
m_hwndEncryptTooltip = CreateWindowEx(NULL, TOOLTIPS_CLASS, NULL, WS_POPUP | TTS_NOPREFIX | TTS_BALLOON, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, GetDlgItem(m_hwndDlg, IDC_CBX_ENCRYPT_CSC), NULL, m_hInstance, NULL); if (NULL != m_hwndEncryptTooltip) { TOOLINFO ti; ti.cbSize = sizeof(TOOLINFO); ti.uFlags = TTF_TRACK | TTF_ABSOLUTE; ti.hwnd = m_hwndDlg; ti.uId = IDC_CBX_ENCRYPT_CSC; ti.lpszText = LPSTR_TEXTCALLBACK; ti.hinst = NULL; ti.lParam = 0; SendMessage(m_hwndEncryptTooltip, TTM_ADDTOOL, 0, (LPARAM)(LPTOOLINFO)&ti); //
// Set the tooltip width to 3/4 the dialog width.
//
RECT rcDlg; GetClientRect(m_hwndDlg, &rcDlg); SendMessage(m_hwndEncryptTooltip, TTM_SETMAXTIPWIDTH, 0, (LPARAM)(((rcDlg.right-rcDlg.left) * 3) / 4)); } } }
void COfflineFilesPage::OnTTN_GetDispInfo( LPNMTTDISPINFO pttdi ) { LPNMHDR pnmhdr = (LPNMHDR)pttdi; BOOL bResult = TRUE;
UINT idCtl = (UINT)(UINT_PTR)pnmhdr->idFrom; if (TTF_IDISHWND & pttdi->uFlags) { idCtl = GetDlgCtrlID((HWND)pnmhdr->idFrom); } if (IDC_CBX_ENCRYPT_CSC == idCtl) { //
// Provide the text and image for the encryption tooltip.
//
//
// These constants are standard for TTM_SETTITLE.
//
enum TTICON { TTICON_NONE, TTICON_INFO, TTICON_WARNING, TTICON_ERROR }; //
// Map of state to body text.
//
const UINT rgBodyText[][2] = { // -------------- Decryption ------------ ---------- Encryption ------------------
{ IDS_TT_BODY_DECRYPTED_PARTIAL_NONADMIN, IDS_TT_BODY_ENCRYPTED_PARTIAL_NONADMIN }, // Non-admin user
{ IDS_TT_BODY_DECRYPTED_PARTIAL, IDS_TT_BODY_ENCRYPTED_PARTIAL } // Admin user
}; //
// Map of state to title text and icon.
//
const struct { UINT idsTitle; // Title text
int iIcon; // TTICON_XXXX
} rgTitleAndIcon[] = { { IDS_TT_TITLE_DECRYPTED_PARTIAL, TTICON_INFO }, // Decryption
{ IDS_TT_TITLE_ENCRYPTED_PARTIAL, TTICON_WARNING } // Encryption
};
const BOOL bEncrypted = IsCacheEncrypted(NULL); //
// For non-admin users, the "Encrypt CSC" checkbox is disabled.
//
const BOOL bCbxEncryptEnabled = IsWindowEnabled(GetDlgItem(m_hwndDlg, IDC_CBX_ENCRYPT_CSC)); //
// Tooltip body text.
//
m_szEncryptTooltipBody[0] = TEXT('\0'); LoadString(m_hInstance, rgBodyText[int(bCbxEncryptEnabled)][int(bEncrypted)], m_szEncryptTooltipBody, ARRAYSIZE(m_szEncryptTooltipBody));
pttdi->lpszText = m_szEncryptTooltipBody; //
// Tooltip title text and icon.
//
const iIcon = rgTitleAndIcon[int(bEncrypted)].iIcon; LPTSTR pszTitle; if (0 < FormatStringID(&pszTitle, m_hInstance, rgTitleAndIcon[int(bEncrypted)].idsTitle)) { SendMessage(m_hwndEncryptTooltip, TTM_SETTITLE, (WPARAM)iIcon, (LPARAM)pszTitle); LocalFree(pszTitle); } } }
void COfflineFilesPage::ShowEncryptionTooltip( bool bEncrypted ) { if (NULL != m_hwndEncryptTooltip) { //
// Position tooltip correctly before showing
//
TrackEncryptionTooltip(); //
// Show the tooltip.
//
TOOLINFO ti; ti.cbSize = sizeof(ti); ti.hwnd = m_hwndDlg; ti.uId = IDC_CBX_ENCRYPT_CSC;
SendMessage(m_hwndEncryptTooltip, TTM_TRACKACTIVATE, (WPARAM)TRUE, (LPARAM)&ti); SendMessage(m_hwndEncryptTooltip, TTM_UPDATE, 0, 0); } }
void COfflineFilesPage::HideEncryptionTooltip( void ) { if (NULL != m_hwndEncryptTooltip) { SendMessage(m_hwndEncryptTooltip, TTM_TRACKACTIVATE, (WPARAM)FALSE, 0); } }
void COfflineFilesPage::TrackEncryptionTooltip( void ) { //
// Point the tip stem at center of the lower edge of the encryption
// checkbox.
// The Windows UX manual says checkboxes are 10 dialog units wide.
//
if (NULL != m_hwndEncryptTooltip) { const INT DialogBaseUnitsX = LOWORD(GetDialogBaseUnits()); const INT cxCbx = (DialogBaseUnitsX * 10) / 4; RECT rc;
GetWindowRect(GetDlgItem(m_hwndDlg, IDC_CBX_ENCRYPT_CSC), &rc);
SendMessage(m_hwndEncryptTooltip, TTM_TRACKPOSITION, 0, (LPARAM)(DWORD)MAKELONG(rc.left + (cxCbx / 2), rc.bottom)); } }
//
// Set the state of the "Encrypt Cache" checkbox to reflect
// the actual state of cache encryption. Also display
// the balloon tooltip if the checkbox is in the
// indeterminate state.
//
void COfflineFilesPage::UpdateEncryptionCheckboxOrBalloon( bool bCheckbox ) { //
// "Encrypt CSC" checkbox.
// The display logic is captured in this table.
//
const UINT rgCheck[] = { BST_UNCHECKED, // 00 = Decrypted,
BST_INDETERMINATE, // 01 = Partially decrypted
BST_CHECKED, // 10 = Encrypted,
BST_INDETERMINATE // 11 = Partially encrypted
};
BOOL bPartial = FALSE; const BOOL bEncrypted = IsCacheEncrypted(&bPartial); const int iState = (int(bEncrypted) << 1) | int(bPartial);
if (bCheckbox) { //
// Update the checkbox.
//
CheckDlgButton(m_hwndDlg, IDC_CBX_ENCRYPT_CSC, rgCheck[iState]); } else { //
// Update the tooltip
//
if (BST_INDETERMINATE == rgCheck[iState]) { ShowEncryptionTooltip(boolify(bEncrypted)); } else { HideEncryptionTooltip(); } } }
void COfflineFilesPage::UpdateEncryptionCheckbox( void ) { UpdateEncryptionCheckboxOrBalloon(true); }
void COfflineFilesPage::UpdateEncryptionTooltipBalloon( void ) { UpdateEncryptionCheckboxOrBalloon(false); }
BOOL COfflineFilesPage::ApplySettings( HWND hwnd, bool bPropSheetClosing ) { //
// Query the current state of controls on the page to see if
// anything has changed.
//
PgState s; GetPageState(&s); if (s != m_state) { //
// Something on the page has changed.
// Open the reg keys.
//
HKEY hkeyLM = NULL; HKEY hkeyCU = NULL; DWORD dwDisposition; DWORD dwResult = RegCreateKeyEx(HKEY_LOCAL_MACHINE, REGSTR_KEY_OFFLINEFILES, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hkeyLM, &dwDisposition); if (ERROR_SUCCESS != dwResult) { Trace((TEXT("Error %d opening NetCache machine settings key"), dwResult)); //
// Continue...
// Note that EnableCtls has disabled any controls that require
// WRITE access to HKLM.
//
} dwResult = RegCreateKeyEx(HKEY_CURRENT_USER, REGSTR_KEY_OFFLINEFILES, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hkeyCU, &dwDisposition);
if (ERROR_SUCCESS != dwResult) { //
// Failure to open HKCU is a problem. No use in proceeding.
//
Trace((TEXT("Error %d opening NetCache user settings key"), dwResult)); RegCloseKey(hkeyLM); return FALSE; } //
// Handle encryption/decryption of the cache (part 1).
// Encryption/decryption can only be done when CSC is enabled.
// Therefore, since the user can change both the "enabled" and
// "encrypted" state from the same property page we need to be smart
// about when to do the encryption. We may need to do it before
// disabling CSC or after enabling CSC.
//
bool bEncryptOperationPerformed = false; if (m_state.GetCscEnabled() && !s.GetCscEnabled()) { //
// User is disabling CSC. If they also want to change the cache
// encryption state we must do it now while CSC is enabled.
//
_ApplyEncryptionSetting(hkeyLM, hkeyCU, s, bPropSheetClosing, &bEncryptOperationPerformed); }
bool bUpdateSystrayUI = false; _ApplyEnabledSetting(hkeyLM, hkeyCU, s, &bUpdateSystrayUI);
//
// Handle encryption/decryption of the cache (part 2).
//
if (!bEncryptOperationPerformed) { //
// Encryption has not yet been performed. If user wants to change encryption
// state, do it now.
// Note that if the user enabled CSC and that enabling failed, encryption
// will also fail. Not a worry since the probability that CSC will fail
// to be enabled is extrememly low (I've never seen it fail). If it does
// the encryption process will display an error message.
//
_ApplyEncryptionSetting(hkeyLM, hkeyCU, s, bPropSheetClosing, &bEncryptOperationPerformed); }
//
// Write "sync-at-logon/logoff" (quick vs. full) settings.
//
_ApplySyncAtLogonSetting(hkeyLM, hkeyCU, s); _ApplySyncAtLogoffSetting(hkeyLM, hkeyCU, s); //
// Write the various "reminders" settings.
//
_ApplyReminderSettings(hkeyLM, hkeyCU, s); //
// Create or remove the folder link on the desktop.
//
_ApplyFolderLinkSetting(hkeyLM, hkeyCU, s); //
// Write cache size as pct * 10,000.
//
_ApplyCacheSizeSetting(hkeyLM, hkeyCU, s); //
// Refresh the cached page state info.
//
GetPageState(&m_state); //
// Update the SysTray icon if necessary.
//
if (bUpdateSystrayUI) { HWND hwndNotify = NULL; if (!s.GetCscEnabled()) { //
// If we're disabling CSC, refresh the shell windows BEFORE we
// destroy the SysTray CSCUI "service".
//
hwndNotify = _FindNotificationWindow(); if (IsWindow(hwndNotify)) { SendMessage(hwndNotify, PWM_REFRESH_SHELL, 0, 0); } }
HWND hwndSysTray = FindWindow(SYSTRAY_CLASSNAME, NULL); if (IsWindow(hwndSysTray)) { SendMessage(hwndSysTray, STWM_ENABLESERVICE, STSERVICE_CSC, s.GetCscEnabled()); }
if (s.GetCscEnabled()) { SHLoadNonloadedIconOverlayIdentifiers();
//
// If we're enabling CSC, refresh the shell windows AFTER we
// create the SysTray CSCUI "service".
//
hwndNotify = _FindNotificationWindow(); if (IsWindow(hwndNotify)) { PostMessage(hwndNotify, PWM_REFRESH_SHELL, 0, 0); } } } if (hkeyLM) { RegCloseKey(hkeyLM); } if (hkeyCU) { RegCloseKey(hkeyCU); } } return TRUE; }
HRESULT COfflineFilesPage::_ApplyEnabledSetting( HKEY hkeyLM, HKEY hkeyCU, const PgState& pgstNow, bool *pbUpdateSystrayUI ) { HRESULT hr = S_OK; *pbUpdateSystrayUI = false;
//
// Process the "enabled" setting even if the page state hasn't
// changed. This is a special case because we initialize the
// "enabled" checkbox from IsCSCEnabled() but we change the
// enabled/disabled state by setting a registry value and
// possibly rebooting.
//
DWORD dwValue = DWORD(pgstNow.GetCscEnabled()); DWORD dwResult = RegSetValueEx(hkeyLM, REGSTR_VAL_CSCENABLED, 0, REG_DWORD, (CONST BYTE *)&dwValue, sizeof(dwValue)); hr = HRESULT_FROM_WIN32(dwResult); if (FAILED(hr)) { Trace((TEXT("Error 0x%08X setting reg value \"%s\""), hr, REGSTR_VAL_CSCENABLED)); }
//
// Handle any enabling/disabling of CSC.
//
if (pgstNow.GetCscEnabled() != boolify(IsCSCEnabled())) { bool bReboot = false; DWORD dwError = ERROR_SUCCESS; if (EnableOrDisableCsc(pgstNow.GetCscEnabled(), &bReboot, &dwError)) { if (bReboot) { //
// Requires a reboot.
//
PropSheet_RebootSystem(GetParent(m_hwndDlg)); } else { //
// It's dynamic (no reboot) so update the systray UI.
// Note that we want to update the systray UI AFTER we've
// made any configuration changes to the registry
// (i.e. balloon settings).
//
*pbUpdateSystrayUI = true; } } else { //
// Error trying to enable or disable CSC.
//
CscMessageBox(m_hwndDlg, MB_ICONERROR | MB_OK, Win32Error(dwError), m_hInstance, pgstNow.GetCscEnabled() ? IDS_ERR_ENABLECSC : IDS_ERR_DISABLECSC); } } return hr; }
HRESULT COfflineFilesPage::_ApplySyncAtLogoffSetting( HKEY hkeyLM, HKEY hkeyCU, const PgState& pgstNow ) { //
// Write "sync-at-logoff" (quick vs. full) setting.
//
DWORD dwValue = DWORD(pgstNow.GetFullSyncAtLogoff()); DWORD dwResult = RegSetValueEx(hkeyCU, REGSTR_VAL_SYNCATLOGOFF, 0, REG_DWORD, (CONST BYTE *)&dwValue, sizeof(dwValue)); HRESULT hr = HRESULT_FROM_WIN32(dwResult); if (SUCCEEDED(hr)) { if (!m_state.GetFullSyncAtLogoff() && pgstNow.GetFullSyncAtLogoff()) { //
// If the user has just turned on full sync we want to
// make sure SyncMgr is enabled for sync-at-logoff.
// There are some weirdnesses with doing this but it's the most
// consistent behavior we can offer the user given
// the current design of SyncMgr and CSC. Internal use and beta
// testing shows that users expect Sync-at-logoff to be enabled
// when this checkbox is checked.
//
RegisterSyncMgrHandler(TRUE); RegisterForSyncAtLogonAndLogoff(SYNCMGRREGISTERFLAG_PENDINGDISCONNECT, SYNCMGRREGISTERFLAG_PENDINGDISCONNECT); } } else { Trace((TEXT("Error 0x%08X setting reg value \"%s\""), hr, REGSTR_VAL_SYNCATLOGOFF)); } return hr; }
HRESULT COfflineFilesPage::_ApplySyncAtLogonSetting( HKEY hkeyLM, HKEY hkeyCU, const PgState& pgstNow ) { //
// Write "sync-at-logon" (quick vs. full) setting.
//
DWORD dwValue = DWORD(pgstNow.GetFullSyncAtLogon()); DWORD dwResult = RegSetValueEx(hkeyCU, REGSTR_VAL_SYNCATLOGON, 0, REG_DWORD, (CONST BYTE *)&dwValue, sizeof(dwValue)); HRESULT hr = HRESULT_FROM_WIN32(dwResult); if (SUCCEEDED(hr)) { if (!m_state.GetFullSyncAtLogon() && pgstNow.GetFullSyncAtLogon()) { //
// If the user has just turned on full sync we want to
// make sure SyncMgr is enabled for sync-at-logon.
// There are some weirdnesses with doing this but it's the most
// consistent behavior we can offer the user given
// the current design of SyncMgr and CSC. Internal use and beta
// testing shows that users expect Sync-at-logon to be enabled
// when this checkbox is checked.
//
RegisterSyncMgrHandler(TRUE); RegisterForSyncAtLogonAndLogoff(SYNCMGRREGISTERFLAG_CONNECT, SYNCMGRREGISTERFLAG_CONNECT); } } else { Trace((TEXT("Error 0x%08X setting reg value \"%s\""), hr, REGSTR_VAL_SYNCATLOGON)); } return hr; }
HRESULT COfflineFilesPage::_ApplyReminderSettings( HKEY hkeyLM, HKEY hkeyCU, const PgState& pgstNow ) { DWORD dwValue = DWORD(!pgstNow.GetRemindersEnabled()); DWORD dwResult = RegSetValueEx(hkeyCU, REGSTR_VAL_NOREMINDERS, 0, REG_DWORD, (CONST BYTE *)&dwValue, sizeof(dwValue)); HRESULT hr = HRESULT_FROM_WIN32(dwResult); if (FAILED(hr)) { Trace((TEXT("Error 0x%08X setting reg value \"%s\""), hr, REGSTR_VAL_NOREMINDERS)); }
dwValue = DWORD(pgstNow.GetReminderFreq()); dwResult = RegSetValueEx(hkeyCU, REGSTR_VAL_REMINDERFREQMINUTES, 0, REG_DWORD, (CONST BYTE *)&dwValue, sizeof(dwValue)); hr = HRESULT_FROM_WIN32(dwResult); if (FAILED(hr)) { Trace((TEXT("Error 0x%08X setting reg value \"%s\""), hr, REGSTR_VAL_REMINDERFREQMINUTES)); }
if (m_state.GetReminderFreq() != pgstNow.GetReminderFreq()) { PostToSystray(PWM_RESET_REMINDERTIMER, 0, 0); } return hr; }
HRESULT COfflineFilesPage::_ApplyFolderLinkSetting( HKEY /* hkeyLM */, HKEY /* hkeyCU */, const PgState& pgstNow ) { if (m_state.GetLinkOnDesktop() != pgstNow.GetLinkOnDesktop()) { TCHAR szLinkPath[MAX_PATH]; bool bLinkFileIsOnDesktop = IsLinkOnDesktop(szLinkPath, ARRAYSIZE(szLinkPath)); if (bLinkFileIsOnDesktop && !pgstNow.GetLinkOnDesktop()) { DeleteOfflineFilesFolderLink(m_hwndDlg); } else if (!bLinkFileIsOnDesktop && pgstNow.GetLinkOnDesktop()) { COfflineFilesFolder::CreateLinkOnDesktop(m_hwndDlg); } } return S_OK; }
HRESULT COfflineFilesPage::_ApplyCacheSizeSetting( HKEY hkeyLM, HKEY hkeyCU, const PgState& pgstNow ) {
double pctCacheSize = Rx(TrackBar_GetPos(m_hwndSlider)); DWORD dwValue = DWORD(pctCacheSize * 10000.00); DWORD dwResult = RegSetValueEx(hkeyLM, REGSTR_VAL_DEFCACHESIZE, 0, REG_DWORD, (CONST BYTE *)&dwValue, sizeof(dwValue));
HRESULT hr = HRESULT_FROM_WIN32(dwResult); if (FAILED(hr)) { Trace((TEXT("Error 0x%08X setting reg value \"%s\""), hr, REGSTR_VAL_DEFCACHESIZE)); }
ULARGE_INTEGER ulCacheSize;
ulCacheSize.QuadPart = DWORDLONG(m_llAvailableDiskSpace * pctCacheSize); if (!CSCSetMaxSpace(ulCacheSize.HighPart, ulCacheSize.LowPart)) { Trace((TEXT("Error %d setting cache size"), GetLastError())); } return hr; }
HRESULT COfflineFilesPage::_ApplyEncryptionSetting( HKEY hkeyLM, HKEY hkeyCU, const PgState& pgstNow, bool bPropSheetClosing, bool *pbPerformed ) { HRESULT hr = S_OK;
*pbPerformed = false; if (m_state.GetEncrypted() != pgstNow.GetEncrypted()) { EncryptOrDecryptCache(BST_CHECKED == pgstNow.GetEncrypted(), bPropSheetClosing); *pbPerformed = true; //
// Record the user's setting in the registry as a "preference". If policy
// is later applied then removed we need to know the user's previous preference.
// Note that it's a per-machine preference. Also note that if the "encrypted"
// state of the checkbox has changed, we are assured that the user has WRITE
// access to HKLM.
//
DWORD dwValue = pgstNow.GetEncrypted(); DWORD dwResult = RegSetValueEx(hkeyLM, REGSTR_VAL_ENCRYPTCACHE, 0, REG_DWORD, (CONST BYTE *)&dwValue, sizeof(dwValue)); if (ERROR_SUCCESS != dwResult) { hr = HRESULT_FROM_WIN32(dwResult); Trace((TEXT("Error 0x%08X setting reg value \"%s\""), hr, REGSTR_VAL_ENCRYPTCACHE)); } } return hr; }
//
// Structure for communicating between the Prop Sheet code
// and the CSC progress callbacks.
//
struct ENCRYPT_PROGRESS_INFO { HINSTANCE hInstance; // Module containing UI text strings.
HWND hwndParentDefault; // Default parent window for error UI.
IProgressDialog *pDlg; // Progress dialog.
int cFilesTotal; // Total files to be processed.
int cFilesProcessed; // Running count of files processed.
bool bUserCancelled; // User cancelled operation?
bool bPropSheetClosing; // User pressed "OK" so the prop sheet is closing.
};
//
// Organize the parameters passed from a CSC callback function
// into a single structure. Note that not all the callback
// parameters are used here. I've commented out the ones that
// aren't. If they're needed later, uncomment them and
// fill in the value in EncryptDecryptCallback().
//
struct CSC_CALLBACK_DATA { LPCWSTR lpszName; DWORD dwReason; DWORD dwParam1; DWORD dwParam2; DWORD_PTR dwContext;
/* ----- Unused ------
DWORD dwStatus; DWORD dwHintFlags; DWORD dwPinCount; WIN32_FIND_DATAW *pFind32; */ };
//
// Helper to get the HWND of the progress dialog
// from the progress info block.
//
HWND ParentWindowFromProgressInfo( const ENCRYPT_PROGRESS_INFO &epi ) { const HWND hwndParent = GetProgressDialogWindow(epi.pDlg); if (NULL != hwndParent) return hwndParent;
return epi.hwndParentDefault; }
//
// The progress dialog lower's the priority class of it's thread to
// THREAD_PRIORITY_BELOW_NORMAL so that it doesn't compete with the
// thread doing the real work. Unfortunately, with this encryption
// stuff this resulted in the dialog being starved of CPU time so that
// it didn't appear in some cases. To ensure proper operation of the
// progress dialog we promote it's priority back to
// THREAD_PRIORITY_NORMAL. This function is the helper to do this.
//
BOOL SetProgressThreadPriority( IProgressDialog *pDlg, int iPriority, int *piPrevPriority ) { BOOL bResult = FALSE; //
// Get the thread handle for the progress dialog.
//
const DWORD dwThreadId = GetWindowThreadProcessId(GetProgressDialogWindow(pDlg), NULL); const HANDLE hThread = OpenThread(THREAD_SET_INFORMATION, FALSE, dwThreadId); if (NULL != hThread) { //
// Set the thread's priority. Return the previous thread
// priority if the caller requests it.
//
const int iPrevPriority = GetThreadPriority(hThread); if (SetThreadPriority(hThread, iPriority)) { if (NULL != piPrevPriority) { *piPrevPriority = iPrevPriority; } bResult = TRUE; } CloseHandle(hThread); } return bResult; }
//
// Handler for Encryption CSCPROC_REASON_BEGIN
//
DWORD EncryptDecrypt_BeginHandler( const CSC_CALLBACK_DATA& cbd, bool bEncrypting ) { //
// Nothing to do on "begin".
//
return CSCPROC_RETURN_CONTINUE; }
//
// Handler for Encryption CSCPROC_REASON_MORE_DATA
//
// Returns:
// CSCPROC_RETURN_CONTINUE == Success. Continue!
// CSCPROC_RETURN_ABORT == User cancelled.
// CSCPROC_RETURN_RETRY == An error occured and user says "retry".
//
DWORD EncryptDecrypt_MoreDataHandler( const CSC_CALLBACK_DATA& cbd, bool bEncrypting ) { const TCHAR szNull[] = TEXT(""); const DWORD dwError = cbd.dwParam2; ENCRYPT_PROGRESS_INFO * const pepi = (ENCRYPT_PROGRESS_INFO *)cbd.dwContext; DWORD dwResult = CSCPROC_RETURN_CONTINUE; LPCTSTR pszName = cbd.lpszName ? cbd.lpszName : szNull;
//
// Update the progress UI with the file name and % processed.
//
pepi->pDlg->SetLine(2, pszName, TRUE, NULL); pepi->pDlg->SetProgress(++(pepi->cFilesProcessed), pepi->cFilesTotal); //
// Handle any errors.
//
if (ERROR_SUCCESS != dwError) { //
// The formatting of this message is as follows (encryption version shown):
//
// -----------------------------------------------------
// Offline Files
// -----------------------------------------------------
//
// Error encrypting offline copy of 'filename'.
//
// < error description >
//
// [Cancel][Try Again][Continue]
//
//
TCHAR szPath[MAX_PATH]; ::PathCompactPathEx(szPath, pszName, 50, 0); // Compact to 50 chars max.
LPTSTR pszError; if (0 < FormatSystemError(&pszError, dwError)) { INT iResponse; if (ERROR_SHARING_VIOLATION == dwError) { //
// "File is open" is so common we special-case it to provide a bit more
// instruction to the user.
//
iResponse = CscMessageBox(ParentWindowFromProgressInfo(*pepi), MB_ICONWARNING | MB_CANCELTRYCONTINUE, pepi->hInstance, bEncrypting ? IDS_ERR_FMT_ENCRYPTFILE_INUSE : IDS_ERR_FMT_DECRYPTFILE_INUSE, szPath); } else { //
// Handle all other errors. This embeds the error text from winerror
// into the message.
//
iResponse = CscMessageBox(ParentWindowFromProgressInfo(*pepi), MB_ICONWARNING | MB_CANCELTRYCONTINUE, pepi->hInstance, bEncrypting ? IDS_ERR_FMT_ENCRYPTFILE : IDS_ERR_FMT_DECRYPTFILE, szPath, pszError); } LocalFree(pszError); switch(iResponse) { case IDCANCEL: dwResult = CSCPROC_RETURN_ABORT; break;
case IDTRYAGAIN: //
// Take one file away from the progress total.
//
pepi->cFilesProcessed--; dwResult = CSCPROC_RETURN_RETRY; break;
case IDCONTINUE: //
// Fall through...
//
default: //
// Continue processing.
//
break; } } } return dwResult; }
//
// Handler for Encryption CSCPROC_REASON_END
//
// Returns:
// CSCPROC_RETURN_CONTINUE
//
DWORD EncryptDecrypt_EndHandler( const CSC_CALLBACK_DATA& cbd, bool bEncrypting ) { const DWORD fCompleted = cbd.dwParam1; const DWORD dwError = cbd.dwParam2; ENCRYPT_PROGRESS_INFO * const pepi = (ENCRYPT_PROGRESS_INFO *)cbd.dwContext;
//
// Advance progress to 100% and stop progress dialog.
//
pepi->pDlg->SetProgress(pepi->cFilesTotal, pepi->cFilesTotal); pepi->pDlg->StopProgressDialog(); //
// Handle any errors.
//
if (!fCompleted) { //
// CSC says it did not complete the encryption/decryption process
// without errors.
//
if (ERROR_SUCCESS != dwError) { //
// This path is taken if some error occured outside of the
// file-processing code (i.e. opening the CSC database,
// recording encryption state flags in the database etc).
//
// -----------------------------------------------------
// Offline Files
// -----------------------------------------------------
//
// Error encrypting offline files.
//
// < error-specific text >
// [OK]
//
// Note that we're at the end of the operation so there's no
// reason to offer "Cancel" as a user choice.
//
LPTSTR pszError; if (0 < FormatSystemError(&pszError, dwError)) { CscMessageBox(ParentWindowFromProgressInfo(*pepi), MB_ICONERROR | MB_OK, pepi->hInstance, bEncrypting ? IDS_ERR_FMT_ENCRYPTCSC : IDS_ERR_FMT_DECRYPTCSC, pszError); LocalFree(pszError); } } else { //
// This path is taken if one or more errors were reported
// in the "more data" callback. In this case the error was
// already reported so we do nothing.
//
} } return CSCPROC_RETURN_CONTINUE; // Note: CSC ignores return value on CSCPROC_REASON_END.
}
//
// Encryption/Decryption callback from CSC.
//
// dwReason dwParam1 dwParam2
// ------------------------- ------------------ --------------------------
// CSCPROC_REASON_BEGIN 1 == Encrypting 0
// CSCPROC_REASON_MORE_DATA 0 Win32 error code
// CSCPROC_REASON_END 1 == Completed dwParam1 == 1 ? 0
// dwParam1 == 0 ? GetLastError()
//
DWORD CALLBACK COfflineFilesPage::EncryptDecryptCallback( LPCWSTR lpszName, DWORD dwStatus, DWORD dwHintFlags, DWORD dwPinCount, WIN32_FIND_DATAW *pFind32, DWORD dwReason, DWORD dwParam1, DWORD dwParam2, DWORD_PTR dwContext ) { static bool bEncrypting; ENCRYPT_PROGRESS_INFO * const pepi = (ENCRYPT_PROGRESS_INFO *)dwContext;
if (pepi->bUserCancelled) { //
// If user has already cancelled on a previous callback
// there's no reason to process this callback. Just return
// "abort" to CSC.
//
return CSCPROC_RETURN_ABORT; }
DWORD dwResult = CSCPROC_RETURN_CONTINUE; //
// Package callback data into a struct we can pass to the
// handler functions. Yeah, it's a bit more expensive but
// handling the various "reasons" in separate functions sure makes
// for cleaner code than if they're all handled in a big switch
// statement.
//
CSC_CALLBACK_DATA cbd; cbd.lpszName = lpszName; cbd.dwReason = dwReason; cbd.dwParam1 = dwParam1; cbd.dwParam2 = dwParam2; cbd.dwContext = dwContext;
//
// Call the appropriate callback reason handler.
//
switch(dwReason) { case CSCPROC_REASON_BEGIN: bEncrypting = boolify(dwParam1); dwResult = EncryptDecrypt_BeginHandler(cbd, bEncrypting); break;
case CSCPROC_REASON_MORE_DATA: dwResult = EncryptDecrypt_MoreDataHandler(cbd, bEncrypting); break; case CSCPROC_REASON_END: dwResult = EncryptDecrypt_EndHandler(cbd, bEncrypting); break;
default: break; } //
// Detect if user has cancelled the operation in response to
// an error message or directly in the progress dialog.
//
if (CSCPROC_RETURN_ABORT == dwResult || (!pepi->bUserCancelled && pepi->pDlg->HasUserCancelled())) { pepi->bUserCancelled = true; dwResult = CSCPROC_RETURN_ABORT; }
if (pepi->bUserCancelled && pepi->bPropSheetClosing) { //
// Only display this cautionary message if the user has
// clicked the "OK" button. If they clicked "Apply" the prop sheet
// remains active and we'll display the encryption warning tooltip
// balloon on the page itself. If they clicked "OK" the prop sheet
// is going away so the tooltip will not be presented to the user.
// Either way we need to let the user know that cancelling
// encryption or decryption leaves the cache in a partial state.
//
// -----------------------------------------------------
// Offline Files
// -----------------------------------------------------
//
// Encryption of Offline Files is enabled but
// not all files have been encrypted.
//
//
const UINT ids = bEncrypting ? IDS_ENCRYPTCSC_CANCELLED : IDS_DECRYPTCSC_CANCELLED; const UINT uType = MB_OK | (bEncrypting ? MB_ICONWARNING : MB_ICONINFORMATION);
CscMessageBox(ParentWindowFromProgressInfo(*pepi), uType, pepi->hInstance, ids); } return dwResult; }
//
// Encrypt or Decrypt the cache.
//
void COfflineFilesPage::EncryptOrDecryptCache( bool bEncrypt, bool bPropSheetClosing ) { HANDLE hMutex = RequestPermissionToEncryptCache(); if (NULL != hMutex) { //
// Excellent. No one (i.e. policy) is trying to encrypt/decrypt
// the cache. We're in business.
//
CMutexAutoRelease mutex_auto_release(hMutex); // Ensure release of mutex.
IProgressDialog *ppd; if (SUCCEEDED(CoCreateInstance(CLSID_ProgressDialog, NULL, CLSCTX_INPROC_SERVER, IID_IProgressDialog, (void **)&ppd))) { //
// Set up the progress dialog using the standard "encrypt file"
// animation. The dialog is modal.
//
TCHAR szText[MAX_PATH]; if (0 < LoadString(m_hInstance, IDS_APPLICATION, szText, ARRAYSIZE(szText))) { ppd->SetTitle(szText); } if (0 < LoadString(m_hInstance, bEncrypt ? IDS_ENCRYPTING_DOTDOTDOT : IDS_DECRYPTING_DOTDOTDOT, szText, ARRAYSIZE(szText))) { ppd->SetLine(1, szText, FALSE, NULL); } //
// TODO: [brianau - 3/8/00] Need special encrypting/decrypting AVI.
//
ppd->SetAnimation(m_hInstance, bEncrypt ? IDA_FILEENCR : IDA_FILEDECR); ppd->StartProgressDialog(m_hwndDlg, NULL, PROGDLG_NOTIME | PROGDLG_MODAL, NULL); //
// Pass this info to the progress callback so we can display UI.
//
ENCRYPT_PROGRESS_INFO epi; epi.hInstance = m_hInstance; epi.hwndParentDefault = m_hwndDlg; epi.pDlg = ppd; epi.bUserCancelled = false; epi.bPropSheetClosing = bPropSheetClosing;
CSCSPACEUSAGEINFO sui; ZeroMemory(&sui, sizeof(sui)); GetCscSpaceUsageInfo(&sui);
epi.cFilesTotal = sui.dwNumFilesInCache; epi.cFilesProcessed = 0; //
// Boost the progress dialog thread's priority to "normal" priority class.
// The progress dialog sets it's priority class to "below normal" so it
// doesn't steal all of the CPU running the animation. However, that also
// means that the progress dialog doesn't work so well when displaying progress
// for a higher-priority compute-bound thread like encryption.
//
SetProgressThreadPriority(ppd, THREAD_PRIORITY_NORMAL, NULL); //
// Encrypt/Decrypt the cache files. Will provide progress info through
// the callback EncryptDecryptCallback. Errors are handled in the callback
// reason handlers.
//
CSCEncryptDecryptDatabase(bEncrypt, EncryptDecryptCallback, (DWORD_PTR)&epi); ppd->StopProgressDialog(); ppd->Release(); } } else { //
// Let the user know an encryption/decryption operation is already
// in progress for system policy.
//
CscMessageBox(m_hwndDlg, MB_ICONINFORMATION | MB_OK, m_hInstance, IDS_ENCRYPTCSC_INPROGFORPOLICY); } //
// Make sure the encryption checkbox reflects the encryption state of
// the cache when we're done. We don't do it if the prop sheet is closing
// because that may flash the warning tooltip just as the sheet is going
// away. Looks bad.
//
if (!bPropSheetClosing) { UpdateEncryptionCheckbox(); UpdateEncryptionTooltipBalloon(); } }
//
// Enables or disables CSC according to the bEnable arg.
//
// Returns:
//
// TRUE == Operation successful (reboot may be required).
// FALSE == Operation failed. See *pdwError for cause.
//
// *pbReboot indicates if a reboot is required.
// *pdwError returns any error code.
//
bool COfflineFilesPage::EnableOrDisableCsc( bool bEnable, bool *pbReboot, DWORD *pdwError ) { DWORD dwError = ERROR_SUCCESS;
//
// We'll assume no reboot required.
//
if (NULL != pbReboot) *pbReboot = false;
if (!CSCDoEnableDisable(bEnable)) { //
// Tried to enable or disable but failed.
// If enabling, it's just a failure and we return.
// If disabling, it may have failed because there are open files.
//
dwError = GetLastError(); if (!bEnable && ERROR_BUSY == dwError) { //
// Failed to disable and there are open files.
// Tell the user to close all open files then try again.
//
CscMessageBox(m_hwndDlg, MB_ICONINFORMATION | MB_OK, m_hInstance, IDS_OPENFILESONDISABLE);
if (!CSCDoEnableDisable(bEnable)) { dwError = GetLastError(); if (ERROR_BUSY == dwError) { //
// Still can't disable CSC because there are open files.
// This means we'll have to reboot.
//
if (NULL != pbReboot) *pbReboot = true; } } } } //
// Return error code to caller.
//
if (NULL != pdwError) *pdwError = dwError;
return ERROR_SUCCESS == dwError || ERROR_BUSY == dwError; }
//
// UI info passed to PurgeCache then returned to PurgeCacheCallback.
//
struct PROGRESS_UI_INFO { HINSTANCE hInstance; // Module containing UI text strings.
HWND hwndParent; // Parent window for error dialog.
IProgressDialog *pDlg; // Progress dialog.
};
//
// This callback updates the progress UI for deleting cached items.
//
//
BOOL COfflineFilesPage::PurgeCacheCallback( CCachePurger *pPurger ) { BOOL bContinue = TRUE; PROGRESS_UI_INFO *ppui = (PROGRESS_UI_INFO *)pPurger->CallbackData(); IProgressDialog *pDlg = ppui->pDlg;
const DWORD dwPhase = pPurger->Phase();
//
// Adjust dialog appearance at start of each phase.
//
if (0 == pPurger->FileOrdinal()) { TCHAR szText[MAX_PATH]; if (0 < LoadString(ppui->hInstance, PURGE_PHASE_SCAN == dwPhase ? IDS_SCANNING_DOTDOTDOT : IDS_DELETING_DOTDOTDOT, szText, ARRAYSIZE(szText))) { pDlg->SetLine(2, szText, FALSE, NULL); } //
// We don't start registering progress until the "delete" phase.
// To keep this code simple we just set the progress bar at 0% at the beginning
// of each phase. This way it will be 0% throughout the scanning phase and then
// during the delete phase we'll increment it. The scanning phase is very fast.
//
pDlg->SetProgress(0, 100); }
if (PURGE_PHASE_SCAN == dwPhase) { //
// Do nothing. We've already set the "Scanning..." text above at the
// start of the phase.
//
} else if (PURGE_PHASE_DELETE == dwPhase) { DWORD dwResult = pPurger->FileDeleteResult(); //
// Divide each value by 1,000 so that our numbers aren't so large. This
// means that if you're deleting less than 1,000 bytes of files, progress won't register.
// I don't think that's a very likely scenario. The DWORD() casts are required because
// IProgressDialog::SetProgress only accepts DWORDs. Dividing by 1,000 makes the
// likelihood of DWORD overflow very low. To overflow the DWORD you'd need to be deleting
// 4.294 e12 bytes from the cache. The current limit on cache size is 4GB so that's
// not going to happen in Win2000.
//
pDlg->SetProgress(DWORD(pPurger->BytesDeleted() / 1000), DWORD(pPurger->BytesToDelete() / 1000)); if (ERROR_SUCCESS != dwResult) { //
// The purger won't call us for directory entries. Only files.
//
INT iUserResponse = IDOK; if (ERROR_BUSY == dwResult) { //
// Special case for ERROR_BUSY. This means that the
// file is currently open for use by some process.
// Most likely the network redirector. I don't like the
// standard text for ERROR_BUSY from winerror.
//
iUserResponse = CscMessageBox(ppui->hwndParent, MB_OKCANCEL | MB_ICONERROR, ppui->hInstance, IDS_FMT_ERR_DELFROMCACHE_BUSY, pPurger->FileName()); } else { iUserResponse = CscMessageBox(ppui->hwndParent, MB_OKCANCEL | MB_ICONERROR, Win32Error(dwResult), ppui->hInstance, IDS_FMT_ERR_DELFROMCACHE, pPurger->FileName()); } if (IDCANCEL == iUserResponse) { bContinue = FALSE; // User cancelled through error dialog.
} } } if (pDlg->HasUserCancelled()) bContinue = FALSE; // User cancelled through progress dialog.
return bContinue; }
//
// This feature has been included for use by PSS when there's no other
// way of fixing CSC operation. Note this is only a last resort.
// It will wipe out all the contents of the CSC cache including the
// notion of which files are pinned. It does not affect any files
// on the network. It does require a system reboot. Again, use only
// as a last resort when CSC cache corruption is suspected.
//
void COfflineFilesPage::OnFormatCache( void ) { if (IDYES == CscMessageBox(m_hwndDlg, MB_YESNO | MB_ICONWARNING, m_hInstance, IDS_REFORMAT_CACHE)) { HKEY hkey; DWORD dwDisposition; DWORD dwResult = RegCreateKeyEx(HKEY_LOCAL_MACHINE, REGSTR_KEY_OFFLINEFILES, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hkey, &dwDisposition); if (ERROR_SUCCESS == dwResult) { DWORD dwValue = 1; dwResult = RegSetValueEx(hkey, REGSTR_VAL_FORMATCSCDB, 0, REG_DWORD, (CONST BYTE *)&dwValue, sizeof(dwValue)); if (ERROR_SUCCESS == dwResult) { //
// Tell prop sheet to return ID_PSREBOOTSYSTEM from PropertySheet().
// Caller of PropertySheet is responsible for prompting user if they
// want to reboot now or not.
//
PropSheet_RebootSystem(GetParent(m_hwndDlg)); } RegCloseKey(hkey); if (ERROR_SUCCESS != dwResult) { Trace((TEXT("Format failed with error %d"), dwResult)); CscWin32Message(m_hwndDlg, dwResult, CSCUI::SEV_ERROR); } } } }
//
// Invoked when user selects "Delete Files..." button in the CSC
// options dialog.
//
void COfflineFilesPage::OnDeleteCache( void ) { //
// Ask the user if they want to delete only temporary files
// from the cache or both temp and pinned files. Also gives
// them an opportunity to cancel before beginning the deletion
// operation.
//
CCachePurgerSel sel; CCachePurger::AskUserWhatToPurge(m_hwndDlg, &sel); if (PURGE_FLAG_NONE != sel.Flags()) { CCoInit coinit; if (SUCCEEDED(coinit.Result())) { //
// Use the shell's standard progress dialog.
//
IProgressDialog *ppd; if (SUCCEEDED(CoCreateInstance(CLSID_ProgressDialog, NULL, CLSCTX_INPROC_SERVER, IID_IProgressDialog, (void **)&ppd))) { //
// Set up the progress dialog using the standard shell "file delete"
// animation (the one without the recycle bin). The dialog
// is modal.
//
TCHAR szText[MAX_PATH]; if (0 < LoadString(m_hInstance, IDS_APPLICATION, szText, ARRAYSIZE(szText))) { ppd->SetTitle(szText); } ppd->SetAnimation(m_hInstance, IDA_FILEDEL); ppd->StartProgressDialog(m_hwndDlg, NULL, PROGDLG_AUTOTIME | PROGDLG_MODAL, NULL); //
// Pass this info to the progress callback so we can display UI.
//
PROGRESS_UI_INFO pui; pui.hInstance = m_hInstance; pui.hwndParent = m_hwndDlg; pui.pDlg = ppd; //
// Purge the cache files. Will provide progress info through
// the callback PurgeCacheCallback.
//
CCachePurger purger(sel, PurgeCacheCallback, &pui); purger.Scan(); purger.Delete();
ppd->StopProgressDialog(); //
// Display message to user.
// "Deleted 10 files (2.5 MB)."
//
FileSize fs(purger.BytesDeleted()); fs.GetString(szText, ARRAYSIZE(szText));
if (0 < purger.FilesDeleted()) { CscMessageBox(m_hwndDlg, MB_OK | MB_ICONINFORMATION, m_hInstance, 1 == purger.FilesDeleted() ? IDS_FMT_DELCACHE_FILEDELETED : IDS_FMT_DELCACHE_FILESDELETED, purger.FilesDeleted(), szText); } else { CscMessageBox(m_hwndDlg, MB_OK | MB_ICONINFORMATION, m_hInstance, IDS_DELCACHE_NOFILESDELETED); } ppd->Release(); } } } }
//
// Determine if there's a shortcut to the offline files folder
// on the desktop.
//
bool COfflineFilesPage::IsLinkOnDesktop( LPTSTR pszPathOut, UINT cchPathOut ) { return S_OK == COfflineFilesFolder::IsLinkOnDesktop(m_hwndDlg, pszPathOut, cchPathOut); }
//
// This function checks to see if CSC is compatible with Windows Terminal
// Server. If it is not, it hides all of the dialog's normal controls
// and replaces them with a block of text explaining the current mode
// of Terminal Server and that the user needs to reconfigure Terminal
// Server in order to use CSC.
//
// Returns:
// true - Dialog controls have been disabled.
// false - Dialog controls not disabled. CSC is compatible with TS mode.
//
bool COfflineFilesPage::DisableForTerminalServer( void ) { bool bDisabled = false; DWORD dwTsMode; HRESULT hr = CSCUIIsTerminalServerCompatibleWithCSC(&dwTsMode); if (S_FALSE == hr) { RECT rcCbxEnable; RECT rcBtnAdvanced; RECT rcDlg; RECT rcText; //
// Base the size and position of the text rectangle off
// of existing controls in the dialog.
//
// ISSUE-2001/1/22-brianau Any bi-di issues here?
//
GetWindowRect(GetDlgItem(m_hwndDlg, IDC_CBX_ENABLE_CSC), &rcCbxEnable); GetWindowRect(GetDlgItem(m_hwndDlg, IDC_BTN_ADVANCED), &rcBtnAdvanced); GetWindowRect(m_hwndDlg, &rcDlg);
rcText.left = rcCbxEnable.left - rcDlg.left; rcText.top = rcCbxEnable.top - rcDlg.top; rcText.right = rcBtnAdvanced.right - rcDlg.left; rcText.bottom = rcBtnAdvanced.bottom - rcDlg.top;
//
// Create a static text control for the text block.
//
HWND hwndText = CreateWindow(TEXT("static"), TEXT(""), WS_CHILD | WS_VISIBLE, rcText.left, rcText.top, rcText.right - rcText.left, rcText.bottom - rcText.top, m_hwndDlg, NULL, NULL, NULL); if (NULL != hwndText) { //
// Load and display the text explaining what the user needs
// to do to enable CSC.
//
LPTSTR pszText; if (SUCCEEDED(TS_GetIncompatibilityReasonText(dwTsMode, &pszText))) { HFONT hFont = (HFONT)SendMessage(m_hwndDlg, WM_GETFONT, 0, 0); SendMessage(hwndText, WM_SETFONT, (WPARAM)hFont, FALSE);
SetWindowText(hwndText, pszText); LocalFree(pszText); } //
// Hide all of the controls on the page.
//
static const UINT rgCtls[] = { IDC_CBX_FULLSYNC_AT_LOGOFF, IDC_CBX_FULLSYNC_AT_LOGON, IDC_CBX_REMINDERS, IDC_CBX_LINK_ON_DESKTOP, IDC_CBX_ENCRYPT_CSC, IDC_TXT_CACHESIZE_PCT, IDC_SLIDER_CACHESIZE_PCT, IDC_LBL_CACHESIZE_PCT, IDC_BTN_VIEW_CACHE, IDC_BTN_ADVANCED, IDC_BTN_DELETE_CACHE, IDC_TXT_REMINDERS1, IDC_EDIT_REMINDERS, IDC_SPIN_REMINDERS, IDC_CBX_ENABLE_CSC };
for (int iCtl = 0; iCtl < ARRAYSIZE(rgCtls); iCtl++) { ShowWindow(GetDlgItem(m_hwndDlg, rgCtls[iCtl]), SW_HIDE); } } bDisabled = true; } return bDisabled; }
//-----------------------------------------------------------------------------
// CAdvOptDlg
//-----------------------------------------------------------------------------
const CAdvOptDlg::CtlActions CAdvOptDlg::m_rgCtlActions[CConfig::eNumOfflineActions] = { { IDC_RBN_GOOFFLINE_SILENT, CConfig::eGoOfflineSilent }, { IDC_RBN_GOOFFLINE_FAIL, CConfig::eGoOfflineFail } };
const DWORD CAdvOptDlg::m_rgHelpIDs[] = { IDOK, IDH_OK, IDCANCEL, IDH_CANCEL, IDC_RBN_GOOFFLINE_SILENT, HIDC_RBN_GOOFFLINE_SILENT, IDC_RBN_GOOFFLINE_FAIL, HIDC_RBN_GOOFFLINE_FAIL, IDC_GRP_CUSTGOOFFLINE, HIDC_LV_CUSTGOOFFLINE, IDC_LV_CUSTGOOFFLINE, HIDC_LV_CUSTGOOFFLINE, IDC_BTN_ADD_CUSTGOOFFLINE, HIDC_BTN_ADD_CUSTGOOFFLINE, IDC_BTN_EDIT_CUSTGOOFFLINE, HIDC_BTN_EDIT_CUSTGOOFFLINE, IDC_BTN_DELETE_CUSTGOOFFLINE, HIDC_BTN_DELETE_CUSTGOOFFLINE, IDC_STATIC2, DWORD(-1), // Icon
IDC_STATIC3, DWORD(-1), // Icon's text
IDC_STATIC4, DWORD(-1), // Grp box #1
0, 0 };
int CAdvOptDlg::Run( void ) { int iResult = (int)DialogBoxParam(m_hInstance, MAKEINTRESOURCE(IDD_CSC_ADVOPTIONS), m_hwndParent, DlgProc, (LPARAM)this);
if (-1 == iResult || 0 == iResult) { Trace((TEXT("Error %d creating CSC advanced options dialog"), GetLastError())); } return iResult; }
INT_PTR CALLBACK CAdvOptDlg::DlgProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam ) { BOOL bResult = FALSE;
//
// Retrieve the "this" pointer from the dialog's userdata.
// It was placed there in OnInitDialog().
//
CAdvOptDlg *pThis = (CAdvOptDlg *)GetWindowLongPtr(hDlg, DWLP_USER);
switch(message) { case WM_INITDIALOG: { pThis = reinterpret_cast<CAdvOptDlg *>(lParam); SetWindowLongPtr(hDlg, DWLP_USER, (INT_PTR)pThis); bResult = pThis->OnInitDialog(hDlg, (HWND)wParam, lParam); break; }
case WM_NOTIFY: TraceAssert(NULL != pThis); bResult = pThis->OnNotify(hDlg, (int)wParam, (LPNMHDR)lParam); break;
case WM_COMMAND: TraceAssert(NULL != pThis); bResult = pThis->OnCommand(hDlg, HIWORD(wParam), LOWORD(wParam), (HWND)lParam); break;
case WM_HELP: TraceAssert(NULL != pThis); bResult = pThis->OnHelp(hDlg, (LPHELPINFO)lParam); break;
case WM_CONTEXTMENU: TraceAssert(NULL != pThis); bResult = pThis->OnContextMenu(wParam, lParam); break;
case WM_DESTROY: TraceAssert(NULL != pThis); bResult = pThis->OnDestroy(hDlg); break;
default: break; }
return bResult; }
BOOL CAdvOptDlg::OnInitDialog( HWND hwnd, HWND hwndFocus, LPARAM lInitParam ) { CConfig& config = CConfig::GetSingleton();
m_hwndDlg = hwnd; m_hwndLV = GetDlgItem(hwnd, IDC_LV_CUSTGOOFFLINE);
CreateListColumns(m_hwndLV); ListView_SetExtendedListViewStyle(m_hwndLV, LVS_EX_FULLROWSELECT);
//
// Set the default go-offline action radio buttons.
//
CConfig::OfflineAction action = (CConfig::OfflineAction)config.GoOfflineAction(&m_bNoConfigGoOfflineAction); for (int i = 0; i < ARRAYSIZE(m_rgCtlActions); i++) { CheckDlgButton(hwnd, m_rgCtlActions[i].idRbn, m_rgCtlActions[i].action == action ? BST_CHECKED : BST_UNCHECKED); } //
// Fill the custom go-offline actions listview.
//
HDPA hdpaCustomGOA = DPA_Create(4); if (NULL != hdpaCustomGOA) { config.GetCustomGoOfflineActions(hdpaCustomGOA);
const int cGOA = DPA_GetPtrCount(hdpaCustomGOA); for (i = 0; i < cGOA; i++) { CConfig::CustomGOA *pGOA = (CConfig::CustomGOA *)DPA_GetPtr(hdpaCustomGOA, i); if (NULL != pGOA) { AddGOAToListView(m_hwndLV, i, *pGOA); } } CConfig::ClearCustomGoOfflineActions(hdpaCustomGOA); DPA_Destroy(hdpaCustomGOA); }
//
// Adjust "enabledness" of controls for system policy.
//
EnableCtls(m_hwndDlg); //
// Remember the initial page state so we can intelligently apply changes.
//
GetPageState(&m_state);
return TRUE; }
BOOL CAdvOptDlg::OnHelp( HWND hDlg, LPHELPINFO pHelpInfo ) { if (HELPINFO_WINDOW == pHelpInfo->iContextType) { int idCtl = GetDlgCtrlID((HWND)pHelpInfo->hItemHandle); WinHelp((HWND)pHelpInfo->hItemHandle, UseWindowsHelp(idCtl) ? NULL : c_szHelpFile, HELP_WM_HELP, (DWORD_PTR)((LPTSTR)m_rgHelpIDs)); }
return TRUE; }
void CAdvOptDlg::CreateListColumns( HWND hwndList ) { //
// Create the header titles.
//
TCHAR szServer[40] = {0}; TCHAR szAction[40] = {0};
LoadString(m_hInstance, IDS_TITLE_COL_SERVER, szServer, ARRAYSIZE(szServer)); LoadString(m_hInstance, IDS_TITLE_COL_ACTION, szAction, ARRAYSIZE(szAction));
RECT rcList; GetClientRect(hwndList, &rcList); int cxList = rcList.right - rcList.left - GetSystemMetrics(SM_CXVSCROLL);
#define LVCOLMASK (LVCF_FMT | LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM)
LV_COLUMN rgCols[] = { { LVCOLMASK, LVCFMT_LEFT, (2 * cxList)/3, szServer, 0, iLVSUBITEM_SERVER }, { LVCOLMASK, LVCFMT_LEFT, (1 * cxList)/3, szAction, 0, iLVSUBITEM_ACTION } }; //
// Add the columns to the listview.
//
for (INT i = 0; i < ARRAYSIZE(rgCols); i++) { if (-1 == ListView_InsertColumn(hwndList, i, &rgCols[i])) { Trace((TEXT("CAdvOptDlg::CreateListColumns failed to add column %d"), i)); } } }
int CAdvOptDlg::GetFirstSelectedLVItemRect( RECT *prc ) { int iSel = ListView_GetNextItem(m_hwndLV, -1, LVNI_SELECTED); if (-1 != iSel) { if (ListView_GetItemRect(m_hwndLV, iSel, prc, LVIR_SELECTBOUNDS)) { ClientToScreen(m_hwndLV, (LPPOINT)&prc->left); ClientToScreen(m_hwndLV, (LPPOINT)&prc->right); return iSel; } } return -1; }
BOOL CAdvOptDlg::OnContextMenu( WPARAM wParam, LPARAM lParam ) { HWND hwndItem = (HWND)wParam; INT xPos = -1; INT yPos = -1; INT iHit = -1; BOOL bResult = FALSE;
if (-1 == lParam) { //
// Not invoked with mouse click. Probably Shift F10.
// Pretend mouse was clicked in center of first selected item.
//
RECT rc; iHit = GetFirstSelectedLVItemRect(&rc); if (-1 != iHit) { xPos = rc.left + ((rc.right - rc.left) / 2); yPos = rc.top + ((rc.bottom - rc.top) / 2); } } else { //
// Invoked with mouse click. Now find out if a LV item was
// selected directly.
//
xPos = LOWORD(lParam); yPos = HIWORD(lParam);
LVHITTESTINFO hti; hti.pt.x = xPos; hti.pt.y = yPos; hti.flags = LVHT_ONITEM; ScreenToClient(m_hwndLV, &hti.pt); iHit = (INT)SendMessage(m_hwndLV, LVM_HITTEST, 0, (LPARAM)&hti); } if (-1 == iHit) { //
// LV item not selected directly or Shift-F10 was not pressed.
// Display "what's this" help for the listview control.
//
WinHelp(hwndItem, UseWindowsHelp(GetDlgCtrlID(hwndItem)) ? NULL : c_szHelpFile, HELP_CONTEXTMENU, (DWORD_PTR)((LPTSTR)m_rgHelpIDs)); } else { //
// LV item selected directly or Shift F10 pressed. Display context menu for item.
//
if (0 < ListView_GetSelectedCount(m_hwndLV) && IsCustomActionListviewEnabled()) { HMENU hMenu = LoadMenu(m_hInstance, MAKEINTRESOURCE(IDR_ADVOPTIONS_CONTEXTMENU)); if (NULL != hMenu) { HMENU hmenuTrackPopup = GetSubMenu(hMenu, 0); int cSetByPolicy = 0; CountSelectedListviewItems(&cSetByPolicy); if (0 < cSetByPolicy) { //
// Disable menu items if any item in selection was set by policy.
//
int cItems = GetMenuItemCount(hmenuTrackPopup); for (int i = 0; i < cItems; i++) { EnableMenuItem(hmenuTrackPopup, i, MF_GRAYED | MF_BYPOSITION); } } else { //
// Build a mask indicating which actions are present in the selected
// listview items.
//
int iItem = -1; const DWORD fSilent = 0x00000001; const DWORD fFail = 0x00000002; DWORD fActions = 0; CConfig::CustomGOA *pGOA = NULL; while(-1 != (iItem = ListView_GetNextItem(m_hwndLV, iItem, LVNI_SELECTED))) { pGOA = GetListviewObject(m_hwndLV, iItem); if (NULL != pGOA) { TraceAssert(!pGOA->SetByPolicy()); switch(pGOA->GetAction()) { case CConfig::eGoOfflineSilent: fActions |= fSilent; break; case CConfig::eGoOfflineFail: fActions |= fFail; break; default: break; } } } //
// Calculate how many bits are set in the action mask.
// If there's only one action set, we check that item in the menu.
// Otherwise, we leave them all unchecked to indicate a heterogeneous
// set.
//
int c = 0; // Count of bits set.
DWORD dw = fActions; for (c = 0; 0 != dw; c++) dw &= dw - 1;
//
// If the selection is homogeneous with respect to the action,
// check the menu item corresponding to the action. Otherwise
// leave all items unchecked.
//
if (1 == c) { const struct { DWORD fMask; UINT idCmd; } rgCmds[] = { { fSilent, IDM_ACTION_WORKOFFLINE }, { fFail, IDM_ACTION_FAIL } };
for (int i = 0; i < ARRAYSIZE(rgCmds); i++) { if ((fActions & rgCmds[i].fMask) == rgCmds[i].fMask) { CheckMenuRadioItem(hmenuTrackPopup, IDM_ACTION_WORKOFFLINE, IDM_ACTION_FAIL, rgCmds[i].idCmd, MF_BYCOMMAND); break; } } } }
TrackPopupMenu(hmenuTrackPopup, TPM_LEFTALIGN | TPM_RIGHTBUTTON, xPos, yPos, 0, GetParent(hwndItem), NULL); } DestroyMenu(hMenu); } bResult = TRUE; } return bResult; }
//
// Return the offline action code associated with the currently-checked
// offline-action radio button.
//
CConfig::OfflineAction CAdvOptDlg::GetCurrentGoOfflineAction( void ) const { CConfig::OfflineAction action = CConfig::eNumOfflineActions; for (int i = 0; i < ARRAYSIZE(m_rgCtlActions); i++) { if (BST_CHECKED == IsDlgButtonChecked(m_hwndDlg, m_rgCtlActions[i].idRbn)) { action = m_rgCtlActions[i].action; break; } } TraceAssert(CConfig::eNumOfflineActions != action); return action; }
BOOL CAdvOptDlg::OnCommand( HWND hwnd, WORD wNotifyCode, WORD wID, HWND hwndCtl ) { BOOL bResult = TRUE; if (BN_CLICKED == wNotifyCode) { switch(wID) { case IDOK: ApplySettings(); //
// Fall through...
//
case IDCANCEL: EndDialog(hwnd, wID); break;
case IDC_BTN_ADD_CUSTGOOFFLINE: OnAddCustomGoOfflineAction(); bResult = FALSE; break;
case IDC_BTN_EDIT_CUSTGOOFFLINE: OnEditCustomGoOfflineAction(); bResult = FALSE; break;
case IDC_BTN_DELETE_CUSTGOOFFLINE: OnDeleteCustomGoOfflineAction(); FocusOnSomethingInListview(); if (0 < ListView_GetItemCount(m_hwndLV)) SetFocus(GetDlgItem(hwnd, IDC_BTN_DELETE_CUSTGOOFFLINE)); bResult = FALSE; break;
case IDM_ACTION_WORKOFFLINE: case IDM_ACTION_FAIL: case IDM_ACTION_DELETE: OnContextMenuItemSelected(wID); break;
default: break; } } return bResult; }
void CAdvOptDlg::ApplySettings( void ) { //
// Now store changes from the "Advanced" dialog.
//
PgState s; GetPageState(&s); if (m_state != s) { HKEY hkeyCU; DWORD dwDisposition; DWORD dwResult = RegCreateKeyEx(HKEY_CURRENT_USER, REGSTR_KEY_OFFLINEFILES, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hkeyCU, &dwDisposition); if (ERROR_SUCCESS == dwResult) { DWORD dwValue = DWORD(s.GetDefGoOfflineAction()); dwResult = RegSetValueEx(hkeyCU, REGSTR_VAL_GOOFFLINEACTION, 0, REG_DWORD, (CONST BYTE *)&dwValue, sizeof(dwValue)); if (ERROR_SUCCESS != dwResult) { Trace((TEXT("Error %d setting reg value \"%s\""), dwResult, REGSTR_VAL_GOOFFLINEACTION)); }
//
// Delete all existing contents from key before saving
// current list of actions.
//
SHDeleteKey(hkeyCU, REGSTR_SUBKEY_CUSTOMGOOFFLINEACTIONS); HKEY hkeyCustom; dwResult = RegCreateKeyEx(hkeyCU, REGSTR_SUBKEY_CUSTOMGOOFFLINEACTIONS, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hkeyCustom, &dwDisposition); if (ERROR_SUCCESS == dwResult) { HRESULT hr = CConfig::SaveCustomGoOfflineActions(hkeyCustom, s.GetCustomGoOfflineActions()); if (FAILED(hr)) { Trace((TEXT("Error 0x%08X setting custom offline actions"), hr)); } RegCloseKey(hkeyCustom); } RegCloseKey(hkeyCU); } else { Trace((TEXT("Error %d opening advanced settings user key"), dwResult)); } } }
void CAdvOptDlg::DeleteSelectedListviewItems( void ) { int iItem = -1; CConfig::CustomGOA *pGOA = NULL; CAutoSetRedraw autoredraw(m_hwndLV, false); while(-1 != (iItem = ListView_GetNextItem(m_hwndLV, -1, LVNI_SELECTED))) { pGOA = GetListviewObject(m_hwndLV, iItem); if (pGOA) { TraceAssert(!pGOA->SetByPolicy()); ListView_DeleteItem(m_hwndLV, iItem); delete pGOA; } } //
// If the list is empty, this will disable the "Delete" and
// "Edit" buttons.
//
EnableCtls(m_hwndDlg); }
void CAdvOptDlg::SetSelectedListviewItemsAction( CConfig::OfflineAction action ) { int iItem = -1; CConfig::CustomGOA *pGOA = NULL; CAutoSetRedraw autoredraw(m_hwndLV, false); while(-1 != (iItem = ListView_GetNextItem(m_hwndLV, iItem, LVNI_SELECTED))) { pGOA = GetListviewObject(m_hwndLV, iItem); if (pGOA) { TraceAssert(!pGOA->SetByPolicy()); pGOA->SetAction(action); ListView_RedrawItems(m_hwndLV, iItem, iItem); } } }
int CAdvOptDlg::CountSelectedListviewItems( int *pcSetByPolicy ) { TraceAssert(NULL != pcSetByPolicy); int iItem = -1; int cSelected = 0; CConfig::CustomGOA *pGOA = NULL; while(-1 != (iItem = ListView_GetNextItem(m_hwndLV, iItem, LVNI_SELECTED))) { cSelected++; pGOA = GetListviewObject(m_hwndLV, iItem); if (pGOA && pGOA->SetByPolicy()) (*pcSetByPolicy)++; } return cSelected; }
void CAdvOptDlg::OnContextMenuItemSelected( int idMenuItem ) { if (IDM_ACTION_DELETE == idMenuItem) { DeleteSelectedListviewItems(); } else { CConfig::OfflineAction action = CConfig::eNumOfflineActions; switch(idMenuItem) { case IDM_ACTION_WORKOFFLINE: action = CConfig::eGoOfflineSilent; break; case IDM_ACTION_FAIL: action = CConfig::eGoOfflineFail; break; default: break; } TraceAssert(CConfig::eNumOfflineActions != action);
SetSelectedListviewItemsAction(action); } }
//
// Responds to the user pressing the "Add..." button.
//
void CAdvOptDlg::OnAddCustomGoOfflineAction( void ) { CConfig::OfflineAction action = GetCurrentGoOfflineAction(); TCHAR szServer[MAX_PATH] = {0}; bool bDone = false; while(!bDone) { //
// Run the "Add custom go-offline action" dialog.
// User enters a server name and selects an action
// from a set of radio buttons.
//
CustomGOAAddDlg dlg(m_hInstance, m_hwndDlg, szServer, ARRAYSIZE(szServer), &action); int iResult = dlg.Run();
if (IDCANCEL == iResult || TEXT('\0') == szServer[0]) { //
// User cancelled or didn't enter anything.
//
bDone = true; } else { //
// User entered a server name. Check if it's already in
// our list.
//
int iItem = -1; CConfig::CustomGOA *pObj = FindGOAInListView(m_hwndLV, szServer, &iItem); if (NULL != pObj) { //
// Already an entry in list for this server.
// If not set by policy, can replace it if desired.
// If set by policy, can't change or delete it.
//
bool bSetByPolicy = pObj->SetByPolicy(); DWORD idMsg = bSetByPolicy ? IDS_ERR_GOOFFLINE_DUPACTION_NOCHG : IDS_ERR_GOOFFLINE_DUPACTION; DWORD dwFlags = bSetByPolicy ? MB_OK : MB_YESNO; if (IDYES == CscMessageBox(m_hwndDlg, dwFlags | MB_ICONWARNING, m_hInstance, idMsg, szServer)) { ReplaceCustomGoOfflineAction(pObj, iItem, action); bDone = true; } } else { //
// Server doesn't already exist in list.
// Check if it's available on the net.
//
CAutoWaitCursor waitcursor; DWORD dwNetErr = CheckNetServer(szServer); switch(dwNetErr) { case ERROR_SUCCESS: //
// Server is available. Add the entry to the listview.
//
AddCustomGoOfflineAction(szServer, action); bDone = true; break;
default: { LPTSTR pszNetMsg = NULL; if (ERROR_EXTENDED_ERROR == dwNetErr) { const DWORD cchNetMsg = MAX_PATH; pszNetMsg = (LPTSTR)LocalAlloc(LPTR, cchNetMsg * sizeof(*pszNetMsg)); if (NULL != pszNetMsg) { TCHAR szNetProvider[MAX_PATH]; WNetGetLastError(&dwNetErr, pszNetMsg, cchNetMsg, szNetProvider, ARRAYSIZE(szNetProvider)); } } else { FormatSystemError(&pszNetMsg, dwNetErr); } if (NULL != pszNetMsg) { //
// "The server 'servername' is either invalid
// or cannot be verified at this time. Add anyway?"
// [Yes] [No] [Cancel].
//
switch(CscMessageBox(m_hwndDlg, MB_YESNOCANCEL | MB_ICONWARNING, m_hInstance, IDS_ERR_INVALIDSERVER, szServer, pszNetMsg)) { case IDYES: AddCustomGoOfflineAction(szServer, action); //
// Fall through...
//
case IDCANCEL: bDone = true; //
// Fall through...
//
case IDNO: break; } LocalFree(pszNetMsg); } break; } } } } } }
//
// Verify a server by going out to the net.
// Assumes pszServer points to a properly-formatted
// server name. (i.e. "Server" or "\\Server")
//
DWORD CAdvOptDlg::CheckNetServer( LPCTSTR pszServer ) { TraceAssert(NULL != pszServer);
TCHAR rgchResult[MAX_PATH]; DWORD cbResult = sizeof(rgchResult); LPTSTR pszSystem = NULL;
//
// Ensure the server name has a preceding "\\" for the
// call to WNetGetResourceInformation.
//
TCHAR szServer[MAX_PATH]; while(*pszServer && TEXT('\\') == *pszServer) pszServer++;
szServer[0] = TEXT('\\'); szServer[1] = TEXT('\\'); StringCchCopy(szServer+2, ARRAYSIZE(szServer)-2, pszServer);
NETRESOURCE nr; nr.dwScope = RESOURCE_GLOBALNET; nr.dwType = RESOURCETYPE_ANY; nr.dwDisplayType = 0; nr.dwUsage = 0; nr.lpLocalName = NULL; nr.lpRemoteName = szServer; nr.lpComment = NULL; nr.lpProvider = NULL;
return WNetGetResourceInformation(&nr, rgchResult, &cbResult, &pszSystem); }
//
// Adds a CustomGOA object to the listview.
//
void CAdvOptDlg::AddCustomGoOfflineAction( LPCTSTR pszServer, CConfig::OfflineAction action ) { AddGOAToListView(m_hwndLV, 0, CConfig::CustomGOA(pszServer, action, false)); }
//
// Replaces the action for an object in the listview.
//
void CAdvOptDlg::ReplaceCustomGoOfflineAction( CConfig::CustomGOA *pGOA, int iItem, CConfig::OfflineAction action ) { pGOA->SetAction(action); ListView_RedrawItems(m_hwndLV, iItem, iItem); }
//
// Create and add a CustomGOA object to the listview.
//
int CAdvOptDlg::AddGOAToListView( HWND hwndLV, int iItem, const CConfig::CustomGOA& goa ) { int iItemResult = -1; CConfig::CustomGOA *pGOA = new CConfig::CustomGOA(goa); if (NULL != pGOA) { LVITEM item; item.iSubItem = 0; item.mask = LVIF_PARAM | LVIF_TEXT; item.pszText = LPSTR_TEXTCALLBACK; item.iItem = -1 == iItem ? ListView_GetItemCount(hwndLV) : iItem; item.lParam = (LPARAM)pGOA; iItemResult = ListView_InsertItem(hwndLV, &item); if (-1 == iItemResult) { delete pGOA; } } return iItemResult; }
//
// Find the matching CustomGOA object in the listview for a given
// server.
//
CConfig::CustomGOA * CAdvOptDlg::FindGOAInListView( HWND hwndLV, LPCTSTR pszServer, int *piItem ) { int cItems = ListView_GetItemCount(hwndLV); for (int iItem = 0; iItem < cItems; iItem++) { CConfig::CustomGOA *pObj = GetListviewObject(hwndLV, iItem); if (pObj) { if (0 == lstrcmpi(pObj->GetServerName(), pszServer)) { if (piItem) *piItem = iItem; return pObj; } } } return NULL; }
void CAdvOptDlg::OnEditCustomGoOfflineAction( void ) { int cSetByPolicy = 0; int cSelected = CountSelectedListviewItems(&cSetByPolicy);
if (0 < cSelected && 0 == cSetByPolicy) { TraceAssert(0 == cSetByPolicy); CConfig::OfflineAction action = GetCurrentGoOfflineAction(); TCHAR szServer[MAX_PATH] = {0}; CConfig::CustomGOA *pGOA = NULL; int iItem = -1; //
// At least one selected item wasn't set by policy.
//
if (1 == cSelected) { //
// Only one item selected so we can display a server name
// in the dialog and indicate it's currently-set action.
//
iItem = ListView_GetNextItem(m_hwndLV, -1, LVNI_SELECTED); pGOA = GetListviewObject(m_hwndLV, iItem); if (pGOA) { action = pGOA->GetAction(); pGOA->GetServerName(szServer, ARRAYSIZE(szServer)); } }
//
// Display the "edit" dialog and let the user select a new action.
//
CustomGOAEditDlg dlg(m_hInstance, m_hwndDlg, szServer, &action); if (IDOK == dlg.Run()) { SetSelectedListviewItemsAction(action); } } }
void CAdvOptDlg::OnDeleteCustomGoOfflineAction( void ) { int cSetByPolicy = 0; int cSelected = CountSelectedListviewItems(&cSetByPolicy);
if (0 < cSelected) { DeleteSelectedListviewItems(); } }
BOOL CAdvOptDlg::OnNotify( HWND hDlg, int idCtl, LPNMHDR pnmhdr ) { BOOL bResult = TRUE;
switch(pnmhdr->code) { case NM_SETFOCUS: if (IDC_LV_CUSTGOOFFLINE == idCtl) FocusOnSomethingInListview(); break;
case LVN_GETDISPINFO: OnLVN_GetDispInfo((LV_DISPINFO *)pnmhdr); break;
case LVN_COLUMNCLICK: OnLVN_ColumnClick((NM_LISTVIEW *)pnmhdr); break;
case LVN_ITEMCHANGED: OnLVN_ItemChanged((NM_LISTVIEW *)pnmhdr); break;
case LVN_ITEMACTIVATE: OnEditCustomGoOfflineAction(); break;
case LVN_KEYDOWN: OnLVN_KeyDown((NMLVKEYDOWN *)pnmhdr); break; }
return bResult; }
void CAdvOptDlg::FocusOnSomethingInListview( void ) { //
// Focus on something.
//
int iFocus = ListView_GetNextItem(m_hwndLV, -1, LVNI_FOCUSED); if (-1 == iFocus) iFocus = 0;
ListView_SetItemState(m_hwndLV, iFocus, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED);
}
int CALLBACK CAdvOptDlg::CompareLVItems( LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort ) { CAdvOptDlg *pdlg = reinterpret_cast<CAdvOptDlg *>(lParamSort); int diff = 0;
CConfig::CustomGOA *pGOA1 = (CConfig::CustomGOA *)lParam1; CConfig::CustomGOA *pGOA2 = (CConfig::CustomGOA *)lParam2; TCHAR szText[2][MAX_PATH];
//
// This array controls the comparison column IDs used when
// values for the selected column are equal. These should
// remain in order of the iLVSUBITEM_xxxxx enumeration with
// respect to the first element in each row.
//
static const int rgColComp[2][2] = { { iLVSUBITEM_SERVER, iLVSUBITEM_ACTION }, { iLVSUBITEM_ACTION, iLVSUBITEM_SERVER } }; int iCompare = 0; while(0 == diff && iCompare < ARRAYSIZE(rgColComp)) { switch(rgColComp[pdlg->m_iLastColSorted][iCompare++]) { case iLVSUBITEM_SERVER: diff = lstrcmpi(pGOA1->GetServerName(), pGOA2->GetServerName()); break;
case iLVSUBITEM_ACTION: if (0 < LoadString(pdlg->m_hInstance, IDS_GOOFFLINE_ACTION_FIRST + (DWORD)pGOA1->GetAction(), szText[0], ARRAYSIZE(szText[0]))) { if (0 < LoadString(pdlg->m_hInstance, IDS_GOOFFLINE_ACTION_FIRST + (DWORD)pGOA2->GetAction(), szText[1], ARRAYSIZE(szText[1]))) { diff = lstrcmpi(szText[0], szText[1]); } } break;
default: //
// If you hit this, you need to update this function
// to handle the new column you've added to the listview.
//
TraceAssert(false); break; } } return pdlg->m_bSortAscending ? diff : -1 * diff; }
void CAdvOptDlg::OnLVN_ItemChanged( NM_LISTVIEW *pnmlv ) { static const int rgBtns[] = { IDC_BTN_EDIT_CUSTGOOFFLINE, IDC_BTN_DELETE_CUSTGOOFFLINE };
//
// LVN_ITEMCHANGED is sent multiple times when you move the
// highlight bar in a listview.
// Only run this code when the "focused" state bit is set
// for the "new state". This should be the last call in
// the series.
//
if (LVIS_FOCUSED & pnmlv->uNewState && IsCustomActionListviewEnabled()) { bool bEnable = 0 < ListView_GetSelectedCount(m_hwndLV); if (bEnable) { //
// Only enable if all items weren't set by policy.
//
int cSetByPolicy = 0; CountSelectedListviewItems(&cSetByPolicy); bEnable = (0 == cSetByPolicy); }
for (int i = 0; i < ARRAYSIZE(rgBtns); i++) { HWND hwnd = GetDlgItem(m_hwndDlg, rgBtns[i]); if (bEnable != boolify(IsWindowEnabled(hwnd))) { EnableWindow(hwnd, bEnable); } } } }
void CAdvOptDlg::OnLVN_ColumnClick( NM_LISTVIEW *pnmlv ) { if (m_iLastColSorted != pnmlv->iSubItem) { m_bSortAscending = true; m_iLastColSorted = pnmlv->iSubItem; } else { m_bSortAscending = !m_bSortAscending; }
ListView_SortItems(m_hwndLV, CompareLVItems, LPARAM(this)); }
void CAdvOptDlg::OnLVN_KeyDown( NMLVKEYDOWN *plvkd ) { if (VK_DELETE == plvkd->wVKey && IsCustomActionListviewEnabled()) { int cSetByPolicy = 0; CountSelectedListviewItems(&cSetByPolicy); if (0 == cSetByPolicy) { OnDeleteCustomGoOfflineAction(); FocusOnSomethingInListview(); } else { //
// Provide feedback that deleting things set by policy
// isn't allowed. Visual feedback is that the "Remove"
// button and context menu item are disabled. That
// doesn't help when user hits the [Delete] key.
//
MessageBeep(MB_OK); } } }
void CAdvOptDlg::OnLVN_GetDispInfo( LV_DISPINFO *plvdi ) { static TCHAR szText[MAX_PATH];
CConfig::CustomGOA* pObj = (CConfig::CustomGOA *)plvdi->item.lParam;
szText[0] = TEXT('\0'); if (LVIF_TEXT & plvdi->item.mask) { switch(plvdi->item.iSubItem) { case iLVSUBITEM_SERVER: if (pObj->SetByPolicy()) { //
// Format as "server ( policy )"
//
TCHAR szFmt[80]; if (0 < LoadString(m_hInstance, IDS_FMT_GOOFFLINE_SERVER_POLICY, szFmt, ARRAYSIZE(szFmt))) { wnsprintf(szText, ARRAYSIZE(szText), szFmt, pObj->GetServerName()); } } else { //
// Just plain 'ol "server".
//
pObj->GetServerName(szText, ARRAYSIZE(szText)); } break; case iLVSUBITEM_ACTION: LoadString(m_hInstance, IDS_GOOFFLINE_ACTION_FIRST + (DWORD)pObj->GetAction(), szText, ARRAYSIZE(szText)); break;
default: break; } plvdi->item.pszText = szText; }
if (LVIF_IMAGE & plvdi->item.mask) { //
// Not displaying any images. This is just a placeholder.
// Should be optimized out by compiler.
//
} }
CConfig::CustomGOA * CAdvOptDlg::GetListviewObject( HWND hwndLV, int iItem ) { LVITEM item; item.iItem = iItem; item.iSubItem = 0; item.mask = LVIF_PARAM; if (-1 != ListView_GetItem(hwndLV, &item)) { return (CConfig::CustomGOA *)item.lParam; } return NULL; }
BOOL CAdvOptDlg::OnDestroy( HWND hwnd ) { if (NULL != m_hwndLV) { int cItems = ListView_GetItemCount(m_hwndLV); for (int i = 0; i < cItems; i++) { CConfig::CustomGOA *pObj = GetListviewObject(m_hwndLV, i); delete pObj; } ListView_DeleteAllItems(m_hwndLV); } return FALSE; }
void CAdvOptDlg::EnableCtls( HWND hwnd ) { static const struct { UINT idCtl; bool bRestricted;
} rgCtls[] = { { IDC_RBN_GOOFFLINE_SILENT, m_bNoConfigGoOfflineAction }, { IDC_RBN_GOOFFLINE_FAIL, m_bNoConfigGoOfflineAction }, { IDC_GRP_GOOFFLINE_DEFAULTS, m_bNoConfigGoOfflineAction }, { IDC_GRP_CUSTGOOFFLINE, false }, { IDC_BTN_ADD_CUSTGOOFFLINE, false }, { IDC_BTN_EDIT_CUSTGOOFFLINE, false }, { IDC_BTN_DELETE_CUSTGOOFFLINE, false } }; //
// bCscEnabled is always true here. The logic in the options prop page won't
// let us display the "Advanced" dialog if it isn't.
//
bool bCscEnabled = true; for (int i = 0; i < ARRAYSIZE(rgCtls); i++) { bool bEnable = bCscEnabled; HWND hwndCtl = GetDlgItem(hwnd, rgCtls[i].idCtl); if (bEnable) { //
// May be some further policy restrictions.
//
if (rgCtls[i].bRestricted) bEnable = false;
if (bEnable) { if (IDC_BTN_EDIT_CUSTGOOFFLINE == rgCtls[i].idCtl || IDC_BTN_DELETE_CUSTGOOFFLINE == rgCtls[i].idCtl) { //
// Only enable "Edit" and "Delete" buttons if there's an active
// selection in the listview.
//
bEnable = (0 < ListView_GetSelectedCount(m_hwndLV)); } } }
if (!bEnable) { if (GetFocus() == hwndCtl) { //
// If disabling a control that has focus, advance the
// focus to the next control in the tab order before
// disabling the current control. Otherwise, it will
// be stuck with focus and tabbing is busted.
//
SetFocus(GetNextDlgTabItem(hwnd, hwndCtl, FALSE)); } } EnableWindow(GetDlgItem(hwnd, rgCtls[i].idCtl), bEnable); } }
void CAdvOptDlg::GetPageState( PgState *pps ) { pps->SetDefGoOfflineAction(GetCurrentGoOfflineAction()); pps->SetCustomGoOfflineActions(m_hwndLV); }
CAdvOptDlg::PgState::~PgState( void ) { if (NULL != m_hdpaCustomGoOfflineActions) { CConfig::ClearCustomGoOfflineActions(m_hdpaCustomGoOfflineActions); DPA_Destroy(m_hdpaCustomGoOfflineActions); } }
//
// Retrieve the records from the "custom go-offline actions" listview and place them
// into a member array, sorted by server name.
//
void CAdvOptDlg::PgState::SetCustomGoOfflineActions( HWND hwndLV ) { int iItem = -1; LVITEM item; item.iSubItem = 0; item.mask = LVIF_PARAM;
if (NULL != m_hdpaCustomGoOfflineActions) { CConfig::ClearCustomGoOfflineActions(m_hdpaCustomGoOfflineActions);
int cLvItems = ListView_GetItemCount(hwndLV); for (int iLvItem = 0; iLvItem < cLvItems; iLvItem++) { CConfig::CustomGOA *pGOA = CAdvOptDlg::GetListviewObject(hwndLV, iLvItem); if (NULL != pGOA) { if (!pGOA->SetByPolicy()) { int cArrayItems = DPA_GetPtrCount(m_hdpaCustomGoOfflineActions); int iArrayItem; for (iArrayItem = 0; iArrayItem < cArrayItems; iArrayItem++) { CConfig::CustomGOA *pCustomGOA = (CConfig::CustomGOA *)DPA_GetPtr(m_hdpaCustomGoOfflineActions, iArrayItem); if (NULL != pCustomGOA) { if (0 > lstrcmpi(pGOA->GetServerName(), pCustomGOA->GetServerName())) break; } } CConfig::CustomGOA *pCopy = new CConfig::CustomGOA(*pGOA); if (NULL != pCopy) { if (iArrayItem < cArrayItems) { if (-1 != DPA_InsertPtr(m_hdpaCustomGoOfflineActions, iArrayItem, pCopy)) { pCopy = NULL; } } else { if (-1 != DPA_AppendPtr(m_hdpaCustomGoOfflineActions, pCopy)) { pCopy = NULL; } } if (NULL != pCopy) { delete pCopy; } } } } } } }
//
// Two page states are equal if their Default go-offline actions are equal and their
// customized go-offline actions are equal. Action is tested first because it's a
// faster test.
//
bool CAdvOptDlg::PgState::operator == ( const CAdvOptDlg::PgState& rhs ) const { bool bMatch = false; if (m_DefaultGoOfflineAction == rhs.m_DefaultGoOfflineAction) { if (NULL != m_hdpaCustomGoOfflineActions && NULL != rhs.m_hdpaCustomGoOfflineActions) { const int cLhs = DPA_GetPtrCount(m_hdpaCustomGoOfflineActions); const int cRhs = DPA_GetPtrCount(rhs.m_hdpaCustomGoOfflineActions);
if (cLhs == cRhs) { for (int i = 0; i < cLhs; i++) { CConfig::CustomGOA *pGoaLhs = (CConfig::CustomGOA *)DPA_GetPtr(m_hdpaCustomGoOfflineActions, i); CConfig::CustomGOA *pGoaRhs = (CConfig::CustomGOA *)DPA_GetPtr(rhs.m_hdpaCustomGoOfflineActions, i); if (NULL != pGoaLhs && NULL != pGoaRhs) { if (pGoaLhs->GetAction() != pGoaRhs->GetAction()) break;
if (0 != lstrcmpi(pGoaLhs->GetServerName(), pGoaRhs->GetServerName())) break; } } bMatch = (i == cLhs); } } } return bMatch; }
//-----------------------------------------------------------------------------
// CustomGOAAddDlg
// "GOA" == Go Offline Action
// This dialog is for adding customized offline actions for particular
// network servers.
// It is invoked via the "Add..." button on the "Advanced" dialog.
//-----------------------------------------------------------------------------
const CustomGOAAddDlg::CtlActions CustomGOAAddDlg::m_rgCtlActions[CConfig::eNumOfflineActions] = { { IDC_RBN_GOOFFLINE_SILENT, CConfig::eGoOfflineSilent }, { IDC_RBN_GOOFFLINE_FAIL, CConfig::eGoOfflineFail } };
const DWORD CustomGOAAddDlg::m_rgHelpIDs[] = { IDOK, IDH_OK, IDCANCEL, IDH_CANCEL, IDC_EDIT_GOOFFLINE_SERVER, HIDC_EDIT_GOOFFLINE_SERVER, IDC_STATIC4, HIDC_EDIT_GOOFFLINE_SERVER, // "Computer:"
IDC_RBN_GOOFFLINE_SILENT, HIDC_RBN_GOOFFLINE_SILENT, IDC_RBN_GOOFFLINE_FAIL, HIDC_RBN_GOOFFLINE_FAIL, IDC_BTN_BROWSEFORSERVER, HIDC_BTN_BROWSEFORSERVER, IDC_GRP_GOOFFLINE_DEFAULTS, DWORD(-1), IDC_STATIC2, DWORD(-1), // icon
IDC_STATIC3, DWORD(-1), // icon's text
0, 0 };
CustomGOAAddDlg::CustomGOAAddDlg( HINSTANCE hInstance, HWND hwndParent, LPTSTR pszServer, UINT cchServer, CConfig::OfflineAction *pAction ) : m_hInstance(hInstance), m_hwndParent(hwndParent), m_hwndDlg(NULL), m_hwndEdit(NULL), m_pszServer(pszServer), m_cchServer(cchServer), m_pAction(pAction) { TraceAssert(NULL != pAction); }
int CustomGOAAddDlg::Run( void ) { return (int)DialogBoxParam(m_hInstance, MAKEINTRESOURCE(IDD_CSC_ADVOPTIONS_ADD), m_hwndParent, DlgProc, (LPARAM)this); }
INT_PTR CALLBACK CustomGOAAddDlg::DlgProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam ) { BOOL bResult = FALSE;
CustomGOAAddDlg *pThis = (CustomGOAAddDlg *)GetWindowLongPtr(hDlg, DWLP_USER);
switch(message) { case WM_INITDIALOG: { pThis = (CustomGOAAddDlg *)lParam; TraceAssert(NULL != pThis); SetWindowLongPtr(hDlg, DWLP_USER, (LONG_PTR)pThis); bResult = pThis->OnInitDialog(hDlg, (HWND)wParam, lParam); break; }
case WM_COMMAND: TraceAssert(NULL != pThis); bResult = pThis->OnCommand(hDlg, HIWORD(wParam), LOWORD(wParam), (HWND)lParam); break;
case WM_HELP: TraceAssert(NULL != pThis); bResult = pThis->OnHelp(hDlg, (LPHELPINFO)lParam); break;
case WM_CONTEXTMENU: TraceAssert(NULL != pThis); bResult = pThis->OnContextMenu((HWND)wParam, LOWORD(lParam), HIWORD(lParam)); break;
case WM_DESTROY: TraceAssert(NULL != pThis); bResult = pThis->OnDestroy(hDlg); break;
default: break; }
return bResult; }
BOOL CustomGOAAddDlg::OnInitDialog( HWND hDlg, HWND hwndFocus, LPARAM lInitParam ) { m_hwndDlg = hDlg; //
// Set the default go-offline action radio buttons.
//
for (int i = 0; i < ARRAYSIZE(m_rgCtlActions); i++) { CheckDlgButton(hDlg, m_rgCtlActions[i].idRbn, m_rgCtlActions[i].action == *m_pAction ? BST_CHECKED : BST_UNCHECKED); } m_hwndEdit = GetDlgItem(hDlg, IDC_EDIT_GOOFFLINE_SERVER);
SetWindowText(m_hwndEdit, m_pszServer);
return TRUE; }
void CustomGOAAddDlg::GetEnteredServerName( LPTSTR pszServer, UINT cchServer, bool bTrimLeadingJunk ) { //
// Get the server name.
//
GetWindowText(m_hwndEdit, pszServer, cchServer); if (bTrimLeadingJunk) { //
// Remove any leading "\\" or space user might have entered.
//
LPTSTR pszLookahead = pszServer; while(*pszLookahead && (TEXT('\\') == *pszLookahead || TEXT(' ') == *pszLookahead)) pszLookahead++;
if (pszLookahead > pszServer) { // overlapped
TraceAssert(StringByteSize(pszLookahead) < cchServer*sizeof(TCHAR)); MoveMemory(pszServer, pszLookahead, StringByteSize(pszLookahead)); } } }
//
// Query the dialog and return the state through pointer args.
//
void CustomGOAAddDlg::GetActionInfo( LPTSTR pszServer, UINT cchServer, CConfig::OfflineAction *pAction ) { TraceAssert(NULL != pszServer); TraceAssert(NULL != pAction); //
// Get the action radio button setting.
//
*pAction = CConfig::eNumOfflineActions; for(int i = 0; i < ARRAYSIZE(m_rgCtlActions); i++) { if (BST_CHECKED == IsDlgButtonChecked(m_hwndDlg, m_rgCtlActions[i].idRbn)) { *pAction = m_rgCtlActions[i].action; break; } } TraceAssert(CConfig::eNumOfflineActions != *pAction);
//
// Retrieve the entered server name with leading junk removed.
// Should have just a bare server name (i.e. "worf").
//
GetEnteredServerName(pszServer, cchServer, true); }
//
// See if server name entered could be valid.
// This weeds out things like entering a UNC share name
// instead of a server name.
//
// "\\rastaman" or "rastaman" is good.
// "\\rastaman\ntwin" is bad.
//
// This function will display error UI if the name is not valid.
// Returns:
// true = Name is a UNC server name.
// false = Name is not a UNC server name.
//
bool CustomGOAAddDlg::CheckServerNameEntered( void ) { TCHAR szPath[MAX_PATH]; szPath[0] = TEXT('\\'); szPath[1] = TEXT('\\'); GetEnteredServerName(szPath+2, ARRAYSIZE(szPath)-2, true); // Name with leading "\\" and any spaces removed.
if (!::PathIsUNCServer(szPath)) { //
// Name provided is not a UNC server name.
//
GetEnteredServerName(szPath, ARRAYSIZE(szPath), false); // Name "as entered".
CscMessageBox(m_hwndDlg, MB_OK | MB_ICONERROR, m_hInstance, IDS_ERR_NOTSERVERNAME, szPath); return false; } return true; }
BOOL CustomGOAAddDlg::OnCommand( HWND hDlg, WORD wNotifyCode, WORD wID, HWND hwndCtl ) { switch(wID) { case IDOK: //
// First see if server name entered could be valid.
// This weeds out things like entering a UNC share name
// instead of a server name.
//
// "\\rastaman" or "rastaman" is good.
// "\\rastaman\ntwin" is bad.
//
// This function will display error UI if the name is not valid.
//
if (!CheckServerNameEntered()) { //
// Invalid name. Return focus to the server name edit control.
//
SetFocus(GetDlgItem(hDlg, IDC_EDIT_GOOFFLINE_SERVER)); break; }
GetActionInfo(m_pszServer, m_cchServer, m_pAction); //
// Fall through...
//
case IDCANCEL: EndDialog(hDlg, wID); break;
case IDC_BTN_BROWSEFORSERVER: { TCHAR szServer[MAX_PATH];
szServer[0] = TEXT('\0'); BrowseForServer(hDlg, szServer, ARRAYSIZE(szServer)); if (TEXT('\0') != szServer[0]) { SetWindowText(GetDlgItem(hDlg, IDC_EDIT_GOOFFLINE_SERVER), szServer); } break; } } return FALSE; }
//
// Use the SHBrowseForFolder dialog to find a server.
//
void CustomGOAAddDlg::BrowseForServer( HWND hDlg, LPTSTR pszServer, UINT cchServer ) { TraceAssert(NULL != pszServer); LPTSTR pszTitle; if (0 < LoadStringAlloc(&pszTitle, m_hInstance, IDS_BROWSEFORSERVER)) { BROWSEINFO bi; ZeroMemory(&bi, sizeof(bi));
//
// Start browsing in the network folder.
//
LPITEMIDLIST pidlRoot = NULL; HRESULT hr = SHGetSpecialFolderLocation(hDlg, CSIDL_NETWORK, &pidlRoot); if (SUCCEEDED(hr)) { TCHAR szServer[MAX_PATH];
bi.hwndOwner = hDlg; bi.pidlRoot = pidlRoot; bi.pszDisplayName = szServer; // assumed to be at least MAX_PATH
bi.lpszTitle = pszTitle; bi.ulFlags = BIF_BROWSEFORCOMPUTER; bi.lpfn = NULL; bi.lParam = NULL; bi.iImage = 0;
LPITEMIDLIST pidlFolder = SHBrowseForFolder(&bi); ILFree(pidlRoot); if (NULL != pidlFolder) { ILFree(pidlFolder); StringCchCopy(pszServer, cchServer, szServer); } } else { CscMessageBox(hDlg, MB_OK, Win32Error(HRESULT_CODE(hr))); } LocalFree(pszTitle); } }
BOOL CustomGOAAddDlg::OnHelp( HWND hDlg, LPHELPINFO pHelpInfo ) { if (HELPINFO_WINDOW == pHelpInfo->iContextType) { int idCtl = GetDlgCtrlID((HWND)pHelpInfo->hItemHandle); WinHelp((HWND)pHelpInfo->hItemHandle, UseWindowsHelp(idCtl) ? NULL : c_szHelpFile, HELP_WM_HELP, (DWORD_PTR)((LPTSTR)m_rgHelpIDs)); } return FALSE; }
BOOL CustomGOAAddDlg::OnContextMenu( HWND hwndItem, int xPos, int yPos ) { int idCtl = GetDlgCtrlID(hwndItem); WinHelp(hwndItem, UseWindowsHelp(idCtl) ? NULL : c_szHelpFile, HELP_CONTEXTMENU, (DWORD_PTR)((LPTSTR)m_rgHelpIDs));
return FALSE; }
BOOL CustomGOAAddDlg::OnDestroy( HWND hDlg ) {
return FALSE; }
//-----------------------------------------------------------------------------
// CustomGOAEditDlg
// "GOA" == Go Offline Action
// This dialog is for editing customized offline actions for particular
// network servers.
// It is invoked via the "Edit..." button on the "Advanced" dialog.
//-----------------------------------------------------------------------------
const CustomGOAEditDlg::CtlActions CustomGOAEditDlg::m_rgCtlActions[CConfig::eNumOfflineActions] = { { IDC_RBN_GOOFFLINE_SILENT, CConfig::eGoOfflineSilent }, { IDC_RBN_GOOFFLINE_FAIL, CConfig::eGoOfflineFail }, };
const DWORD CustomGOAEditDlg::m_rgHelpIDs[] = { IDOK, IDH_OK, IDCANCEL, IDH_CANCEL, IDC_TXT_GOOFFLINE_SERVER, HIDC_TXT_GOOFFLINE_SERVER, IDC_STATIC4, HIDC_TXT_GOOFFLINE_SERVER, // "Computer:"
IDC_RBN_GOOFFLINE_SILENT, HIDC_RBN_GOOFFLINE_SILENT, IDC_RBN_GOOFFLINE_FAIL, HIDC_RBN_GOOFFLINE_FAIL, IDC_GRP_GOOFFLINE_DEFAULTS, DWORD(-1), IDC_STATIC2, DWORD(-1), // icon
IDC_STATIC3, DWORD(-1), // icon's text.
0, 0 };
CustomGOAEditDlg::CustomGOAEditDlg( HINSTANCE hInstance, HWND hwndParent, LPCTSTR pszServer, // NULL == multi-server.
CConfig::OfflineAction *pAction ) : m_hInstance(hInstance), m_hwndParent(hwndParent), m_hwndDlg(NULL), m_pAction(pAction) { TraceAssert(NULL != pAction);
if (NULL != pszServer && TEXT('\0') != *pszServer) { StringCchCopy(m_szServer, ARRAYSIZE(m_szServer), pszServer); } else { m_szServer[0] = TEXT('\0'); LoadString(m_hInstance, IDS_GOOFFLINE_MULTISERVER, m_szServer, ARRAYSIZE(m_szServer)); } }
int CustomGOAEditDlg::Run( void ) { return (int)DialogBoxParam(m_hInstance, MAKEINTRESOURCE(IDD_CSC_ADVOPTIONS_EDIT), m_hwndParent, DlgProc, (LPARAM)this); }
INT_PTR CALLBACK CustomGOAEditDlg::DlgProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam ) { BOOL bResult = FALSE;
CustomGOAEditDlg *pThis = (CustomGOAEditDlg *)GetWindowLongPtr(hDlg, DWLP_USER);
switch(message) { case WM_INITDIALOG: { pThis = (CustomGOAEditDlg *)lParam; TraceAssert(NULL != pThis); SetWindowLongPtr(hDlg, DWLP_USER, (LONG_PTR)pThis); bResult = pThis->OnInitDialog(hDlg, (HWND)wParam, lParam); break; }
case WM_COMMAND: TraceAssert(NULL != pThis); bResult = pThis->OnCommand(hDlg, HIWORD(wParam), LOWORD(wParam), (HWND)lParam); break;
case WM_HELP: TraceAssert(NULL != pThis); bResult = pThis->OnHelp(hDlg, (LPHELPINFO)lParam); break;
case WM_CONTEXTMENU: TraceAssert(NULL != pThis); bResult = pThis->OnContextMenu((HWND)wParam, LOWORD(lParam), HIWORD(lParam)); break;
case WM_DESTROY: TraceAssert(NULL != pThis); bResult = pThis->OnDestroy(hDlg); break;
default: break; } return bResult; }
BOOL CustomGOAEditDlg::OnInitDialog( HWND hDlg, HWND hwndFocus, LPARAM lInitParam ) { m_hwndDlg = hDlg; //
// Set the default go-offline action radio buttons.
//
for (int i = 0; i < ARRAYSIZE(m_rgCtlActions); i++) { CheckDlgButton(hDlg, m_rgCtlActions[i].idRbn, m_rgCtlActions[i].action == *m_pAction ? BST_CHECKED : BST_UNCHECKED); } SetWindowText(GetDlgItem(hDlg, IDC_TXT_GOOFFLINE_SERVER), m_szServer);
return TRUE; }
//
// Query the dialog and return the state through pointer args.
//
void CustomGOAEditDlg::GetActionInfo( CConfig::OfflineAction *pAction ) { TraceAssert(NULL != pAction); //
// Get the action radio button setting.
//
*pAction = CConfig::eNumOfflineActions; for(int i = 0; i < ARRAYSIZE(m_rgCtlActions); i++) { if (BST_CHECKED == IsDlgButtonChecked(m_hwndDlg, m_rgCtlActions[i].idRbn)) { *pAction = m_rgCtlActions[i].action; break; } } TraceAssert(CConfig::eNumOfflineActions != *pAction); }
BOOL CustomGOAEditDlg::OnCommand( HWND hDlg, WORD wNotifyCode, WORD wID, HWND hwndCtl ) { switch(wID) { case IDOK: GetActionInfo(m_pAction); //
// Fall through...
//
case IDCANCEL: EndDialog(hDlg, wID); break; } return FALSE; }
BOOL CustomGOAEditDlg::OnHelp( HWND hDlg, LPHELPINFO pHelpInfo ) { if (HELPINFO_WINDOW == pHelpInfo->iContextType) { int idCtl = GetDlgCtrlID((HWND)pHelpInfo->hItemHandle); WinHelp((HWND)pHelpInfo->hItemHandle, UseWindowsHelp(idCtl) ? NULL : c_szHelpFile, HELP_WM_HELP, (DWORD_PTR)((LPTSTR)m_rgHelpIDs)); }
return FALSE; }
BOOL CustomGOAEditDlg::OnContextMenu( HWND hwndItem, int xPos, int yPos ) { int idCtl = GetDlgCtrlID(hwndItem); WinHelp(hwndItem, UseWindowsHelp(idCtl) ? NULL : c_szHelpFile, HELP_CONTEXTMENU, (DWORD_PTR)((LPTSTR)m_rgHelpIDs));
return FALSE; }
BOOL CustomGOAEditDlg::OnDestroy( HWND hDlg ) {
return FALSE; }
//-----------------------------------------------------------------------------
// COfflineFilesSheet
//-----------------------------------------------------------------------------
COfflineFilesSheet::COfflineFilesSheet( HINSTANCE hInstance, LONG *pDllRefCount, HWND hwndParent ) : m_hInstance(hInstance), m_pDllRefCount(pDllRefCount), m_hwndParent(hwndParent) { InterlockedIncrement(m_pDllRefCount); }
COfflineFilesSheet::~COfflineFilesSheet( void ) { ASSERT( 0 != *m_pDllRefCount ); InterlockedDecrement(m_pDllRefCount); }
BOOL CALLBACK COfflineFilesSheet::AddPropSheetPage( HPROPSHEETPAGE hpage, LPARAM lParam ) { PROPSHEETHEADER * ppsh = (PROPSHEETHEADER *)lParam;
if (ppsh->nPages < COfflineFilesSheet::MAXPAGES) { ppsh->phpage[ppsh->nPages++] = hpage; return TRUE; }
return FALSE; }
//
// Static function for creating and running an instance of the
// CSCUI options dialog. This is the ONLY function callable
// by non-member code to create and run an options dialog.
//
DWORD COfflineFilesSheet::CreateAndRun( HINSTANCE hInstance, HWND hwndParent, LONG *pDllRefCount, BOOL bAsync ) { //
// First try to activate an existing instance of the prop sheet.
//
TCHAR szSheetTitle[MAX_PATH] = {0}; LoadString(hInstance, IDS_CSCOPT_PROPSHEET_TITLE, szSheetTitle, ARRAYSIZE(szSheetTitle));
HWND hwnd = FindWindowEx(NULL, NULL, WC_DIALOG, szSheetTitle); if (NULL == hwnd || !SetForegroundWindow(hwnd)) { //
// This thread param buffer will be deleted by the
// thread proc.
//
ThreadParams *ptp = new ThreadParams(hwndParent, pDllRefCount); if (NULL != ptp) { if (bAsync) { //
// LoadLibrary on ourselves so that we stay in memory even
// if the caller calls FreeLibrary. We'll call FreeLibrary
// when the thread proc exits.
//
ptp->SetModuleHandle(LoadLibrary(TEXT("cscui.dll")));
DWORD idThread; HANDLE hThread = CreateThread(NULL, 0, ThreadProc, ptp, 0, &idThread);
if (INVALID_HANDLE_VALUE != hThread) { CloseHandle(hThread); } else { //
// Thread creation failed. Delete thread param buffer.
//
delete ptp; } } else { ThreadProc(ptp); } } } return 0; }
//
// The share dialog's thread proc.
//
DWORD WINAPI COfflineFilesSheet::ThreadProc( LPVOID pvParam ) { ThreadParams *ptp = reinterpret_cast<ThreadParams *>(pvParam); TraceAssert(NULL != ptp);
HINSTANCE hInstance = ptp->m_hInstance; // Save local copy.
COfflineFilesSheet dlg(ptp->m_hInstance ? ptp->m_hInstance : g_hInstance, ptp->m_pDllRefCount, ptp->m_hwndParent); dlg.Run();
delete ptp;
if (NULL != hInstance) FreeLibraryAndExitThread(hInstance, 0);
return 0; }
DWORD COfflineFilesSheet::Run( void ) { DWORD dwError = ERROR_SUCCESS;
if (CConfig::GetSingleton().NoConfigCache()) { Trace((TEXT("System policy restricts configuration of Offline Files cache"))); return ERROR_SUCCESS; }
TCHAR szSheetTitle[MAX_PATH] = {0}; LoadString(m_hInstance, IDS_CSCOPT_PROPSHEET_TITLE, szSheetTitle, ARRAYSIZE(szSheetTitle));
HPROPSHEETPAGE rghPages[COfflineFilesSheet::MAXPAGES]; PROPSHEETHEADER psh; ZeroMemory(&psh, sizeof(psh)); //
// Define sheet.
//
psh.dwSize = sizeof(PROPSHEETHEADER); psh.dwFlags = 0; psh.hInstance = m_hInstance; psh.hwndParent = m_hwndParent; psh.pszIcon = MAKEINTRESOURCE(IDI_CSCUI_ICON); psh.pszCaption = szSheetTitle; psh.nPages = 0; psh.nStartPage = 0; psh.phpage = rghPages;
//
// Policy doesn't prevent user from configuring CSC cache.
// Add the dynamic page(s).
//
CCoInit coinit; HRESULT hr = coinit.Result(); if (SUCCEEDED(hr)) { IShellExtInit *psei; hr = CoCreateInstance(CLSID_OfflineFilesOptions, NULL, CLSCTX_INPROC_SERVER, IID_IShellExtInit, (void **)&psei);
if (SUCCEEDED(hr)) { IShellPropSheetExt *pspse; hr = psei->QueryInterface(IID_IShellPropSheetExt, (void **)&pspse); if (SUCCEEDED(hr)) { hr = pspse->AddPages(AddPropSheetPage, (LPARAM)&psh); pspse->Release(); pspse = NULL; }
switch(PropertySheet(&psh)) { case ID_PSREBOOTSYSTEM: //
// User wants to change enabled state of CSC. Requires reboot.
//
if (IDYES == CscMessageBox(m_hwndParent, MB_YESNO | MB_ICONINFORMATION, m_hInstance, IDS_REBOOTSYSTEM)) { dwError = CSCUIRebootSystem(); if (ERROR_SUCCESS != dwError) { Trace((TEXT("Reboot failed with error %d"), dwError)); CscMessageBox(m_hwndParent, MB_ICONWARNING | MB_OK, Win32Error(dwError), m_hInstance, IDS_ERR_REBOOTFAILED); } } dwError = ERROR_SUCCESS; // Run() succeeded.
break;
case -1: { dwError = GetLastError(); Trace((TEXT("PropertySheet failed with error %d"), dwError)); CscWin32Message(m_hwndParent, dwError, CSCUI::SEV_ERROR); break; } default: break; } psei->Release(); psei = NULL; } else { Trace((TEXT("CoCreateInstance failed with result 0x%08X"), hr)); } } else { Trace((TEXT("CoInitialize failed with result 0x%08X"), hr)); }
return dwError; }
//
// Exported API for launching the CSC Options property sheet.
// If policy disallows configuration of CSC, we display a messagebox
// with an error message.
//
DWORD CSCUIOptionsPropertySheetEx(HWND hwndParent, BOOL bAsync) { DWORD dwResult = ERROR_SUCCESS; if (!CConfig::GetSingleton().NoConfigCache()) { dwResult = COfflineFilesSheet::CreateAndRun(g_hInstance, hwndParent, &g_cRefCount, bAsync); } else { CscMessageBox(hwndParent, MB_OK, g_hInstance, IDS_ERR_POLICY_NOCONFIGCSC); } return dwResult; }
DWORD CSCUIOptionsPropertySheet(HWND hwndParent) { return CSCUIOptionsPropertySheetEx(hwndParent, TRUE); }
STDAPI_(void) CSCOptions_RunDLLW(HWND hwndStub, HINSTANCE /*hInst*/, LPWSTR pszCmdLine, int /*nCmdShow*/) { DllAddRef();
HWND hwndParent = FindWindowW(NULL, pszCmdLine); CSCUIOptionsPropertySheetEx(hwndParent ? hwndParent : hwndStub, FALSE);
DllRelease(); }
STDAPI_(void) CSCOptions_RunDLLA(HWND hwndStub, HINSTANCE hInst, LPSTR pszCmdLine, int nCmdShow) { WCHAR wszCmdLine[MAX_PATH];
DllAddRef();
SHAnsiToUnicode(pszCmdLine, wszCmdLine, ARRAYSIZE(wszCmdLine)); CSCOptions_RunDLLW(hwndStub, hInst, wszCmdLine, nCmdShow);
DllRelease(); }
//-----------------------------------------------------------------------------
// CscOptPropSheetExt
// This is the shell prop sheet extension implementation for creating
// the "Offline Folders" property page.
//-----------------------------------------------------------------------------
CscOptPropSheetExt::CscOptPropSheetExt( HINSTANCE hInstance, LONG *pDllRefCnt ) : m_cRef(0), m_pDllRefCnt(pDllRefCnt), m_hInstance(hInstance), m_pOfflineFoldersPg(NULL) { InterlockedIncrement(m_pDllRefCnt); }
CscOptPropSheetExt::~CscOptPropSheetExt( void ) { delete m_pOfflineFoldersPg; ASSERT( 0 != *m_pDllRefCnt ); InterlockedDecrement(m_pDllRefCnt); }
HRESULT CscOptPropSheetExt::QueryInterface( REFIID riid, void **ppvOut ) { HRESULT hr = E_NOINTERFACE;
if (NULL == ppvOut) return E_INVALIDARG;
*ppvOut = NULL; if (IID_IUnknown == riid || IID_IShellExtInit == riid) { *ppvOut = static_cast<IShellExtInit *>(this); } else if (IID_IShellPropSheetExt == riid) { *ppvOut = static_cast<IShellPropSheetExt *>(this); } if (NULL != *ppvOut) { ((LPUNKNOWN)*ppvOut)->AddRef(); hr = NOERROR; } return hr; }
ULONG CscOptPropSheetExt::AddRef( void ) { return InterlockedIncrement(&m_cRef); }
ULONG CscOptPropSheetExt::Release( void ) { ASSERT( 0 != m_cRef ); ULONG cRef = InterlockedDecrement(&m_cRef); if ( 0 == cRef ) { delete this; } return cRef; }
HRESULT CscOptPropSheetExt::Initialize( LPCITEMIDLIST pidlFolder, LPDATAOBJECT pdtobj, HKEY hkeyProgID ) { return NOERROR; }
HRESULT CscOptPropSheetExt::AddPages( LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam ) { TraceAssert(NULL != lpfnAddPage); TraceAssert(NULL == m_pOfflineFoldersPg);
HRESULT hr = E_FAIL; // Assume failure.
if (!CConfig::GetSingleton().NoConfigCache()) { hr = E_OUTOFMEMORY; HPROPSHEETPAGE hOfflineFoldersPg = NULL; m_pOfflineFoldersPg = new COfflineFilesPage(m_hInstance, static_cast<IShellPropSheetExt *>(this)); if (NULL != m_pOfflineFoldersPg) { hr = AddPage(lpfnAddPage, lParam, *m_pOfflineFoldersPg, &hOfflineFoldersPg); } }
return hr; }
HRESULT CscOptPropSheetExt::AddPage( LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam, const COfflineFilesPage& pg, HPROPSHEETPAGE *phPage ) { TraceAssert(NULL != lpfnAddPage); TraceAssert(NULL != phPage);
HRESULT hr = E_FAIL;
PROPSHEETPAGE psp;
psp.dwSize = sizeof(psp); psp.dwFlags = PSP_USECALLBACK | PSP_USEREFPARENT; psp.hInstance = m_hInstance; psp.pszTemplate = MAKEINTRESOURCE(pg.GetDlgTemplateID()); psp.hIcon = NULL; psp.pszTitle = NULL; psp.pfnDlgProc = pg.GetDlgProcPtr(); psp.lParam = (LPARAM)&pg; psp.pcRefParent = (UINT *)m_pDllRefCnt; psp.pfnCallback = (LPFNPSPCALLBACK)pg.GetCallbackFuncPtr();
*phPage = CreatePropertySheetPage(&psp); if (NULL != *phPage) { if (!lpfnAddPage(*phPage, lParam)) { Trace((TEXT("AddPage Failed to add page."))); DestroyPropertySheetPage(*phPage); *phPage = NULL; } } else { Trace((TEXT("CreatePropertySheetPage failed."))); } if (NULL != *phPage) { AddRef(); hr = NOERROR; } return hr; }
STDAPI COfflineFilesOptions_CreateInstance( REFIID riid, void **ppv ) { HRESULT hr = E_NOINTERFACE;
if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IShellPropSheetExt) || IsEqualIID(riid, IID_IShellExtInit)) { //
// Create the property sheet extension to handle the CSC options property
// pages.
//
CscOptPropSheetExt *pse = new CscOptPropSheetExt(g_hInstance, &g_cRefCount); if (NULL != pse) { pse->AddRef(); hr = pse->QueryInterface(riid, ppv); pse->Release(); } else hr = E_OUTOFMEMORY; } if (FAILED(hr)) { *ppv = NULL; }
return hr; }
//
// Initialize the "config items" object.
// This loads all of the user preference/policy information for the page when
// the page is first created.
//
void COfflineFilesPage::CConfigItems::Load( void ) { #define LOADCFG(i, f) m_rgItems[i].dwValue = DWORD(c.f(&m_rgItems[i].bSetByPolicy))
CConfig& c = CConfig::GetSingleton();
LOADCFG(iCFG_NOCONFIGCACHE, NoConfigCache); LOADCFG(iCFG_SYNCATLOGOFF, SyncAtLogoff); LOADCFG(iCFG_SYNCATLOGON, SyncAtLogon); LOADCFG(iCFG_NOREMINDERS, NoReminders); LOADCFG(iCFG_REMINDERFREQMINUTES, ReminderFreqMinutes); LOADCFG(iCFG_DEFCACHESIZE, DefaultCacheSize); LOADCFG(iCFG_NOCACHEVIEWER, NoCacheViewer); LOADCFG(iCFG_CSCENABLED, CscEnabled); LOADCFG(iCFG_ENCRYPTCACHE, EncryptCache);
#undef LOADCFG
}
|