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

5367 lines
163 KiB

//+-------------------------------------------------------------------------
//
// 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
}