982 lines
29 KiB
C++
982 lines
29 KiB
C++
//+-------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
//
|
|
// Copyright (C) Microsoft Corporation, 1997 - 1999
|
|
//
|
|
// File: purge.cpp
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
#include "pch.h"
|
|
#pragma hdrstop
|
|
|
|
#include <cscuiext.h> // CSCUIRemoveFolderFromCache
|
|
#include "purge.h"
|
|
#include "cscutils.h"
|
|
#include "msgbox.h"
|
|
#include "resource.h"
|
|
#include "security.h"
|
|
#include "util.h"
|
|
#include "strings.h"
|
|
|
|
//
|
|
// 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);
|
|
BOOL LVSetItemData(HWND hwndLV, int i, LPARAM lParam);
|
|
LPARAM LVGetItemData(HWND hwndLV, int i);
|
|
int LVAddItem(HWND hwndLV, LPCTSTR pszItem);
|
|
};
|
|
|
|
|
|
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_hmutexPurgeInProg(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_hmutexPurgeInProg = CreateMutex(NULL, TRUE, c_szPurgeInProgMutex);
|
|
}
|
|
|
|
CCachePurger::~CCachePurger(
|
|
void
|
|
)
|
|
{
|
|
if (NULL != m_hmutexPurgeInProg)
|
|
{
|
|
ReleaseMutex(m_hmutexPurgeInProg);
|
|
CloseHandle(m_hmutexPurgeInProg);
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// 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;
|
|
CscFindData cfd;
|
|
|
|
CscFindHandle hFind(CacheFindFirst(pszPath, m_sel.UserSid(), &cfd));
|
|
if (hFind.IsValidHandle())
|
|
{
|
|
do
|
|
{
|
|
bool bIsDirectory = (0 != (FILE_ATTRIBUTE_DIRECTORY & cfd.fd.dwFileAttributes));
|
|
//
|
|
// Create full path to this file/folder.
|
|
//
|
|
::PathAppend(pszPath, cfd.fd.cFileName);
|
|
|
|
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++;
|
|
}
|
|
}
|
|
}
|
|
::PathRemoveFileSpec(pszPath);
|
|
}
|
|
while(bContinue && CacheFindNext(hFind, &cfd));
|
|
}
|
|
::PathRemoveBackslash(pszPath);
|
|
|
|
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[MAX_PATH * 2];
|
|
if (0 < m_sel.ShareCount())
|
|
{
|
|
//
|
|
// Delete 1+ (but not all) shares.
|
|
//
|
|
for (int i = 0; i < m_sel.ShareCount(); i++)
|
|
{
|
|
lstrcpyn(szPath, m_sel.ShareName(i), ARRAYSIZE(szPath));
|
|
|
|
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;
|
|
lstrcpyn(cfd.fd.cFileName, szPath, ARRAYSIZE(cfd.fd.cFileName));
|
|
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.
|
|
//
|
|
CscFindHandle hFind(CacheFindFirst(NULL, m_sel.UserSid(), &cfd));
|
|
if (hFind.IsValidHandle())
|
|
{
|
|
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.
|
|
//
|
|
CscFindHandle hFind(CacheFindFirst(NULL, m_sel.UserSid(), &cfd));
|
|
if (hFind.IsValidHandle())
|
|
{
|
|
do
|
|
{
|
|
lstrcpyn(szPath, cfd.fd.cFileName, ARRAYSIZE(szPath));
|
|
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;
|
|
CscFindHandle hFind(CacheFindFirst(NULL, &cfd));
|
|
if (hFind.IsValidHandle())
|
|
{
|
|
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);
|
|
}
|
|
int iItem = LVAddItem(m_hwndLV, sfi.szDisplayName);
|
|
if (0 <= iItem)
|
|
{
|
|
//
|
|
// All items are initially checked.
|
|
//
|
|
ListView_SetCheckState(m_hwndLV, iItem, TRUE);
|
|
//
|
|
// 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)
|
|
{
|
|
if (!LVSetItemData(m_hwndLV, iItem, (LPARAM)pszFileName))
|
|
{
|
|
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;
|
|
}
|
|
|
|
|
|
BOOL
|
|
CConfirmPurgeDialog::LVSetItemData(
|
|
HWND hwndLV,
|
|
int i,
|
|
LPARAM lParam
|
|
)
|
|
{
|
|
LVITEM item;
|
|
item.mask = LVIF_PARAM;
|
|
item.iItem = i;
|
|
item.iSubItem = 0;
|
|
item.lParam = lParam;
|
|
|
|
return ListView_SetItem(hwndLV, &item);
|
|
}
|
|
|
|
|
|
int
|
|
CConfirmPurgeDialog::LVAddItem(
|
|
HWND hwndLV,
|
|
LPCTSTR pszItem
|
|
)
|
|
{
|
|
LVITEM item;
|
|
item.mask = LVIF_TEXT;
|
|
item.pszText = (LPTSTR)pszItem;
|
|
item.iSubItem = 0;
|
|
item.iItem = ListView_GetItemCount(hwndLV);
|
|
|
|
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();
|
|
}
|