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.
 
 
 
 
 
 

1426 lines
45 KiB

// CVSSProp.cpp : implementation file
//
#include "stdafx.h"
#include "utils.h"
#include "VSSProp.h"
#include "RemDlg.h"
#include "Settings.h"
#include "Hosting.h"
#include "uihelp.h"
#include "msgcomm.h" // vss error msg
#include <vss.h> // _VSS_SNAPSHOT_CONTEXT
#include <vsmgmt.h>
#include <vsswprv.h> // VSS_SWPRV_ProviderId
#include <vswriter.h>// VssFreeSnapshotProperties
#include <vsbackup.h> // VssFreeSnapshotProperties
#include <htmlhelp.h>
#include <clusapi.h> // GetNodeClusterState
#include <lm.h>
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CVSSProp property page
IMPLEMENT_DYNCREATE(CVSSProp, CPropertyPage)
CVSSProp::CVSSProp() : CPropertyPage(CVSSProp::IDD)
{
//{{AFX_DATA_INIT(CVSSProp)
//}}AFX_DATA_INIT
m_strComputer = _T("");
m_strSelectedVolume = _T("");
m_strDisabled.LoadString(IDS_DISABLED);
m_hImageList = NULL;
m_nScrollbarWidth = 16;
m_nSnapshotListColumnWidth = 0;
m_nSnapshotListCountPerPage = 0;
}
CVSSProp::CVSSProp(LPCTSTR pszComputer, LPCTSTR pszVolume) : CPropertyPage(CVSSProp::IDD)
{
#ifdef DEBUG
OutputDebugString(_T("CVSSProp::CVSSPRop\n"));
#endif
if (!pszComputer)
m_strComputer = _T("");
else
m_strComputer = pszComputer + (TWO_WHACKS(pszComputer) ? 2 : 0);
m_strSelectedVolume = (pszVolume ? pszVolume : _T(""));
m_strDisabled.LoadString(IDS_DISABLED);
m_hImageList = NULL;
m_nScrollbarWidth = 16;
m_nSnapshotListColumnWidth = 0;
m_nSnapshotListCountPerPage = 0;
}
CVSSProp::~CVSSProp()
{
#ifdef DEBUG
OutputDebugString(_T("CVSSProp::~CVSSPRop\n"));
#endif
if(NULL != m_hImageList)
{
ImageList_Destroy(m_hImageList);
m_hImageList = NULL;
}
}
HRESULT CVSSProp::StoreShellExtPointer(IShellPropSheetExt* piShellExt)
{
if (!piShellExt)
return E_INVALIDARG;
// This assignment will call AddRef().
// Release() will later be called by ~CVSSProp().
m_spiShellExt = piShellExt;
return S_OK;
}
void CVSSProp::DoDataExchange(CDataExchange* pDX)
{
CPropertyPage::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CVSSProp)
DDX_Control(pDX, IDC_VOLUME_LIST, m_ctrlVolumeList);
DDX_Control(pDX, IDC_ENABLE, m_ctrlEnable);
DDX_Control(pDX, IDC_DISABLE, m_ctrlDisable);
DDX_Control(pDX, IDC_SETTINGS, m_ctrlSettings);
DDX_Control(pDX, IDC_SNAPSHOT_LIST, m_ctrlSnapshotList);
DDX_Control(pDX, IDC_CREATE, m_ctrlCreate);
DDX_Control(pDX, IDC_DELETE, m_ctrlDelete);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CVSSProp, CPropertyPage)
//{{AFX_MSG_MAP(CVSSProp)
ON_BN_CLICKED(IDC_CREATE, OnCreateNow)
ON_BN_CLICKED(IDC_DELETE, OnDeleteNow)
ON_NOTIFY(LVN_ITEMCHANGED, IDC_SNAPSHOT_LIST, OnItemchangedSnapshotList)
ON_NOTIFY(LVN_ITEMCHANGED, IDC_VOLUME_LIST, OnItemchangedVolumeList)
ON_WM_CONTEXTMENU()
ON_WM_HELPINFO()
ON_BN_CLICKED(IDC_ENABLE, OnEnable)
ON_BN_CLICKED(IDC_DISABLE, OnDisable)
ON_BN_CLICKED(IDC_SETTINGS, OnSettings)
ON_NOTIFY(NM_CLICK, IDC_EXPLANATION, OnHelpLink)
ON_NOTIFY(NM_RETURN, IDC_EXPLANATION, OnHelpLink)
ON_MESSAGE(WM_SETPAGEFOCUS, OnSetPageFocus)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CVSSProp message handlers
//
// If we have successfully taken one snapshot of the specified volume, we will
// return VSS_S_ASYNC_FINISHED.
//
HRESULT CVSSProp::TakeOneSnapshotNow(IN LPCTSTR pszVolumeName)
{
if (!pszVolumeName || !*pszVolumeName)
return E_INVALIDARG;
VSS_ID SnapshotSetId = GUID_NULL;
HRESULT hr = m_spiCoord->StartSnapshotSet(&SnapshotSetId);
if (SUCCEEDED(hr))
{
VSS_ID SnapshotId = GUID_NULL;
hr = m_spiCoord->AddToSnapshotSet(
(PTSTR)pszVolumeName,
VSS_SWPRV_ProviderId,
&SnapshotId);
if (SUCCEEDED(hr))
{
CComPtr<IVssAsync> spiAsync;
hr = m_spiCoord->DoSnapshotSet(NULL, &spiAsync);
if (SUCCEEDED(hr))
{
hr = spiAsync->Wait();
if (SUCCEEDED(hr))
{
HRESULT hrStatus = S_OK;
hr = spiAsync->QueryStatus(&hrStatus, NULL);
if (SUCCEEDED(hr))
{
return hrStatus;
}
}
}
}
}
return hr;
}
//
// OnCreateNow works when only one volume is currently selected.
//
void CVSSProp::OnCreateNow()
{
CWaitCursor wait;
if (m_strSelectedVolume.IsEmpty())
return;
PTSTR pszVolumeName = GetVolumeName(&m_VolumeList, m_strSelectedVolume);
ASSERT(pszVolumeName);
HRESULT hr = TakeOneSnapshotNow(pszVolumeName);
if (VSS_S_ASYNC_FINISHED == (DWORD)hr)
{
UpdateSnapshotList();
UpdateDiffArea();
UpdateEnableDisableButtons();
} else if (FAILED(hr))
{
DoErrMsgBox(m_hWnd, MB_OK, hr, IDS_TAKESNAPSHOT_ERROR, m_strSelectedVolume);
}
}
//
// OnDeleteNow works on multi-selected snapshots when only one volume is currently selected.
//
void CVSSProp::OnDeleteNow()
{
CWaitCursor wait;
if (m_strSelectedVolume.IsEmpty())
return;
BOOL bAtLeastOneDeleted = FALSE;
HRESULT hr = S_OK;
int nIndex = -1;
while (-1 != (nIndex = m_ctrlSnapshotList.GetNextItem(nIndex, LVNI_SELECTED)))
{
VSSUI_SNAPSHOT *pSnapshot = (VSSUI_SNAPSHOT *)GetListViewItemData(m_ctrlSnapshotList.m_hWnd, nIndex);
ASSERT(pSnapshot);
if (!pSnapshot)
continue; // shouldn't happen, skip it just in case
LONG lDeletedSnapshots = 0;
VSS_ID ProblemSnapshotId = GUID_NULL;
hr = m_spiCoord->DeleteSnapshots(pSnapshot->idSnapshot,
VSS_OBJECT_SNAPSHOT,
TRUE,
&lDeletedSnapshots,
&ProblemSnapshotId
);
if (SUCCEEDED(hr) || VSS_E_OBJECT_NOT_FOUND == hr)
{
hr = S_OK; // ignore if snapshot has already been deleted
bAtLeastOneDeleted = TRUE;
}
if (FAILED(hr))
break;
}
if (FAILED(hr))
DoErrMsgBox(m_hWnd, MB_OK, hr, IDS_DELETESNAPSHOTS_ERROR, m_strSelectedVolume);
if (bAtLeastOneDeleted)
{
UpdateSnapshotList();
if (0 == m_ctrlSnapshotList.GetSelectedCount())
{
::SendMessage(m_hWnd, DM_SETDEFID, (WPARAM)IDC_CREATE, (LPARAM)0);
m_ctrlCreate.SetFocus(); // DeleteNow button has been disabled, set focus to CreateNow button
}
UpdateDiffArea();
UpdateEnableDisableButtons();
}
}
#define HKCU_VSSUI_KEY _T("Software\\Microsoft\\VSSUI")
#define REGVALUENAME_ENABLE _T("EnableReminderOff")
#define REGVALUENAME_DISABLE _T("DisableReminderOff")
void CVSSProp::OnEnable()
{
BOOL bShowReminder = TRUE;
HKEY hKey = NULL;
LONG lErr = RegCreateKeyEx(HKEY_CURRENT_USER,
HKCU_VSSUI_KEY,
0, // reserved
_T(""), // lpClass
REG_OPTION_NON_VOLATILE,
KEY_QUERY_VALUE | KEY_SET_VALUE,
NULL, // lpSecurityAttributes
&hKey,
NULL // lpdwDisposition
);
if (ERROR_SUCCESS == lErr)
{
DWORD dwType = 0;
DWORD dwData = 0;
DWORD cbData = sizeof(DWORD);
lErr = RegQueryValueEx(hKey, REGVALUENAME_ENABLE, 0, &dwType, (LPBYTE)&dwData, &cbData);
if (ERROR_SUCCESS == lErr && REG_DWORD == dwType && 0 != dwData)
bShowReminder = FALSE;
}
int nRet = IDOK;
if (bShowReminder)
{
CReminderDlgEx dlg(hKey, REGVALUENAME_ENABLE);
nRet = dlg.DoModal();
}
if (hKey)
RegCloseKey(hKey);
if (IDOK == nRet)
DoEnable();
}
HRESULT CVSSProp::DoEnable()
{
CWaitCursor wait;
HRESULT hr = S_OK;
LVITEM lvItem = {0};
int nSelectedCount = m_ctrlVolumeList.GetSelectedCount();
if (nSelectedCount > 0)
{
POSITION pos = m_ctrlVolumeList.GetFirstSelectedItemPosition();
while (pos)
{
int nIndex = m_ctrlVolumeList.GetNextSelectedItem(pos);
VSSUI_VOLUME *pVolume = (VSSUI_VOLUME *)GetListViewItemData(m_ctrlVolumeList.m_hWnd, nIndex);
ASSERT(pVolume);
if (!pVolume)
continue; // shouldn't happen, skip it just in case
// bug#495719 - prompt instructions when enabling vss on a small volume
ULONGLONG llDiffVolumeTotalSpace = 0;
ULONGLONG llDiffVolumeFreeSpace = 0;
hr = GetVolumeSpace(
m_spiDiffSnapMgmt,
pVolume->pszDisplayName,
&llDiffVolumeTotalSpace,
&llDiffVolumeFreeSpace);
if (SUCCEEDED(hr) && llDiffVolumeTotalSpace < MINIMUM_DIFF_LIMIT) // ignore the failure of GetVolumeSpace
{
VSSUI_DIFFAREA diffArea;
hr = GetDiffAreaInfo(m_spiDiffSnapMgmt, &m_VolumeList, pVolume->pszVolumeName, &diffArea);
if (S_OK != hr) // failed to retrieve diff area association, assume it doesn't have one
{
DoErrMsgBox(m_hWnd, MB_OK, 0, IDS_CANNOT_ENABLE_SMALL_VOLUME, pVolume->pszDisplayName);
continue; // skip enabling this selection
}
}
// bug#494209: take a snapshot first, if failed, no need to create the default schedule
//
// take one snapshot now, it will create default diff area association if none
//
hr = TakeOneSnapshotNow(pVolume->pszVolumeName);
if (VSS_S_ASYNC_FINISHED == (DWORD)hr)
{
if (1 == nSelectedCount)
UpdateSnapshotList();
UpdateDiffArea(nIndex, pVolume->pszVolumeName);
} else if (FAILED(hr))
{
DoErrMsgBox(m_hWnd, MB_OK, hr, IDS_TAKESNAPSHOT_ERROR, pVolume->pszDisplayName);
break;
}
//
// if none, create default schedule for that volume
//
CComPtr<ITask> spiTask;
hr = FindScheduledTimewarpTask(
(ITaskScheduler *)m_spiTS,
pVolume->pszVolumeName,
&spiTask);
if (FAILED(hr))
{
DoErrMsgBox(m_hWnd, MB_OK, hr, IDS_FINDSCHEDULE_ERROR, pVolume->pszDisplayName);
} else if (S_FALSE == hr) // task not found
{
(void)DeleteAllScheduledTimewarpTasks((ITaskScheduler *)m_spiTS,
m_strComputer,
pVolume->pszVolumeName,
TRUE // i_bDeleteDisabledOnesOnly
);
hr = CreateDefaultEnableSchedule(
(ITaskScheduler *)m_spiTS,
m_strComputer,
pVolume->pszDisplayName,
pVolume->pszVolumeName,
&spiTask);
if (FAILED(hr))
DoErrMsgBox(m_hWnd, MB_OK, hr, IDS_CREATESCHEDULE_ERROR, pVolume->pszDisplayName);
}
if (SUCCEEDED(hr))
{
UpdateSchedule((ITask *)spiTask, nIndex);
// bug#494491: we need to update the Enable/Disable button when schedule changes.
if (1 == nSelectedCount)
{
UpdateEnableDisableButtons();
::SendMessage(m_hWnd, DM_SETDEFID, (WPARAM)IDC_DISABLE, (LPARAM)0);
m_ctrlDisable.SetFocus(); // Disable button will be enabled, set focus to it
}
} else
break;
}
}
return hr;
}
void CVSSProp::OnDisable()
{
BOOL bShowReminder = TRUE;
HKEY hKey = NULL;
LONG lErr = RegCreateKeyEx(HKEY_CURRENT_USER,
HKCU_VSSUI_KEY,
0, // reserved
_T(""), // lpClass
REG_OPTION_NON_VOLATILE,
KEY_QUERY_VALUE | KEY_SET_VALUE,
NULL, // lpSecurityAttributes
&hKey,
NULL // lpdwDisposition
);
if (ERROR_SUCCESS == lErr)
{
DWORD dwType = 0;
DWORD dwData = 0;
DWORD cbData = sizeof(DWORD);
lErr = RegQueryValueEx(hKey, REGVALUENAME_DISABLE, 0, &dwType, (LPBYTE)&dwData, &cbData);
if (ERROR_SUCCESS == lErr && REG_DWORD == dwType && 0 != dwData)
bShowReminder = FALSE;
}
int nRet = IDOK;
if (bShowReminder)
{
CReminderDlg dlg(hKey, REGVALUENAME_DISABLE);
nRet = dlg.DoModal();
}
if (hKey)
RegCloseKey(hKey);
if (IDOK == nRet)
DoDisable();
}
HRESULT CVSSProp::DoDisable()
{
CWaitCursor wait;
HRESULT hr = S_OK;
LVITEM lvItem = {0};
int nSelectedCount = m_ctrlVolumeList.GetSelectedCount();
if (nSelectedCount > 0)
{
POSITION pos = m_ctrlVolumeList.GetFirstSelectedItemPosition();
while (pos)
{
int nIndex = m_ctrlVolumeList.GetNextSelectedItem(pos);
VSSUI_VOLUME *pVolume = (VSSUI_VOLUME *)GetListViewItemData(m_ctrlVolumeList.m_hWnd, nIndex);
ASSERT(pVolume);
if (!pVolume)
continue; // shouldn't happen, skip it just in case
//
// delete all snapshots on that volume
//
hr = DeleteAllSnapshotsOnVolume(pVolume->pszVolumeName);
if (1 == nSelectedCount)
UpdateSnapshotList();
if (FAILED(hr))
{
DoErrMsgBox(m_hWnd, MB_OK, hr, IDS_DELETESNAPSHOTS_ERROR, pVolume->pszDisplayName);
break;
}
//
// delete all scheduled tasks for that volume
//
hr = DeleteAllScheduledTimewarpTasks((ITaskScheduler *)m_spiTS,
m_strComputer,
pVolume->pszVolumeName,
FALSE // i_bDeleteDisabledOnesOnly
);
if (SUCCEEDED(hr))
UpdateSchedule(NULL, nIndex);
else
{
DoErrMsgBox(m_hWnd, MB_OK, hr, IDS_FINDSCHEDULE_ERROR, pVolume->pszDisplayName);
break;
}
//
// remove diff area associate for that volume
//
VSSUI_DIFFAREA diffArea;
hr = GetDiffAreaInfo(m_spiDiffSnapMgmt, &m_VolumeList, pVolume->pszVolumeName, &diffArea);
if (S_OK == hr)
{
PTSTR pszDiffAreaVolumeName = GetVolumeName(&m_VolumeList, diffArea.pszDiffVolumeDisplayName);
ASSERT(pszDiffAreaVolumeName);
hr = m_spiDiffSnapMgmt->ChangeDiffAreaMaximumSize(
pVolume->pszVolumeName,
pszDiffAreaVolumeName,
VSS_ASSOC_REMOVE);
if (VSS_E_OBJECT_NOT_FOUND == hr)
hr = S_OK; // ignore if diff assoc has already been deleted
}
if (SUCCEEDED(hr))
{
UpdateDiffArea(nIndex, pVolume->pszVolumeName);
}
else if (hr == VSS_E_VOLUME_IN_USE)
{
// Special error message (Bug 519124)
DoErrMsgBox(m_hWnd, MB_OK, hr, IDS_DELETEDIFFAREA_ERROR_IN_USE, pVolume->pszDisplayName);
}
else
{
DoErrMsgBox(m_hWnd, MB_OK, hr, IDS_DELETEDIFFAREA_ERROR, pVolume->pszDisplayName);
}
// bug#494491: we need to update the Enable/Disable button even when failure occurs.
if (1 == nSelectedCount)
{
UpdateEnableDisableButtons();
if (SUCCEEDED(hr))
{
::SendMessage(m_hWnd, DM_SETDEFID, (WPARAM)IDC_ENABLE, (LPARAM)0);
m_ctrlEnable.SetFocus(); // Enable button will be enabled, set focus to it
}
}
if (FAILED(hr))
break;
}
}
return hr;
}
HRESULT CVSSProp::DeleteAllSnapshotsOnVolume(
IN LPCTSTR pszVolumeName
)
{
if (!pszVolumeName || !*pszVolumeName)
return E_INVALIDARG;
CComPtr<IVssEnumObject> spiEnumSnapshots;
HRESULT hr = m_spiMgmt->QuerySnapshotsByVolume(
(PTSTR)pszVolumeName,
VSS_SWPRV_ProviderId,
&spiEnumSnapshots
);
if (S_OK == hr)
{
VSS_OBJECT_PROP Prop;
VSS_SNAPSHOT_PROP* pSnapProp = &(Prop.Obj.Snap);
ULONG ulFetched = 0;
while (SUCCEEDED(spiEnumSnapshots->Next(1, &Prop, &ulFetched)) && ulFetched > 0)
{
if (VSS_OBJECT_SNAPSHOT != Prop.Type)
return E_FAIL;
if (pSnapProp->m_lSnapshotAttributes & VSS_VOLSNAP_ATTR_CLIENT_ACCESSIBLE)
{
LONG lDeletedSnapshots = 0;
VSS_ID ProblemSnapshotId = GUID_NULL;
hr = m_spiCoord->DeleteSnapshots(pSnapProp->m_SnapshotId,
VSS_OBJECT_SNAPSHOT,
TRUE,
&lDeletedSnapshots,
&ProblemSnapshotId
);
VssFreeSnapshotProperties(pSnapProp);
if (VSS_E_OBJECT_NOT_FOUND == hr)
hr = S_OK; // ignore if snapshot has already been deleted
if (FAILED(hr))
break;
}
}
}
return hr;
}
void CVSSProp::OnSettings()
{
CWaitCursor wait;
CSettings dlg(m_strComputer, m_strSelectedVolume);
HRESULT hr = dlg.Init(m_spiDiffSnapMgmt,
m_spiTS,
m_bCluster,
&m_VolumeList,
!m_SnapshotList.empty());
if (FAILED(hr))
{
DoErrMsgBox(m_hWnd, MB_OK, hr, IDS_SETTINGS_ERROR, m_strSelectedVolume);
return;
}
dlg.DoModal();
UpdateDiffArea();
UpdateSchedule();
UpdateEnableDisableButtons();
UpdateSnapshotList();
}
void CVSSProp::OnItemchangedSnapshotList(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
m_ctrlDelete.EnableWindow(0 < m_ctrlSnapshotList.GetSelectedCount());
*pResult = 0;
}
void CVSSProp::OnItemchangedVolumeList(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
int nSelectedCount = m_ctrlVolumeList.GetSelectedCount();
if (0 == nSelectedCount)
{
m_ctrlEnable.EnableWindow(FALSE);
m_ctrlDisable.EnableWindow(FALSE);
m_ctrlSettings.EnableWindow(FALSE);
m_ctrlCreate.EnableWindow(FALSE);
m_ctrlDelete.EnableWindow(FALSE);
} else
{
m_ctrlSettings.EnableWindow(1 == nSelectedCount);
m_ctrlCreate.EnableWindow(1 == nSelectedCount);
UpdateEnableDisableButtons();
}
if (1 < nSelectedCount)
{
m_strSelectedVolume = _T("");
} else
{
int nIndex = m_ctrlVolumeList.GetNextItem(-1, LVNI_SELECTED);
if (-1 != nIndex)
{
m_strSelectedVolume = m_ctrlVolumeList.GetItemText(nIndex, 0);
} else
{
m_strSelectedVolume = _T("");
}
}
CWaitCursor wait;
UpdateSnapshotList();
*pResult = 0;
}
void CVSSProp::OnContextMenu(CWnd* pWnd, CPoint point)
{
if (!pWnd)
return;
::WinHelp(pWnd->GetSafeHwnd(),
VSSUI_CTX_HELP_FILE,
HELP_CONTEXTMENU,
(DWORD_PTR)(PVOID)aMenuHelpIDsForVSSProp);
}
BOOL CVSSProp::OnHelpInfo(HELPINFO* pHelpInfo)
{
if (!pHelpInfo ||
pHelpInfo->iContextType != HELPINFO_WINDOW ||
pHelpInfo->iCtrlId < 0)
return FALSE;
::WinHelp((HWND)pHelpInfo->hItemHandle,
VSSUI_CTX_HELP_FILE,
HELP_WM_HELP,
(DWORD_PTR)(PVOID)aMenuHelpIDsForVSSProp);
return TRUE;
}
BOOL CVSSProp::OnInitDialog()
{
CWaitCursor wait;
CPropertyPage::OnInitDialog();
m_bHideAllControls = FALSE;
CString strMsg;
HRESULT hr = S_OK;
do {
hr = InitInterfacePointers();
if (FAILED(hr))
{
GetMsg(strMsg, hr, IDS_VSSPROP_INIT_ERROR);
m_bHideAllControls = TRUE;
break;
}
hr = GetVolumes(); // get a list of volumes that are suitable for taking snapshots
if (FAILED(hr))
{
GetMsg(strMsg, hr, IDS_VSSPROP_GETVOLUMES_ERROR);
m_bHideAllControls = TRUE;
break;
}
if (m_VolumeList.empty())
{
GetMsg(strMsg, 0, IDS_VSSPROP_EMPTY_VOLUMELIST);
m_bHideAllControls = TRUE;
break;
}
if (!m_strSelectedVolume.IsEmpty())
{
BOOL bFound = FALSE;
for (VSSUI_VOLUME_LIST::iterator i = m_VolumeList.begin(); i != m_VolumeList.end(); i++)
{
if (!m_strSelectedVolume.CompareNoCase((*i)->pszDisplayName))
{
bFound = TRUE;
break;
}
}
// in case of mounted drive without assigned drive letter, the SelectedVolume could be the GUID name
if (!bFound)
{
for (VSSUI_VOLUME_LIST::iterator i = m_VolumeList.begin(); i != m_VolumeList.end(); i++)
{
if (!m_strSelectedVolume.CompareNoCase((*i)->pszVolumeName))
{
bFound = TRUE;
m_strSelectedVolume = (*i)->pszDisplayName; // change the selected volume to hold the display name
break;
}
}
}
if (!bFound)
{
GetMsg(strMsg, 0, IDS_VSSPROP_VOLUME_ILEGIBLE, m_strSelectedVolume);
m_bHideAllControls = TRUE;
break;
}
}
} while (0);
if (m_bHideAllControls)
{
GetDlgItem(IDC_VSSPROP_ERROR)->SetWindowText(strMsg);
GetDlgItem(IDC_VSSPROP_ERROR)->EnableWindow(TRUE);
for (int i = IDC_EXPLANATION; i < IDC_VSSPROP_ERROR; i++)
{
GetDlgItem(i)->EnableWindow(FALSE);
GetDlgItem(i)->ShowWindow(SW_HIDE);
}
} else
{
GetDlgItem(IDC_VSSPROP_ERROR)->EnableWindow(FALSE);
GetDlgItem(IDC_VSSPROP_ERROR)->ShowWindow(SW_HIDE);
//
// insert column header of the Volume listbox
//
HWND hwnd = m_ctrlVolumeList.m_hWnd;
m_hImageList = ImageList_LoadBitmap(
_Module.GetResourceInstance(),
MAKEINTRESOURCE(IDB_VOLUME_16x16),
16,
8,
CLR_DEFAULT);
ListView_SetImageList(hwnd, m_hImageList, LVSIL_SMALL);
AddLVColumns(
hwnd,
IDS_VOLUMELIST_COLUMN_VOLUME,
IDS_VOLUMELIST_COLUMN_USED - IDS_VOLUMELIST_COLUMN_VOLUME + 1);
ListView_SetExtendedListViewStyle(hwnd, LVS_EX_FULLROWSELECT);
//
// insert column headers for the snapshot listbox
//
AddLVColumns(
m_ctrlSnapshotList.m_hWnd,
IDS_SNAPSHOTLIST_COLUMN_TIMESTAMP,
IDS_SNAPSHOTLIST_COLUMN_TIMESTAMP - IDS_SNAPSHOTLIST_COLUMN_TIMESTAMP + 1);
ListView_SetExtendedListViewStyle(m_ctrlSnapshotList.m_hWnd, LVS_EX_FULLROWSELECT);
// remember the original column width, later we need to adjust the width
// to eliminate the horizontal scroll bar
m_nSnapshotListColumnWidth = ListView_GetColumnWidth(m_ctrlSnapshotList.m_hWnd, 0);
// before we insert rows, we need to remember the initial volume such that
// we can pass it to SelectVolume() later.
CString cstrVolume = m_strSelectedVolume;
InsertVolumeInfo(hwnd);
InsertDiffAreaInfo(hwnd);
InsertShareInfo(hwnd);
InsertScheduleInfo(hwnd);
SelectVolume(hwnd, cstrVolume);
// Since we're using the medium property page size (227, 215) as other shell pages,
// we need to adjust the column width a little bit to make the UI look prettier.
// We adjust column width after InsertVolumeInfo call, where we might shrink the
// Share column to eliminate the horizontal bar.
int nAdjustment = 10; // we find this number by experiment
int nCol = IDS_VOLUMELIST_COLUMN_VOLUME - IDS_VOLUMELIST_COLUMN_VOLUME;
int nColumnWidth = ListView_GetColumnWidth(hwnd, nCol);
if (nAdjustment < nColumnWidth)
{
// shrink the Volume column
ListView_SetColumnWidth(hwnd, nCol, nColumnWidth - nAdjustment);
// widen the Next Run Time column
nCol = IDS_VOLUMELIST_COLUMN_NEXTRUNTIME - IDS_VOLUMELIST_COLUMN_VOLUME;
nColumnWidth = ListView_GetColumnWidth(hwnd, nCol);
ListView_SetColumnWidth(hwnd, nCol, nColumnWidth + nAdjustment);
}
UpdateEnableDisableButtons();
UpdateSnapshotList();
}
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
void CVSSProp::_ResetInterfacePointers()
{
if ((IVssSnapshotMgmt *)m_spiMgmt)
m_spiMgmt.Release();
if ((IVssCoordinator *)m_spiCoord)
m_spiCoord.Release();
if ((IVssDifferentialSoftwareSnapshotMgmt *)m_spiDiffSnapMgmt)
m_spiDiffSnapMgmt.Release();
if ((ITaskScheduler *)m_spiTS)
m_spiTS.Release();
m_bCluster = FALSE;
}
HRESULT CVSSProp::InitInterfacePointers()
{
_ResetInterfacePointers();
HRESULT hr = S_OK;
if (m_strComputer.IsEmpty())
{
hr = CoCreateInstance(CLSID_VssSnapshotMgmt,
NULL,
CLSCTX_LOCAL_SERVER,
IID_IVssSnapshotMgmt,
(void **)&m_spiMgmt);
if (SUCCEEDED(hr))
hr = CoCreateInstance(CLSID_VSSCoordinator,
NULL,
CLSCTX_LOCAL_SERVER,
IID_IVssCoordinator,
(void **)&m_spiCoord);
} else
{
COSERVERINFO serverInfo = {0};
serverInfo.pwszName = (LPTSTR)(LPCTSTR)m_strComputer;
IID iid = IID_IVssSnapshotMgmt;
MULTI_QI MQI = {0};
MQI.pIID = &iid;
hr = CoCreateInstanceEx(CLSID_VssSnapshotMgmt,
NULL,
CLSCTX_REMOTE_SERVER,
&serverInfo,
1,
&MQI);
if (SUCCEEDED(hr))
{
m_spiMgmt = (IVssSnapshotMgmt *)MQI.pItf;
ZeroMemory(&MQI, sizeof(MQI));
iid = IID_IVssCoordinator;
MQI.pIID = &iid;
hr = CoCreateInstanceEx(CLSID_VSSCoordinator,
NULL,
CLSCTX_REMOTE_SERVER,
&serverInfo,
1,
&MQI);
if (SUCCEEDED(hr))
m_spiCoord = (IVssCoordinator *)MQI.pItf;
}
}
if (SUCCEEDED(hr))
hr = m_spiCoord->SetContext(VSS_CTX_CLIENT_ACCESSIBLE);
if (SUCCEEDED(hr))
hr = m_spiMgmt->GetProviderMgmtInterface(
VSS_SWPRV_ProviderId,
IID_IVssDifferentialSoftwareSnapshotMgmt,
(IUnknown**)&m_spiDiffSnapMgmt);
if (SUCCEEDED(hr))
hr = CoCreateInstance(CLSID_CTaskScheduler,
NULL,
CLSCTX_INPROC_SERVER,
IID_ITaskScheduler,
(void **)&m_spiTS);
if (SUCCEEDED(hr))
{
// SetTargetComputer requires server name to start with whackwhack
if (m_strComputer.IsEmpty())
hr = m_spiTS->SetTargetComputer(NULL);
else
{
CString strTargetComputer = _T("\\\\");
strTargetComputer += m_strComputer;
hr = m_spiTS->SetTargetComputer((LPCTSTR)strTargetComputer);
}
}
if (SUCCEEDED(hr))
{
DWORD dwState = 0;
if (ERROR_SUCCESS == GetNodeClusterState(m_strComputer, &dwState) && ClusterStateRunning == dwState)
{
m_bCluster = TRUE;
}
}
if (FAILED(hr))
_ResetInterfacePointers();
return hr;
}
HRESULT CVSSProp::GetVolumes()
{
if (!m_spiMgmt)
return E_INVALIDARG;
FreeVolumeList(&m_VolumeList);
CComPtr<IVssEnumMgmtObject> spiEnumMgmt;
HRESULT hr = m_spiMgmt->QueryVolumesSupportedForSnapshots(
VSS_SWPRV_ProviderId,
VSS_CTX_CLIENT_ACCESSIBLE,
&spiEnumMgmt);
if (FAILED(hr))
return hr;
VSS_MGMT_OBJECT_PROP Prop;
VSS_VOLUME_PROP *pVolProp = &(Prop.Obj.Vol);
ULONG ulFetched = 0;
while (SUCCEEDED(hr = spiEnumMgmt->Next(1, &Prop, &ulFetched)) && ulFetched > 0)
{
if (VSS_MGMT_OBJECT_VOLUME != Prop.Type)
return E_FAIL;
VSSUI_VOLUME *pVolInfo = (VSSUI_VOLUME *)calloc(1, sizeof(VSSUI_VOLUME));
if (pVolInfo)
{
lstrcpyn(pVolInfo->pszVolumeName, pVolProp->m_pwszVolumeName, MAX_PATH);
lstrcpyn(pVolInfo->pszDisplayName, pVolProp->m_pwszVolumeDisplayName, MAX_PATH);
m_VolumeList.push_back(pVolInfo);
} else
{
FreeVolumeList(&m_VolumeList);
hr = E_OUTOFMEMORY;
}
CoTaskMemFree(pVolProp->m_pwszVolumeName);
CoTaskMemFree(pVolProp->m_pwszVolumeDisplayName);
if (FAILED(hr))
break;
}
if (hr == S_FALSE)
// End of loop detected
hr = S_OK;
return hr;
}
HRESULT CVSSProp::InsertVolumeInfo(HWND hwnd)
{
ListView_DeleteAllItems(hwnd);
for (VSSUI_VOLUME_LIST::iterator i = m_VolumeList.begin(); i != m_VolumeList.end(); i++)
{
LVITEM lvItem = {0};
lvItem.mask = LVIF_TEXT | LVIF_PARAM | LVIF_IMAGE;
lvItem.lParam = (LPARAM)(*i);
lvItem.pszText = (*i)->pszDisplayName;
lvItem.iSubItem = 0;
lvItem.iImage = 1;
ListView_InsertItem(hwnd, &lvItem);
}
if (m_VolumeList.size() > 0)
{
int nVolumeListCountPerPage = ListView_GetCountPerPage(hwnd);
if (m_VolumeList.size() > nVolumeListCountPerPage)
{
// we shrink the "Shares" column to eliminate the honrizontal scroll bar
int nCol = IDS_VOLUMELIST_COLUMN_NUMOFSHARES - IDS_VOLUMELIST_COLUMN_VOLUME;
int nSharesColumnWidth = ListView_GetColumnWidth(hwnd, nCol);
ListView_SetColumnWidth(hwnd, nCol, nSharesColumnWidth - m_nScrollbarWidth);
}
}
return S_OK;
}
//
// Update diff area column of the currently selected volume
//
HRESULT CVSSProp::UpdateDiffArea()
{
if (m_strSelectedVolume.IsEmpty())
return E_INVALIDARG;
int nIndex = m_ctrlVolumeList.GetNextItem(-1, LVNI_SELECTED);
ASSERT(-1 != nIndex);
PTSTR pszVolumeName = GetVolumeName(&m_VolumeList, m_strSelectedVolume);
ASSERT(pszVolumeName);
return UpdateDiffArea(nIndex, pszVolumeName);
}
//
// Update diff area column of the specified volume
//
HRESULT CVSSProp::UpdateDiffArea(int nIndex, LPCTSTR pszVolumeName)
{
CString strMsg = _T("");
VSSUI_DIFFAREA diffArea;
HRESULT hr = GetDiffAreaInfo(m_spiDiffSnapMgmt, &m_VolumeList, pszVolumeName, &diffArea);
if (S_OK == hr)
{
//
// "Used on DiffVolume"
//
TCHAR szUsed[MAX_PATH];
DWORD dwSize = sizeof(szUsed)/sizeof(TCHAR);
DiskSpaceToString(diffArea.llUsedDiffSpace, szUsed, &dwSize);
strMsg.FormatMessage(IDS_USED_ON_VOLUME, szUsed, diffArea.pszDiffVolumeDisplayName);
}
LVITEM lvItem = {0};
lvItem.iItem = nIndex;
lvItem.mask = LVIF_TEXT;
lvItem.pszText = (PTSTR)(LPCTSTR)strMsg;
lvItem.iSubItem = IDS_VOLUMELIST_COLUMN_USED - IDS_VOLUMELIST_COLUMN_VOLUME;
m_ctrlVolumeList.SetItem(&lvItem);
return hr;
}
HRESULT CVSSProp::InsertDiffAreaInfo(HWND hwnd)
{
if (m_VolumeList.empty())
return S_OK;
int nIndex = -1;
while (-1 != (nIndex = ListView_GetNextItem(hwnd, nIndex, LVNI_ALL)))
{
VSSUI_VOLUME *pVolume = (VSSUI_VOLUME *)GetListViewItemData(hwnd, nIndex);
ASSERT(pVolume);
if (!pVolume)
continue; // shouldn't happen, skip it just in case
UpdateDiffArea(nIndex, pVolume->pszVolumeName);
}
return S_OK;
}
HRESULT CVSSProp::InsertShareInfo(HWND hwnd)
{
if (m_VolumeList.empty())
return S_OK;
SHARE_INFO_2 *pInfo = NULL;
DWORD dwEntriesRead = 0;
DWORD dwEntriesTotal = 0;
DWORD dwRet = NetShareEnum((PTSTR)(LPCTSTR)m_strComputer,
2,
(LPBYTE *)&pInfo,
-1, //max
&dwEntriesRead,
&dwEntriesTotal,
NULL // resume handle
);
if (NERR_Success != dwRet)
return HRESULT_FROM_WIN32(dwRet);
TCHAR szNumOfShares[256];
int nIndex = -1;
while (-1 != (nIndex = ListView_GetNextItem(hwnd, nIndex, LVNI_ALL)))
{
VSSUI_VOLUME *pVolume = (VSSUI_VOLUME *)GetListViewItemData(hwnd, nIndex);
ASSERT(pVolume);
if (!pVolume)
continue; // shouldn't happen, skip it just in case
UINT count = 0;
for (DWORD i = 0; i < dwEntriesRead; i++)
{
if (pInfo[i].shi2_type == STYPE_DISKTREE)
{
if (!mylstrncmpi(pInfo[i].shi2_path, pVolume->pszDisplayName, lstrlen(pVolume->pszDisplayName)))
count++;
}
}
_stprintf(szNumOfShares, _T("%d"), count); // no need to localize the format
LVITEM lvItem = {0};
lvItem.iItem = nIndex;
lvItem.mask = LVIF_TEXT;
lvItem.pszText = szNumOfShares;
lvItem.iSubItem = IDS_VOLUMELIST_COLUMN_NUMOFSHARES - IDS_VOLUMELIST_COLUMN_VOLUME;
ListView_SetItem(hwnd, &lvItem);
}
NetApiBufferFree(pInfo);
return S_OK;
}
//
// Update schedule column of the currently selected volume
//
HRESULT CVSSProp::UpdateSchedule()
{
if (m_strSelectedVolume.IsEmpty())
return E_INVALIDARG;
int nIndex = m_ctrlVolumeList.GetNextItem(-1, LVNI_SELECTED);
ASSERT(-1 != nIndex);
VSSUI_VOLUME *pVolume = (VSSUI_VOLUME *)GetListViewItemData(m_ctrlVolumeList.m_hWnd, nIndex);
ASSERT(pVolume);
if (!pVolume)
return E_FAIL;
return UpdateSchedule(nIndex, pVolume->pszVolumeName);
}
//
// Update schedule column of the specified volume
//
HRESULT CVSSProp::UpdateSchedule(int nIndex, LPCTSTR pszVolumeName)
{
if (!pszVolumeName || !*pszVolumeName)
return E_INVALIDARG;
CComPtr<ITask> spiTask;
(void)FindScheduledTimewarpTask((ITaskScheduler *)m_spiTS, pszVolumeName, &spiTask);
UpdateSchedule((ITask *)spiTask, nIndex);
return S_OK;
}
void CVSSProp::UpdateSchedule(ITask * i_piTask, int nIndex)
{
BOOL bEnabled = FALSE;
SYSTEMTIME stNextRun = {0};
if (i_piTask)
(void)GetScheduledTimewarpTaskStatus(i_piTask, &bEnabled, &stNextRun);
LVITEM lvItem = {0};
lvItem.iItem = nIndex;
lvItem.mask = LVIF_IMAGE;
lvItem.iImage = (bEnabled ? 0 : 1);
lvItem.iSubItem = 0;
m_ctrlVolumeList.SetItem(&lvItem);
TCHAR szNextRun[MAX_PATH] = _T("");
DWORD dwSize = sizeof(szNextRun)/sizeof(TCHAR);
if (bEnabled)
SystemTimeToString(&stNextRun, szNextRun, &dwSize);
else
lstrcpyn(szNextRun, m_strDisabled, MAX_PATH);
ZeroMemory(&lvItem, sizeof(LVITEM));
lvItem.iItem = nIndex;
lvItem.mask = LVIF_TEXT;
lvItem.pszText = szNextRun;
lvItem.iSubItem = IDS_VOLUMELIST_COLUMN_NEXTRUNTIME - IDS_VOLUMELIST_COLUMN_VOLUME;
m_ctrlVolumeList.SetItem(&lvItem);
}
HRESULT CVSSProp::InsertScheduleInfo(HWND hwnd)
{
if (m_VolumeList.empty())
return S_OK;
int nIndex = -1;
while (-1 != (nIndex = ListView_GetNextItem(hwnd, nIndex, LVNI_ALL)))
{
VSSUI_VOLUME *pVolume = (VSSUI_VOLUME *)GetListViewItemData(hwnd, nIndex);
ASSERT(pVolume);
if (!pVolume)
continue; // shouldn't happen, skip it just in case
UpdateSchedule(nIndex, pVolume->pszVolumeName);
}
return S_OK;
}
void CVSSProp::SelectVolume(HWND hwnd, LPCTSTR pszVolume)
{
if (m_VolumeList.empty())
return;
int nIndex = -1;
if (pszVolume && *pszVolume)
{
while (-1 != (nIndex = ListView_GetNextItem(hwnd, nIndex, LVNI_ALL)))
{
VSSUI_VOLUME *pVolume = (VSSUI_VOLUME *)GetListViewItemData(hwnd, nIndex);
ASSERT(pVolume);
if (!pVolume)
continue; // shouldn't happen, skip it just in case
if (!lstrcmpi(pszVolume, pVolume->pszDisplayName))
break;
}
}
if (-1 == nIndex)
nIndex = 0;
ListView_SetItemState(hwnd, nIndex, LVIS_SELECTED | LVIS_FOCUSED, 0xffffffff);
ListView_EnsureVisible(hwnd, nIndex, FALSE);
}
HRESULT CVSSProp::GetSnapshots(LPCTSTR pszVolume)
{
if (!pszVolume || !*pszVolume)
return E_INVALIDARG;
FreeSnapshotList(&m_SnapshotList);
CComPtr<IVssEnumObject> spiEnumSnapshots;
HRESULT hr = m_spiMgmt->QuerySnapshotsByVolume((PTSTR)pszVolume, VSS_SWPRV_ProviderId, &spiEnumSnapshots);
if (S_OK == hr)
{
VSS_OBJECT_PROP Prop;
VSS_SNAPSHOT_PROP* pSnapProp = &(Prop.Obj.Snap);
ULONG ulFetched = 0;
while (SUCCEEDED(spiEnumSnapshots->Next(1, &Prop, &ulFetched)) && ulFetched > 0)
{
if (VSS_OBJECT_SNAPSHOT != Prop.Type)
return E_FAIL;
if (pSnapProp->m_lSnapshotAttributes & VSS_VOLSNAP_ATTR_CLIENT_ACCESSIBLE)
{
VSSUI_SNAPSHOT *pSnapInfo = (VSSUI_SNAPSHOT *)calloc(1, sizeof(VSSUI_SNAPSHOT));
if (pSnapInfo)
{
pSnapInfo->idSnapshot = pSnapProp->m_SnapshotId;
pSnapInfo->vssTimeStamp = pSnapProp->m_tsCreationTimestamp;
m_SnapshotList.push_back(pSnapInfo);
} else
{
FreeSnapshotList(&m_SnapshotList);
hr = E_OUTOFMEMORY;
}
VssFreeSnapshotProperties(pSnapProp);
if (FAILED(hr))
break;
}
}
}
return hr;
}
HRESULT CVSSProp::UpdateSnapshotList()
{
if (m_strSelectedVolume.IsEmpty())
{
m_ctrlSnapshotList.DeleteAllItems();
m_ctrlDelete.EnableWindow(FALSE);
return S_OK;
}
HRESULT hr = GetSnapshots(m_strSelectedVolume);
m_ctrlSnapshotList.DeleteAllItems();
m_ctrlDelete.EnableWindow(FALSE);
if (SUCCEEDED(hr))
{
TCHAR szTimeStamp[256];
DWORD dwSize = 0;
LVITEM lvItem = {0};
for (VSSUI_SNAPSHOT_LIST::iterator i = m_SnapshotList.begin(); i != m_SnapshotList.end(); i++)
{
SYSTEMTIME st = {0};
VssTimeToSystemTime(&((*i)->vssTimeStamp), &st);
dwSize = sizeof(szTimeStamp)/sizeof(TCHAR);
SystemTimeToString(&st, szTimeStamp, &dwSize);
ZeroMemory(&lvItem, sizeof(lvItem));
lvItem.mask = LVIF_TEXT | LVIF_PARAM;
lvItem.lParam = (LPARAM)(*i);
lvItem.pszText = szTimeStamp;
m_ctrlSnapshotList.InsertItem(&lvItem);
}
if (m_SnapshotList.size() > 0)
{
HWND hwnd = m_ctrlSnapshotList.m_hWnd;
if (!m_nSnapshotListCountPerPage)
m_nSnapshotListCountPerPage = ListView_GetCountPerPage(hwnd);
if (m_SnapshotList.size() > m_nSnapshotListCountPerPage)
ListView_SetColumnWidth(hwnd, 0, m_nSnapshotListColumnWidth - m_nScrollbarWidth);
else
ListView_SetColumnWidth(hwnd, 0, m_nSnapshotListColumnWidth);
}
}
return hr;
}
//
// If multi-selection: both "Enable" and "Disable" buttons are enabled.
// If single-selection:
// The "Enable" button is disabled whenever there is a schedule.
// The "Disable" button is disabled when no schedule and no diff association.
//
void CVSSProp::UpdateEnableDisableButtons()
{
int nSelectedCount = m_ctrlVolumeList.GetSelectedCount();
if (nSelectedCount == 0)
return;
if (nSelectedCount > 1)
{
m_ctrlEnable.EnableWindow(TRUE);
m_ctrlDisable.EnableWindow(TRUE);
return;
}
int nIndex = m_ctrlVolumeList.GetNextItem(-1, LVNI_SELECTED);
if (-1 == nIndex)
return; // shouldn't happen
CString strNextRunTime;
strNextRunTime = m_ctrlVolumeList.GetItemText(nIndex, IDS_VOLUMELIST_COLUMN_NEXTRUNTIME - IDS_VOLUMELIST_COLUMN_VOLUME);
if (strNextRunTime.CompareNoCase(m_strDisabled))
{
// schedule exists
m_ctrlEnable.EnableWindow(FALSE);
m_ctrlDisable.EnableWindow(TRUE);
return;
}
// no schedule
m_ctrlEnable.EnableWindow(TRUE);
CString strUsed;
strUsed = m_ctrlVolumeList.GetItemText(nIndex, IDS_VOLUMELIST_COLUMN_USED - IDS_VOLUMELIST_COLUMN_VOLUME);
m_ctrlDisable.EnableWindow(!strUsed.IsEmpty());
}
void CVSSProp::OnHelpLink(NMHDR* pNMHDR, LRESULT* pResult)
{
CWaitCursor wait;
::HtmlHelp(0, _T("timewarp.chm"), HH_DISPLAY_TOPIC, (DWORD_PTR)(_T("deploy_timewarp_client.htm")));
*pResult = 0;
}
//
// Q148388 How to Change Default Control Focus on CPropertyPageEx
//
BOOL CVSSProp::OnSetActive()
{
BOOL fRet = CPropertyPage::OnSetActive();
if (!m_bHideAllControls)
{
PostMessage(WM_SETPAGEFOCUS, 0, 0L);
}
return fRet;
}
LRESULT CVSSProp::OnSetPageFocus(WPARAM wParam, LPARAM lParam)
{
GetDlgItem(IDC_VOLUME_LIST)->SetFocus();
return 0;
}