// 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_SNAPSHOT_CONTEXT #include #include // VSS_SWPRV_ProviderId #include // VssFreeSnapshotProperties #include // VssFreeSnapshotProperties #include #include // GetNodeClusterState #include #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 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 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 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 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 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 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; }