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
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;
|
|
}
|