|
|
//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation, 1997 - 1999
//
// File: purge.cpp
//
//--------------------------------------------------------------------------
#include "pch.h"
#pragma hdrstop
#include <cscuiext.h> // CSCUIRemoveFolderFromCache
#include "purge.h"
#include "msgbox.h"
#include "resource.h"
#include "security.h"
#include "util.h"
#include "strings.h"
//
// We use a path buffer with this many TCHARs for the recursive tree walk.
//
#define PURGE_BUFFER_LENGTH (MAX_PATH * 2)
//
// This is also defined in cscui\dll\pch.h
// If you change it there you must change it here and vice versa.
//
#define FLAG_CSC_HINT_PIN_ADMIN FLAG_CSC_HINT_PIN_SYSTEM
//
// Purge confirmation dialog.
// The user can set which files are purged from the cache.
//
class CConfirmPurgeDialog { public: CConfirmPurgeDialog(void) : m_hInstance(NULL), m_hwnd(NULL), m_hwndLV(NULL), m_pSel(NULL) { }
~CConfirmPurgeDialog(void) { if (NULL != m_hwnd) DestroyWindow(m_hwnd); }
int Run(HINSTANCE hInstance, HWND hwndParent, CCachePurgerSel *pSel);
private: HINSTANCE m_hInstance; HWND m_hwnd; HWND m_hwndLV; CCachePurgerSel *m_pSel; // Ptr to destination for selection info.
static INT_PTR CALLBACK DlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); void OnInitDialog(HWND hwnd); void OnDestroy(void); void OnOk(void); void OnSettingChange(UINT uMsg, WPARAM wParam, LPARAM lParam); LPARAM LVGetItemData(HWND hwndLV, int i); int LVAddItem(HWND hwndLV, LPCTSTR pszItem, LPARAM lParam); };
inline ULONGLONG MakeULongLong(DWORD dwLow, DWORD dwHigh) { return ((ULONGLONG)(((DWORD)(dwLow)) | ((LONGLONG)((DWORD)(dwHigh))) << 32)); }
inline bool IsDirty(const CscFindData& cfd) { return 0 != (FLAG_CSCUI_COPY_STATUS_LOCALLY_DIRTY & cfd.dwStatus); }
inline bool IsSuperHidden(const CscFindData& cfd) { const DWORD dwSuperHidden = (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN); return (cfd.fd.dwFileAttributes & dwSuperHidden) == dwSuperHidden; }
inline bool OthersHaveAccess(const CscFindData& cfd) { return CscAccessOther(cfd.dwStatus); }
CCachePurger::CCachePurger( const CCachePurgerSel& sel, LPFNPURGECALLBACK pfnCbk, LPVOID pvCbkData ) : m_ullBytesToScan(0), m_ullBytesScanned(0), m_ullBytesToDelete(0), m_ullBytesDeleted(0), m_ullFileBytes(0), m_dwPhase(0), m_cFilesToScan(0), m_cFilesScanned(0), m_cFilesToDelete(0), m_cFilesDeleted(0), m_iFile(0), m_dwFileAttributes(0), m_dwResult(0), m_hgcPurgeInProgress(NULL), m_pszFile(NULL), m_pvCbkData(pvCbkData), m_bWillDelete(FALSE), m_pfnCbk(pfnCbk), m_bIsValid(true), m_sel(sel), m_bDelPinned(0 != (PURGE_FLAG_PINNED & sel.Flags())), m_bDelUnpinned(0 != (PURGE_FLAG_UNPINNED & sel.Flags())), m_bIgnoreAccess(0 != (PURGE_IGNORE_ACCESS & sel.Flags())), m_bUserIsAnAdmin(boolify(IsCurrentUserAnAdminMember())) { //
// Let the world know we're purging files from the cache.
// In particular, our overlay handler in cscui\dll\shellex.cpp needs
// to disable it's auto-pin code whenever we're purging. If we didn't
// do this AND the source folder is open during the purge, we get a
// nasty race condition between our shell notifications for purging
// and the overlay handler's auto-pin code. We'll delete a file
// and send a notification. The shell updates the icon overlay.
// Our handler sees that the parent folder is pinned so it re-pins
// the purged file which restores the file in the folder. This ends
// up resulting in a very nasty infinite loop. Those interested
// should call IsPurgeInProgress() to determine if the purge is
// in progress (see cscui\dll\util.cpp). [brianau - 11/01/99]
//
m_hgcPurgeInProgress = SHGlobalCounterCreateNamed(c_szPurgeInProgCounter, 0); if (m_hgcPurgeInProgress) { SHGlobalCounterIncrement(m_hgcPurgeInProgress); } }
CCachePurger::~CCachePurger( void ) { if (NULL != m_hgcPurgeInProgress) { SHGlobalCounterDecrement(m_hgcPurgeInProgress); SHGlobalCounterDestroy(m_hgcPurgeInProgress); } }
//
// Deletes a directory and all it's contents according to the PURGE_FLAG_XXXXX flag
// bits set by the caller of PurgeCache().
// This function recursively traverses the cache file hierarchy in a post-order fashion.
// Directory nodes are deleted after all children have been deleted.
// If the caller of PurgeCache provided a callback function, it is called
// after each deletion. If the callback function returns FALSE, the traversal
// operation is terminated.
//
// pstrPath - Address of path string containing the path of the directory to be
// deleted. This object is used to contain the working path throughout
// the tree traversal.
//
// dwPhase - PURGE_PHASE_SCAN - Scanning for file totals.
// PURGE_PHASE_DELETE - Deleting files.
//
// bShareIsOffline - The share is offline.
//
// Returns: true = Continue traversal.
// false = User cancelled via callback. Terminate traversal.
//
//
bool CCachePurger::ProcessDirectory( LPTSTR pszPath, DWORD dwPhase, bool bShareIsOffline ) { bool bContinue = true;
//
// We are working with a buffer longer than MAX_PATH, so we can't use
// the Path functions. However, we can make a couple of simplifying
// assumptions about the path we're working with.
//
int cchPath = lstrlen(pszPath); TraceAssert(PathIsUNC(pszPath)); if (cchPath+1 >= PURGE_BUFFER_LENGTH) { // Not enough buffer space to do anything here, but continue anyway
return true; }
CscFindData cfd; CCscFindHandle hFind(CacheFindFirst(pszPath, m_sel.UserSid(), &cfd)); if (hFind.IsValid()) { // Append a backslash
TraceAssert(cchPath > 2 && pszPath[cchPath-1] != TEXT('\\')); pszPath[cchPath++] = TEXT('\\'); pszPath[cchPath] = TEXT('\0');
do { //
// Create full path to this file/folder.
//
if (SUCCEEDED(StringCchCopy(pszPath + cchPath, PURGE_BUFFER_LENGTH - cchPath, cfd.fd.cFileName))) { bool bIsDirectory = (0 != (FILE_ATTRIBUTE_DIRECTORY & cfd.fd.dwFileAttributes)); if (bIsDirectory) { //
// It's a directory. Recursively delete it's contents.
//
bContinue = ProcessDirectory(pszPath, dwPhase, bShareIsOffline); } if (bContinue) { bool bPinned = (0 != ((FLAG_CSC_HINT_PIN_USER | FLAG_CSC_HINT_PIN_ADMIN) & cfd.dwHintFlags)); //
// The decision to delete a file has several criteria. I've tried to break this
// up using inlines and member variables to make it more understandable and minimize
// maintenance bugs.
// The logic for deletion is this:
//
// bDelete = false;
// If (pinned AND deleting pinned) OR (not pinned and deleting unpinned) then
// If super_hidden then
// bDelete = true;
// else
// If (not locally dirty) then
// If (ignore access) then
// bDelete = true;
// else
// If (user is an admin) then
// bDelete = true;
// else
// if (others have NO access) then
// bDelete = true;
// endif
// endif
// endif
// endif
// endif
// endif
bool bDelete = ((bPinned && m_bDelPinned) || (!bPinned && m_bDelUnpinned)) && (IsSuperHidden(cfd) || (!IsDirty(cfd) && (m_bIgnoreAccess || (m_bUserIsAnAdmin || !OthersHaveAccess(cfd)))));
m_pszFile = pszPath; m_dwFileAttributes = cfd.fd.dwFileAttributes; m_ullFileBytes = MakeULongLong(cfd.fd.nFileSizeLow, cfd.fd.nFileSizeHigh); m_bWillDelete = bDelete;
if (PURGE_PHASE_SCAN == dwPhase) { if (!bIsDirectory && m_pfnCbk) { //
// Exclude directories from the file and byte counts.
//
if (bDelete) { m_cFilesToDelete++; m_ullBytesToDelete += m_ullFileBytes; } m_cFilesScanned++; m_ullBytesScanned += m_ullFileBytes;
m_dwResult = ERROR_SUCCESS; bContinue = boolify((*m_pfnCbk)(this)); m_iFile++; } } else if (PURGE_PHASE_DELETE == dwPhase && bDelete) { LONG lShellEvent = SHCNE_UPDATEITEM;
m_dwResult = CscDelete(pszPath); if (ERROR_SUCCESS == m_dwResult) { if (!bIsDirectory) { m_cFilesDeleted++; m_ullBytesDeleted += m_ullFileBytes; }
if (bShareIsOffline) { lShellEvent = bIsDirectory ? SHCNE_RMDIR : SHCNE_DELETE; } } else { if (ERROR_ACCESS_DENIED == m_dwResult) { //
// This is a little weird. CscDelete
// returns ERROR_ACCESS_DENIED if there's
// a handle open on the file. Set the
// code to ERROR_BUSY so we know to handle
// this as a special case.
//
m_dwResult = ERROR_BUSY; }
//
// NTRAID#NTBUG9-213486-2001/01/29-jeffreys
//
// CscDelete failed. Make sure it's unpinned, so
// it doesn't get the icon overlay anymore.
//
// This can happen if there is a handle open on a file,
// or if there is a view open on a directory, in which
// case there is a change notification handle open. It
// will also happen (later) for any parent directories,
// since they are not empty.
//
CSCUnpinFile(pszPath, FLAG_CSC_HINT_PIN_USER | FLAG_CSC_HINT_PIN_INHERIT_USER, NULL, NULL, NULL); }
ShellChangeNotify(pszPath, &cfd.fd, FALSE, lShellEvent);
if (!bIsDirectory && m_pfnCbk) { bContinue = boolify((*m_pfnCbk)(this)); m_iFile++; } } } }
// Remove the file spec
pszPath[cchPath] = TEXT('\0'); } while(bContinue && CacheFindNext(hFind, &cfd));
// Remove the trailing backslash
pszPath[cchPath-1] = TEXT('\0'); }
return bContinue; }
//
// Public function for purging cache contents.
//
HRESULT CCachePurger::Process( DWORD dwPhase ) { HRESULT hr = NOERROR;
if (!m_bIsValid) return E_OUTOFMEMORY; // Failed ctor.
m_dwPhase = dwPhase;
if (PURGE_PHASE_SCAN == dwPhase) { //
// At start of scanning phase, get the max bytes and file count
// from the CSC database. This will let us provide meaningful
// progress data during the scanning phase.
//
ULARGE_INTEGER ulTotalBytes = {0, 0}; ULARGE_INTEGER ulUsedBytes = {0, 0}; DWORD dwTotalFiles = 0; DWORD dwTotalDirs = 0; TCHAR szVolume[MAX_PATH]; CSCGetSpaceUsage(szVolume, ARRAYSIZE(szVolume), &ulTotalBytes.HighPart, &ulTotalBytes.LowPart, &ulUsedBytes.HighPart, &ulUsedBytes.LowPart, &dwTotalFiles, &dwTotalDirs);
m_cFilesToScan = dwTotalFiles + dwTotalDirs; m_ullBytesToScan = ulTotalBytes.QuadPart; m_ullBytesToDelete = 0; m_ullBytesDeleted = 0; m_ullBytesScanned = 0; m_ullFileBytes = 0; m_cFilesToDelete = 0; m_cFilesDeleted = 0; m_cFilesScanned = 0; m_dwFileAttributes = 0; m_dwResult = 0; m_pszFile = NULL; m_bWillDelete = false; } m_iFile = 0; // Reset this for each phase.
bool bContinue = true; CscFindData cfd; TCHAR szPath[PURGE_BUFFER_LENGTH]; if (0 < m_sel.ShareCount()) { //
// Delete 1+ (but not all) shares.
//
for (int i = 0; i < m_sel.ShareCount(); i++) { if (SUCCEEDED(StringCchCopy(szPath, ARRAYSIZE(szPath), m_sel.ShareName(i)))) { cfd.dwStatus = 0; CSCQueryFileStatus(szPath, &cfd.dwStatus, NULL, NULL);
bContinue = ProcessDirectory(szPath, dwPhase, boolify(FLAG_CSC_SHARE_STATUS_DISCONNECTED_OP & cfd.dwStatus)); if (PURGE_PHASE_DELETE == dwPhase) { LONG lShellEvent = SHCNE_UPDATEITEM;
if (ERROR_SUCCESS == CscDelete(szPath)) { if (FLAG_CSC_SHARE_STATUS_DISCONNECTED_OP & cfd.dwStatus) { lShellEvent = SHCNE_RMDIR; } } else { //
// NTRAID#NTBUG9-213486-2001/01/29-jeffreys
//
// If unable to delete, make sure it's unpinned.
//
CSCUnpinFile(szPath, FLAG_CSC_HINT_PIN_USER | FLAG_CSC_HINT_PIN_INHERIT_USER, NULL, NULL, NULL); }
ZeroMemory(&cfd.fd, sizeof(cfd.fd)); cfd.fd.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY; StringCchCopy(cfd.fd.cFileName, ARRAYSIZE(cfd.fd.cFileName), szPath); ShellChangeNotify(szPath, &cfd.fd, FALSE, lShellEvent); } } }
if (PURGE_PHASE_DELETE == dwPhase) { //
// On the DELETE phase always try to remove any empty
// share entries from the database. CSCDelete will
// harmlessly fail if the share entry cannot be deleted.
//
CCscFindHandle hFind(CacheFindFirst(NULL, m_sel.UserSid(), &cfd)); if (hFind.IsValid()) { do { // Don't unpin on failure here. The user wants to keep these.
if (ERROR_SUCCESS == CscDelete(cfd.fd.cFileName)) { ShellChangeNotify(cfd.fd.cFileName, &cfd.fd, FALSE, (FLAG_CSC_SHARE_STATUS_DISCONNECTED_OP & cfd.dwStatus) ? SHCNE_RMDIR : SHCNE_UPDATEITEM); } } while(CacheFindNext(hFind, &cfd)); } } } else { //
// Delete all shares.
//
CCscFindHandle hFind(CacheFindFirst(NULL, m_sel.UserSid(), &cfd)); if (hFind.IsValid()) { do { if (SUCCEEDED(StringCchCopy(szPath, ARRAYSIZE(szPath), cfd.fd.cFileName))) { bContinue = ProcessDirectory(szPath, dwPhase, boolify(FLAG_CSC_SHARE_STATUS_DISCONNECTED_OP & cfd.dwStatus)); if (PURGE_PHASE_DELETE == dwPhase) { LONG lShellEvent = SHCNE_UPDATEITEM;
if (ERROR_SUCCESS == CscDelete(szPath)) { if (FLAG_CSC_SHARE_STATUS_DISCONNECTED_OP & cfd.dwStatus) { lShellEvent = SHCNE_RMDIR; } } else { //
// NTRAID#NTBUG9-213486-2001/01/29-jeffreys
//
// If unable to delete, make sure it's unpinned.
//
CSCUnpinFile(szPath, FLAG_CSC_HINT_PIN_USER | FLAG_CSC_HINT_PIN_INHERIT_USER, NULL, NULL, NULL); }
ShellChangeNotify(szPath, &cfd.fd, FALSE, lShellEvent); } } } while(bContinue && CacheFindNext(hFind, &cfd)); } }
//
// Flush any pending notifications
//
ShellChangeNotify(NULL, TRUE);
return hr; }
//
// Displays a modal dialog to get cache purging confirmation from the
// user. Let's user indicate if they want to purge only temp files
// from the cache or both temp and pinned.
//
// Returns PURGE_FLAG_XXXX flags and a list of share names
// in the CCachePurgerSel object.
//
void CCachePurger::AskUserWhatToPurge( HWND hwndParent, CCachePurgerSel *pSel ) { CConfirmPurgeDialog dlg; dlg.Run(GetModuleHandle(TEXT("cscui.dll")), hwndParent, pSel); }
//
// Returns:
// 0 = User cancelled.
// 1 = User pressed OK.
//
// Returns PURGE_FLAG_XXXX flags and a list of share names
// in the CCachePurgerSel object.
//
int CConfirmPurgeDialog::Run( HINSTANCE hInstance, HWND hwndParent, CCachePurgerSel *pSel // We don't "own" this. Merely a WRITE reference.
) { TraceAssert(NULL != hInstance); TraceAssert(NULL != hwndParent); TraceAssert(NULL != pSel); m_hInstance = hInstance;
m_pSel = pSel;
int iResult = (int)DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_CONFIRM_PURGE), hwndParent, DlgProc, (LPARAM)this); if (-1 == iResult) { Trace((TEXT("Error %d creating delete confirmation dialog"), GetLastError())); } return iResult; }
INT_PTR CALLBACK CConfirmPurgeDialog::DlgProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) { CConfirmPurgeDialog *pThis = reinterpret_cast<CConfirmPurgeDialog *>(GetWindowLongPtr(hwnd, DWLP_USER));
switch(message) { case WM_INITDIALOG: SetWindowLongPtr(hwnd, DWLP_USER, (INT_PTR)lParam); pThis = reinterpret_cast<CConfirmPurgeDialog *>(lParam); TraceAssert(NULL != pThis); pThis->OnInitDialog(hwnd); return TRUE;
case WM_ENDSESSION: EndDialog(hwnd, IDNO); return FALSE;
case WM_COMMAND: { int iResult = 0; // Assume [Cancel]
switch(LOWORD(wParam)) { case IDOK: iResult = 1; pThis->OnOk(); //
// Fall through...
//
case IDCANCEL: EndDialog(hwnd, iResult); return FALSE; } } break;
case WM_SETTINGCHANGE: case WM_SYSCOLORCHANGE: pThis->OnSettingChange(message, wParam, lParam); break; case WM_DESTROY: pThis->OnDestroy(); pThis->m_hwnd = NULL; return FALSE;
default: break; } return FALSE; }
void CConfirmPurgeDialog::OnInitDialog( HWND hwnd ) { TraceAssert(NULL != hwnd);
RECT rc; m_hwnd = hwnd; m_hwndLV = GetDlgItem(hwnd, IDC_LIST_PURGE); CheckDlgButton(hwnd, IDC_RBN_CONFIRMPURGE_UNPINNED, BST_CHECKED); CheckDlgButton(hwnd, IDC_RBN_CONFIRMPURGE_ALL, BST_UNCHECKED);
//
// Turn on checkboxes in the listview.
//
ListView_SetExtendedListViewStyleEx(m_hwndLV, LVS_EX_CHECKBOXES, LVS_EX_CHECKBOXES); //
// Add the single column to the listview.
//
GetClientRect(m_hwndLV, &rc);
LV_COLUMN col = { LVCF_FMT | LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM, LVCFMT_LEFT, rc.right - rc.left, TEXT(""), 0, 0 };
ListView_InsertColumn(m_hwndLV, 0, &col);
//
// Create the image list for the listview.
//
HIMAGELIST hSmallImages = ImageList_Create(16, 16, ILC_MASK, 1, 0); if (NULL != hSmallImages) { HICON hIcon = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_SHARE)); if (NULL != hIcon) { ImageList_AddIcon(hSmallImages, hIcon); } ListView_SetImageList(m_hwndLV, hSmallImages, LVSIL_SMALL); }
//
// Fill "shares" list with share names.
//
CscFindData cfd; CCscFindHandle hFind(CacheFindFirst(NULL, &cfd)); if (hFind.IsValid()) { LPITEMIDLIST pidl = NULL; SHFILEINFO sfi; CSCSHARESTATS ss; CSCGETSTATSINFO si = { SSEF_NONE, SSUF_TOTAL, true, false }; do { _GetShareStatisticsForUser(cfd.fd.cFileName, &si, &ss); if (0 < ss.cTotal) { ZeroMemory(&sfi, sizeof(sfi)); if (SUCCEEDED(SHSimpleIDListFromFindData(cfd.fd.cFileName, &cfd.fd, &pidl))) { SHGetFileInfo((LPCTSTR)pidl, 0, &sfi, sizeof(sfi), SHGFI_PIDL | SHGFI_DISPLAYNAME); SHFree(pidl); } if (sfi.szDisplayName[0] != TEXT('\0')) { //
// Each item's lParam contains a pointer to the
// UNC path allocated on the heap. Must be deleted
// in OnDestroy().
//
LPTSTR pszFileName = StrDup(cfd.fd.cFileName); if (NULL != pszFileName) { int iItem = LVAddItem(m_hwndLV, sfi.szDisplayName, (LPARAM)pszFileName); if (0 <= iItem) { //
// All items are initially checked.
//
ListView_SetCheckState(m_hwndLV, iItem, TRUE); } else { LocalFree(pszFileName); } } } } } while(CacheFindNext(hFind, &cfd)); } if (0 == ListView_GetItemCount(m_hwndLV)) { //
// No items are in the listview.
// Disable all of the controls, hide the "OK" button and
// change the "Cancel" button to "Close".
//
const UINT rgidCtls[] = { IDC_TXT_CONFIRMPURGE3, IDC_RBN_CONFIRMPURGE_UNPINNED, IDC_RBN_CONFIRMPURGE_ALL, IDC_LIST_PURGE, IDOK}; ShowWindow(GetDlgItem(m_hwnd, IDOK), SW_HIDE);
for (int i = 0; i < ARRAYSIZE(rgidCtls); i++) { EnableWindow(GetDlgItem(m_hwnd, rgidCtls[i]), FALSE); }
TCHAR szText[MAX_PATH]; LoadString(m_hInstance, IDS_BTN_TITLE_CLOSE, szText, ARRAYSIZE(szText)); SetWindowText(GetDlgItem(m_hwnd, IDCANCEL), szText); //
// Replace the listview's caption with something like "There are
// no offline files to delete".
//
LoadString(m_hInstance, IDS_TXT_NO_FILES_TO_DELETE, szText, ARRAYSIZE(szText)); SetWindowText(GetDlgItem(m_hwnd, IDC_TXT_CONFIRMPURGE2), szText); //
// Uncheck both radio buttons.
//
CheckDlgButton(m_hwnd, IDC_RBN_CONFIRMPURGE_UNPINNED, BST_UNCHECKED); CheckDlgButton(m_hwnd, IDC_RBN_CONFIRMPURGE_ALL, BST_UNCHECKED); } }
LPARAM CConfirmPurgeDialog::LVGetItemData( HWND hwndLV, int i ) { LVITEM item; item.mask = LVIF_PARAM; item.iItem = i; item.iSubItem = 0;
if (ListView_GetItem(hwndLV, &item)) { return item.lParam; } return 0; }
int CConfirmPurgeDialog::LVAddItem( HWND hwndLV, LPCTSTR pszItem, LPARAM lParam ) { LVITEM item; item.mask = LVIF_TEXT | LVIF_PARAM; item.pszText = (LPTSTR)pszItem; item.iItem = ListView_GetItemCount(hwndLV); item.iSubItem = 0; item.lParam = lParam;
return ListView_InsertItem(hwndLV, &item); }
void CConfirmPurgeDialog::OnOk( void ) { const int cShares = ListView_GetItemCount(m_hwndLV); for (int i = 0; i < cShares; i++) { if (0 != ListView_GetCheckState(m_hwndLV, i)) { m_pSel->AddShareName((LPCTSTR)LVGetItemData(m_hwndLV, i)); } } if (0 < m_pSel->ShareCount()) { m_pSel->SetFlags((BST_CHECKED == IsDlgButtonChecked(m_hwnd, IDC_RBN_CONFIRMPURGE_UNPINNED)) ? PURGE_FLAG_UNPINNED : PURGE_FLAG_ALL); } else { m_pSel->SetFlags(PURGE_FLAG_NONE); } }
void CConfirmPurgeDialog::OnDestroy( void ) { if (NULL != m_hwndLV) { const int cShares = ListView_GetItemCount(m_hwndLV); for (int i = 0; i < cShares; i++) { LPTSTR psz = (LPTSTR)LVGetItemData(m_hwndLV, i); if (NULL != psz) { LocalFree(psz); } } } }
void CConfirmPurgeDialog::OnSettingChange( UINT uMsg, WPARAM wParam, LPARAM lParam ) { if (NULL != m_hwndLV) SendMessage(m_hwndLV, uMsg, wParam, lParam); }
CCachePurgerSel::~CCachePurgerSel( void ) { if (NULL != m_hdpaShares) { const int cShares = DPA_GetPtrCount(m_hdpaShares); for (int i = 0; i < cShares; i++) { LPTSTR psz = (LPTSTR)DPA_GetPtr(m_hdpaShares, i); if (NULL != psz) { LocalFree(psz); } } DPA_Destroy(m_hdpaShares); } if (NULL != m_psidUser) { LocalFree(m_psidUser); } }
BOOL CCachePurgerSel::SetUserSid( PSID psid ) { if (NULL != m_psidUser) { LocalFree(m_psidUser); m_psidUser = NULL; } if (NULL != psid && IsValidSid(psid)) { DWORD cbSid = GetLengthSid(psid); PSID psidNew = (PSID)LocalAlloc(LPTR, cbSid); if (NULL != psidNew) { if (!CopySid(cbSid, psidNew, psid)) { LocalFree(psidNew); psidNew = NULL; } m_psidUser = psidNew; } } return NULL != m_psidUser; }
BOOL CCachePurgerSel::AddShareName( LPCTSTR pszShare ) { //
// Be tolerant of a NULL pszShare pointer.
//
if (NULL != m_hdpaShares && NULL != pszShare) { LPTSTR pszCopy = StrDup(pszShare); if (NULL != pszCopy) { if (-1 != DPA_AppendPtr(m_hdpaShares, pszCopy)) { return true; } LocalFree(pszCopy); } } return false; }
typedef struct _RemoveFolderCBData { PFN_CSCUIRemoveFolderCallback pfnCB; LPARAM lParam; } RemoveFolderCBData, *PRemoveFolderCBData;
BOOL CALLBACK _RemoveFolderCallback(CCachePurger *pPurger) { PRemoveFolderCBData pcbdata = (PRemoveFolderCBData)pPurger->CallbackData(); if (pcbdata->pfnCB) return pcbdata->pfnCB(pPurger->FileName(), pcbdata->lParam); return TRUE; }
STDAPI CSCUIRemoveFolderFromCache(LPCWSTR pszFolder, DWORD /*dwReserved*/, // can use for flags
PFN_CSCUIRemoveFolderCallback pfnCB, LPARAM lParam) { RemoveFolderCBData cbdata = { pfnCB, lParam };
CCachePurgerSel sel; sel.SetFlags(PURGE_FLAG_ALL | PURGE_IGNORE_ACCESS); sel.AddShareName(pszFolder);
CCachePurger purger(sel, _RemoveFolderCallback, &cbdata);
return purger.Delete(); }
|