1556 lines
51 KiB
C++
1556 lines
51 KiB
C++
|
//
|
||
|
// CleanupWiz.cpp
|
||
|
//
|
||
|
#include "CleanupWiz.h"
|
||
|
#include "resource.h"
|
||
|
#include "priv.h"
|
||
|
#include "dblnul.h"
|
||
|
|
||
|
#include <windowsx.h> // for SetWindowFont
|
||
|
#include <varutil.h>
|
||
|
#include <commctrl.h>
|
||
|
#include <shlwapi.h>
|
||
|
#include <shguidp.h>
|
||
|
#include <ieguidp.h>
|
||
|
|
||
|
|
||
|
// UEM stuff: including this source file is the
|
||
|
// recommended way of using it in your project
|
||
|
// (see comments in the file itself for the reason)
|
||
|
#include "..\inc\uassist.cpp"
|
||
|
|
||
|
#define MAX_GUID_STRING_LEN 39
|
||
|
|
||
|
////////////////////////////////////////////
|
||
|
//
|
||
|
// Globals, constants, externs etc...
|
||
|
//
|
||
|
////////////////////////////////////////////
|
||
|
|
||
|
extern HINSTANCE g_hInst;
|
||
|
STDMETHODIMP GetItemCLSID(IShellFolder2 *psf, LPCITEMIDLIST pidlLast, CLSID *pclsid);
|
||
|
|
||
|
//
|
||
|
// number of items to grow dsa by
|
||
|
//
|
||
|
const int c_GROWBYSIZE = 4;
|
||
|
|
||
|
//
|
||
|
// number of pages in the wizard
|
||
|
//
|
||
|
const int c_NUM_PAGES = 3;
|
||
|
|
||
|
//
|
||
|
// dialog prompt text length
|
||
|
//
|
||
|
const int c_MAX_PROMPT_TEXT = 1024;
|
||
|
const int c_MAX_HEADER_LEN = 64;
|
||
|
const int c_MAX_DATE_LEN = 40;
|
||
|
|
||
|
//
|
||
|
// file modified more than 60 days back is candidate for cleanup
|
||
|
// this value can be overriden by policy
|
||
|
//
|
||
|
const int c_NUMDAYSTODECAY = 60;
|
||
|
|
||
|
//
|
||
|
// Needed for hiding regitems
|
||
|
//
|
||
|
#define DEFINE_SCID(name, fmtid, pid) const SHCOLUMNID name = { fmtid, pid }
|
||
|
DEFINE_SCID(SCID_DESCRIPTIONID, PSGUID_SHELLDETAILS, PID_DESCRIPTIONID);
|
||
|
|
||
|
//
|
||
|
// pointer to member function typedef
|
||
|
//
|
||
|
typedef INT_PTR (STDMETHODCALLTYPE CCleanupWiz::* PCFC_DlgProcFn)(HWND, UINT, WPARAM, LPARAM);
|
||
|
|
||
|
//
|
||
|
// struct for helping us manage our dialog procs
|
||
|
//
|
||
|
typedef struct
|
||
|
{
|
||
|
CCleanupWiz * pcfc;
|
||
|
PCFC_DlgProcFn pfnDlgProc;
|
||
|
} DLGPROCINFO, *PDLGPROCINFO;
|
||
|
|
||
|
//
|
||
|
// enum for columns
|
||
|
//
|
||
|
typedef enum eColIndex
|
||
|
{
|
||
|
FC_COL_SHORTCUT,
|
||
|
FC_COL_DATE
|
||
|
};
|
||
|
|
||
|
|
||
|
//////////////////////////////////////////////////////
|
||
|
//
|
||
|
// iDays can be negative or positive, indicating time in the past or future
|
||
|
//
|
||
|
//
|
||
|
#define FTsPerDayOver1000 (10000*60*60*24) // we've got (1000 x 10,000) 100ns intervals per second
|
||
|
|
||
|
STDAPI_(void) GetFileTimeNDaysFromGivenTime(const FILETIME *pftGiven, FILETIME * pftReturn, int iDays)
|
||
|
{
|
||
|
__int64 i64 = *((__int64 *) pftGiven);
|
||
|
i64 += Int32x32To64(iDays*1000,FTsPerDayOver1000);
|
||
|
|
||
|
*pftReturn = *((FILETIME *) &i64);
|
||
|
}
|
||
|
|
||
|
STDAPI_(void) GetFileTimeNDaysFromCurrentTime(FILETIME *pf, int iDays)
|
||
|
{
|
||
|
SYSTEMTIME st;
|
||
|
FILETIME ftNow;
|
||
|
|
||
|
GetLocalTime(&st);
|
||
|
SystemTimeToFileTime(&st, &ftNow);
|
||
|
|
||
|
GetFileTimeNDaysFromGivenTime(&ftNow, pf, iDays);
|
||
|
}
|
||
|
|
||
|
/////////////////////////////////////////////////
|
||
|
//
|
||
|
//
|
||
|
// The CCleanupWiz class implementation
|
||
|
//
|
||
|
//
|
||
|
/////////////////////////////////////////////////
|
||
|
|
||
|
CCleanupWiz::CCleanupWiz(): _psf(NULL),
|
||
|
_hdsaItems(NULL),
|
||
|
_hTitleFont(NULL),
|
||
|
_iDeltaDays(0),
|
||
|
_dwCleanMode(CLEANUP_MODE_NORMAL)
|
||
|
{
|
||
|
INITCOMMONCONTROLSEX icce;
|
||
|
icce.dwSize = sizeof(icce);
|
||
|
icce.dwICC = ICC_LISTVIEW_CLASSES;
|
||
|
_bInited = InitCommonControlsEx(&icce);
|
||
|
|
||
|
// get the desktop folder
|
||
|
_bInited = _bInited && SUCCEEDED(SHGetDesktopFolder(&_psf));
|
||
|
};
|
||
|
|
||
|
CCleanupWiz::~CCleanupWiz()
|
||
|
{
|
||
|
_CleanUpDSA();
|
||
|
if (_psf)
|
||
|
{
|
||
|
_psf->Release();
|
||
|
_psf = NULL;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
//
|
||
|
// Runs the folder cleaning operation on the desktop folder.
|
||
|
//
|
||
|
//
|
||
|
// bCleanAll
|
||
|
//
|
||
|
// TRUE: show all the items in the desktop, this is done
|
||
|
// only when the user runs the wizard from the right click
|
||
|
// menu or display applet. in this case we don't need to show the
|
||
|
// notification balloon tip.
|
||
|
//
|
||
|
// FALSE: show only those items which are candidates for cleanup.
|
||
|
// this is done when we run the wizard through explorer (every 60 days,
|
||
|
// we check if it's time on login and every 24 hours thereafter)
|
||
|
// so we need to show the balloon tip and proceeed only if the user
|
||
|
// asks us to.
|
||
|
//
|
||
|
|
||
|
STDMETHODIMP CCleanupWiz::Run(DWORD dwCleanMode, HWND hwndParent)
|
||
|
{
|
||
|
HRESULT hr = E_FAIL;
|
||
|
|
||
|
// early out
|
||
|
if (!_bInited)
|
||
|
{
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
_dwCleanMode = dwCleanMode;
|
||
|
|
||
|
if (CLEANUP_MODE_SILENT == _dwCleanMode)
|
||
|
{
|
||
|
hr = _RunSilent();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
_iDeltaDays = GetNumDaysBetweenCleanup();
|
||
|
|
||
|
if (_iDeltaDays < 0)
|
||
|
{
|
||
|
_iDeltaDays = c_NUMDAYSTODECAY; //initial default value
|
||
|
}
|
||
|
|
||
|
LoadString(g_hInst, IDS_ARCHIVEFOLDER, _szFolderName, MAX_PATH);
|
||
|
|
||
|
// init the common control classes we need
|
||
|
hr = _LoadDesktopContents();
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = S_OK;
|
||
|
|
||
|
UINT cItems = DSA_GetItemCount(_hdsaItems);
|
||
|
if (CLEANUP_MODE_NORMAL == dwCleanMode)
|
||
|
{
|
||
|
if (cItems > 0) // if there are items, we want to notify and proceed only if the user wants us to.
|
||
|
{
|
||
|
hr = _ShowBalloonNotification();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hr = S_FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
if (S_OK == hr)
|
||
|
{
|
||
|
_cItemsOnDesktop = cItems;
|
||
|
hr = _InitializeAndLaunchWizard(hwndParent);
|
||
|
}
|
||
|
|
||
|
_LogUsage(); // set registry values to indicate the last run time
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Creates the property pages for the wizard and launches the wizard
|
||
|
//
|
||
|
//
|
||
|
STDMETHODIMP CCleanupWiz::_InitializeAndLaunchWizard(HWND hwndParent)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
|
||
|
DLGPROCINFO adpi[c_NUM_PAGES];
|
||
|
|
||
|
HPROPSHEETPAGE ahpsp[c_NUM_PAGES];
|
||
|
PROPSHEETPAGE psp = {0};
|
||
|
|
||
|
|
||
|
if (!_hTitleFont)
|
||
|
{
|
||
|
NONCLIENTMETRICS ncm = {0};
|
||
|
ncm.cbSize = sizeof(ncm);
|
||
|
SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0);
|
||
|
LOGFONT TitleLogFont = ncm.lfMessageFont;
|
||
|
TitleLogFont.lfWeight = FW_BOLD;
|
||
|
TCHAR szFont[128];
|
||
|
LoadString(g_hInst, IDS_TITLELOGFONT, szFont, ARRAYSIZE(szFont));
|
||
|
lstrcpy(TitleLogFont.lfFaceName, szFont);
|
||
|
|
||
|
HDC hdc = GetDC(NULL);
|
||
|
INT FontSize = 12;
|
||
|
TitleLogFont.lfHeight = 0 - GetDeviceCaps(hdc, LOGPIXELSY) * FontSize / 72;
|
||
|
_hTitleFont = CreateFontIndirect(&TitleLogFont);
|
||
|
ReleaseDC(NULL, hdc);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Intro Page
|
||
|
//
|
||
|
adpi[0].pcfc = this;
|
||
|
adpi[0].pfnDlgProc = &CCleanupWiz::_IntroPageDlgProc;
|
||
|
psp.dwSize = sizeof(psp);
|
||
|
psp.dwFlags = PSP_DEFAULT|PSP_HIDEHEADER;
|
||
|
psp.hInstance = g_hInst;
|
||
|
psp.lParam = (LPARAM) &adpi[0];
|
||
|
psp.pfnDlgProc = s_StubDlgProc;
|
||
|
psp.pszTemplate = MAKEINTRESOURCE(IDD_INTRO);
|
||
|
ahpsp[0] = CreatePropertySheetPage(&psp);
|
||
|
|
||
|
//
|
||
|
// Choose files page
|
||
|
//
|
||
|
adpi[1].pcfc = this;
|
||
|
adpi[1].pfnDlgProc = &CCleanupWiz::_ChooseFilesPageDlgProc;
|
||
|
psp.hInstance = g_hInst;
|
||
|
psp.dwFlags = PSP_DEFAULT|PSP_USEHEADERTITLE| PSP_USEHEADERSUBTITLE;
|
||
|
psp.lParam = (LPARAM) &adpi[1];
|
||
|
psp.pszHeaderTitle = MAKEINTRESOURCE(IDS_CHOOSEFILES);
|
||
|
psp.pszHeaderSubTitle = MAKEINTRESOURCE(IDS_CHOOSEFILES_INFO);
|
||
|
psp.pszTemplate = MAKEINTRESOURCE(IDD_CHOOSEFILES);
|
||
|
psp.pfnDlgProc = s_StubDlgProc;
|
||
|
ahpsp[1] = CreatePropertySheetPage(&psp);
|
||
|
|
||
|
//
|
||
|
// Completion Page
|
||
|
//
|
||
|
adpi[2].pcfc = this;
|
||
|
adpi[2].pfnDlgProc = &CCleanupWiz::_FinishPageDlgProc;
|
||
|
psp.dwFlags = PSP_DEFAULT|PSP_HIDEHEADER;
|
||
|
psp.hInstance = g_hInst;
|
||
|
psp.lParam = (LPARAM) &adpi[2];
|
||
|
psp.pfnDlgProc = s_StubDlgProc;
|
||
|
psp.pszTemplate = MAKEINTRESOURCE(IDD_FINISH);
|
||
|
ahpsp[2] = CreatePropertySheetPage(&psp);
|
||
|
|
||
|
//
|
||
|
// The wizard property sheet
|
||
|
//
|
||
|
PROPSHEETHEADER psh = {0};
|
||
|
|
||
|
psh.dwSize = sizeof(psh);
|
||
|
psh.hInstance = g_hInst;
|
||
|
psh.hwndParent = hwndParent;
|
||
|
psh.phpage = ahpsp;
|
||
|
psh.dwFlags = PSH_WIZARD97|PSH_WATERMARK|PSH_HEADER;
|
||
|
psh.pszbmWatermark = MAKEINTRESOURCE(IDB_WATERMARK);
|
||
|
psh.pszbmHeader = MAKEINTRESOURCE(IDB_LOGO);
|
||
|
psh.nStartPage = _cItemsOnDesktop ? 0 : c_NUM_PAGES - 1; // if there are no pages on desktop, start on final page
|
||
|
psh.nPages = c_NUM_PAGES;
|
||
|
|
||
|
PropertySheet(&psh);
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Pops up a balloon notification tip which asks the user
|
||
|
// if he wants to clean up the desktop.
|
||
|
//
|
||
|
// returns S_OK if user wants us to cleanup.
|
||
|
//
|
||
|
STDMETHODIMP CCleanupWiz::_ShowBalloonNotification()
|
||
|
{
|
||
|
IUserNotification *pun;
|
||
|
HRESULT hr = CoCreateInstance(CLSID_UserNotification, NULL, CLSCTX_ALL,
|
||
|
IID_PPV_ARG(IUserNotification, &pun));
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
TCHAR szTitle[64], szMsg[256]; // we leave enough room for localization bloat
|
||
|
|
||
|
LoadString(g_hInst, IDS_NOTIFICATION_TITLE, szTitle, ARRAYSIZE(szTitle));
|
||
|
LoadString(g_hInst, IDS_NOTIFICATION_TEXT, szMsg, ARRAYSIZE(szMsg));
|
||
|
|
||
|
pun->SetIconInfo(LoadIcon(g_hInst, MAKEINTRESOURCE(IDI_WIZ_ICON)), szTitle);
|
||
|
pun->SetBalloonInfo(szTitle, szMsg, NIIF_WARNING);
|
||
|
|
||
|
// try once, for 20 seconds
|
||
|
pun->SetBalloonRetry(20 * 1000, -1, 1);
|
||
|
|
||
|
// returns S_OK if user wants to continue, ERROR_CANCELLED if timedout
|
||
|
// or canncelled otherwise.
|
||
|
hr = pun->Show(NULL, 0); // we don't support iquerycontinue, we will just wait
|
||
|
|
||
|
pun->Release();
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Gets the list of items on the desktop that should be cleaned
|
||
|
//
|
||
|
// if dwCleanMode == CLEANUP_MODE_NORMAL, it only loads items which have not been used recently
|
||
|
// if dwCleanMode == CLEANUP_MODE_ALL, it loads all items on the desktop, marking those which have not been used recently
|
||
|
//
|
||
|
//
|
||
|
STDMETHODIMP CCleanupWiz::_LoadDesktopContents()
|
||
|
{
|
||
|
ASSERT(_psf);
|
||
|
|
||
|
IEnumIDList * ppenum;
|
||
|
|
||
|
DWORD grfFlags = SHCONTF_FOLDERS | SHCONTF_NONFOLDERS;
|
||
|
HRESULT hr = _psf->EnumObjects(NULL, grfFlags, &ppenum);
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
_CleanUpDSA();
|
||
|
_hdsaItems = DSA_Create(sizeof(FOLDERITEMDATA), c_GROWBYSIZE);
|
||
|
|
||
|
if (_hdsaItems)
|
||
|
{
|
||
|
ULONG celtFetched;
|
||
|
FOLDERITEMDATA fid = {0};
|
||
|
|
||
|
hr = S_OK;
|
||
|
while(SUCCEEDED(hr) && (S_OK == ppenum->Next(1,&fid.pidl, &celtFetched)))
|
||
|
{
|
||
|
if (_IsSupportedType(fid.pidl)) // only support links and regitems
|
||
|
{
|
||
|
// note, the call to _IsCandidateForRemoval also obtains the last
|
||
|
// used timestamp for the item
|
||
|
BOOL bShouldRemove = ((CLEANUP_MODE_SILENT == _dwCleanMode) ||
|
||
|
(_IsCandidateForRemoval(fid.pidl, &fid.ftLastUsed)));
|
||
|
if ( (CLEANUP_MODE_ALL == _dwCleanMode) || bShouldRemove)
|
||
|
{
|
||
|
SHFILEINFO sfi = {0};
|
||
|
if (SHGetFileInfo((LPCTSTR) fid.pidl,
|
||
|
0,
|
||
|
&sfi,
|
||
|
sizeof(sfi),
|
||
|
SHGFI_PIDL | SHGFI_DISPLAYNAME | SHGFI_ICON | SHGFI_SMALLICON ))
|
||
|
{
|
||
|
if (Str_SetPtr(&(fid.pszName), sfi.szDisplayName))
|
||
|
{
|
||
|
fid.hIcon = sfi.hIcon;
|
||
|
fid.bSelected = bShouldRemove;
|
||
|
if (-1 != DSA_AppendItem(_hdsaItems, &fid))
|
||
|
{
|
||
|
// All is well, the item has succesfully been added
|
||
|
// to the dsa, we zero out the fields so as not to
|
||
|
// free those resources now, they will be freed when the
|
||
|
// dsa is destroyed.
|
||
|
ZeroMemory(&fid, sizeof(fid));
|
||
|
continue;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hr = E_OUTOFMEMORY;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
// Common cleanup path for various failure cases above,
|
||
|
// we did not add this item to the dsa, so cleanup now.
|
||
|
_CleanUpDSAItem(&fid);
|
||
|
}
|
||
|
//
|
||
|
// If we did not load any items into the dsa, we return S_FALSE
|
||
|
//
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = DSA_GetItemCount(_hdsaItems) > 0 ? S_OK : S_FALSE;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// we failed to allocate memory for the DSA
|
||
|
hr = E_OUTOFMEMORY;
|
||
|
}
|
||
|
ppenum->Release();
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Gets the list of items on the desktop that should be cleaned
|
||
|
//
|
||
|
// if dwCleanMode == CLEANUP_MODE_SILENT, it loads all items on all desktops, marking them all
|
||
|
//
|
||
|
//
|
||
|
STDMETHODIMP CCleanupWiz::_LoadMergedDesktopContents()
|
||
|
{
|
||
|
ASSERT(_psf);
|
||
|
|
||
|
IEnumIDList * ppenum;
|
||
|
|
||
|
DWORD grfFlags = _dwCleanMode == CLEANUP_MODE_SILENT ? SHCONTF_FOLDERS | SHCONTF_NONFOLDERS: SHCONTF_NONFOLDERS;
|
||
|
HRESULT hr = _psf->EnumObjects(NULL, grfFlags, &ppenum);
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
_CleanUpDSA();
|
||
|
_hdsaItems = DSA_Create(sizeof(FOLDERITEMDATA), c_GROWBYSIZE);
|
||
|
|
||
|
if (_hdsaItems)
|
||
|
{
|
||
|
ULONG celtFetched;
|
||
|
FOLDERITEMDATA fid = {0};
|
||
|
|
||
|
hr = S_OK;
|
||
|
while(SUCCEEDED(hr) && (S_OK == ppenum->Next(1,&fid.pidl, &celtFetched)))
|
||
|
{
|
||
|
if (_IsSupportedType(fid.pidl)) // only support links and regitems
|
||
|
{
|
||
|
// note, the call to _IsCandidateForRemoval also obtains the last
|
||
|
// used timestamp for the item
|
||
|
BOOL bShouldRemove = ((CLEANUP_MODE_SILENT == _dwCleanMode) ||
|
||
|
(_IsCandidateForRemoval(fid.pidl, &fid.ftLastUsed)));
|
||
|
if ( (CLEANUP_MODE_ALL == _dwCleanMode) || bShouldRemove)
|
||
|
{
|
||
|
SHFILEINFO sfi = {0};
|
||
|
if (SHGetFileInfo((LPCTSTR) fid.pidl,
|
||
|
0,
|
||
|
&sfi,
|
||
|
sizeof(sfi),
|
||
|
SHGFI_PIDL | SHGFI_DISPLAYNAME | SHGFI_ICON | SHGFI_SMALLICON ))
|
||
|
{
|
||
|
if (Str_SetPtr(&(fid.pszName), sfi.szDisplayName))
|
||
|
{
|
||
|
fid.hIcon = sfi.hIcon;
|
||
|
fid.bSelected = bShouldRemove;
|
||
|
if (-1 != DSA_AppendItem(_hdsaItems, &fid))
|
||
|
{
|
||
|
// All is well, the item has succesfully been added
|
||
|
// to the dsa, we zero out the fields so as not to
|
||
|
// free those resources now, they will be freed when the
|
||
|
// dsa is destroyed.
|
||
|
ZeroMemory(&fid, sizeof(fid));
|
||
|
continue;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hr = E_OUTOFMEMORY;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
// Common cleanup path for various failure cases above,
|
||
|
// we did not add this item to the dsa, so cleanup now.
|
||
|
_CleanUpDSAItem(&fid);
|
||
|
}
|
||
|
//
|
||
|
// If we did not load any items into the dsa, we return S_FALSE
|
||
|
//
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = DSA_GetItemCount(_hdsaItems) > 0 ? S_OK : S_FALSE;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// we failed to allocate memory for the DSA
|
||
|
hr = E_OUTOFMEMORY;
|
||
|
}
|
||
|
ppenum->Release();
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Expects the given pidl to be a link or regitem. Determines if it is a candidate for removal based
|
||
|
// on when was the last time it was used.
|
||
|
//
|
||
|
STDMETHODIMP_(BOOL) CCleanupWiz::_IsCandidateForRemoval(LPCITEMIDLIST pidl, FILETIME * pftLastUsed)
|
||
|
{
|
||
|
BOOL bRet = FALSE; // be conservative, if we do not know anything about the
|
||
|
// object we will not volunteer to remove it
|
||
|
int cHit = 0;
|
||
|
TCHAR szName[MAX_PATH];
|
||
|
|
||
|
ASSERT(_psf);
|
||
|
|
||
|
//
|
||
|
// we store UEM usage info for the regitems and links on the desktop
|
||
|
//
|
||
|
if (SUCCEEDED(DisplayNameOf(_psf,
|
||
|
pidl,
|
||
|
SHGDN_INFOLDER | SHGDN_FORPARSING,
|
||
|
szName,
|
||
|
ARRAYSIZE(szName))))
|
||
|
{
|
||
|
if (SUCCEEDED(_GetUEMInfo(-1, (LPARAM) szName, &cHit, pftLastUsed)))
|
||
|
{
|
||
|
FILETIME ft;
|
||
|
GetFileTimeNDaysFromCurrentTime(&ft, -_iDeltaDays);
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
SYSTEMTIME st;
|
||
|
FileTimeToSystemTime(&ft, &st);
|
||
|
#endif
|
||
|
|
||
|
bRet = (CompareFileTime(pftLastUsed, &ft) < 0);
|
||
|
}
|
||
|
}
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Copied from shell\shell32\deskfldr.cpp : CDesktopViewCallBack::ShouldShow
|
||
|
// If you modify this, modify that as well
|
||
|
//
|
||
|
STDMETHODIMP CCleanupWiz::_ShouldShow(IShellFolder* psf, LPCITEMIDLIST pidlFolder, LPCITEMIDLIST pidlItem)
|
||
|
{
|
||
|
HRESULT hr = S_OK; //Assume that this item should be shown!
|
||
|
|
||
|
#if 0
|
||
|
//
|
||
|
// we know this is the real desktop
|
||
|
// leaving this code here to maintain parallel with deskfldr.cpp
|
||
|
//
|
||
|
if (!_fCheckedIfRealDesktop) //Have we done this check before?
|
||
|
{
|
||
|
_fRealDesktop = IsDesktopBrowser(_punkSite);
|
||
|
_fCheckedIfRealDesktop = TRUE; //Remember this fact!
|
||
|
}
|
||
|
|
||
|
if (!_fRealDesktop)
|
||
|
return S_OK; //Not a real desktop! So, let's show everything!
|
||
|
#endif
|
||
|
|
||
|
IShellFolder2 *psf2;
|
||
|
if (SUCCEEDED(psf->QueryInterface(IID_PPV_ARG(IShellFolder2, &psf2))))
|
||
|
{
|
||
|
// Get the GUID in the pidl, which requires IShellFolder2.
|
||
|
CLSID guidItem;
|
||
|
if (SUCCEEDED(GetItemCLSID(psf2, pidlItem, &guidItem)))
|
||
|
{
|
||
|
SHELLSTATE ss = {0};
|
||
|
SHGetSetSettings(&ss, SSF_STARTPANELON, FALSE); //See if the StartPanel is on!
|
||
|
|
||
|
TCHAR szRegPath[MAX_PATH];
|
||
|
//Get the proper registry path based on if StartPanel is ON/OFF
|
||
|
wsprintf(szRegPath, REGSTR_PATH_HIDDEN_DESKTOP_ICONS, (ss.fStartPanelOn ? REGSTR_VALUE_STARTPANEL : REGSTR_VALUE_CLASSICMENU));
|
||
|
|
||
|
//Convert the guid to a string
|
||
|
TCHAR szGuidValue[MAX_GUID_STRING_LEN];
|
||
|
|
||
|
SHStringFromGUID(guidItem, szGuidValue, ARRAYSIZE(szGuidValue));
|
||
|
|
||
|
//See if this item is turned off in the registry.
|
||
|
if (SHRegGetBoolUSValue(szRegPath, szGuidValue, FALSE, /* default */FALSE))
|
||
|
hr = S_FALSE; //They want to hide it; So, return S_FALSE.
|
||
|
|
||
|
if (SHRestricted(REST_NOMYCOMPUTERICON) && IsEqualCLSID(CLSID_MyComputer, guidItem))
|
||
|
hr = S_FALSE;
|
||
|
|
||
|
}
|
||
|
psf2->Release();
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Normal, All: We only support removing regitems and links from the desktop
|
||
|
// Silent: We support moving everything except the clean up shortcut folders and the recycle bin
|
||
|
//
|
||
|
STDMETHODIMP_(BOOL) CCleanupWiz::_IsSupportedType(LPCITEMIDLIST pidl)
|
||
|
{
|
||
|
BOOL fRetVal = FALSE;
|
||
|
|
||
|
// if not in silent mode, must be a link or regitem
|
||
|
eFILETYPE eType = _GetItemType(pidl);
|
||
|
if (CLEANUP_MODE_SILENT == _dwCleanMode ||
|
||
|
FC_TYPE_LINK == eType || FC_TYPE_REGITEM == eType)
|
||
|
{
|
||
|
// can't be the recycle bin
|
||
|
IShellFolder* psf;
|
||
|
if (SUCCEEDED(SHGetDesktopFolder(&psf)))
|
||
|
{
|
||
|
IShellFolder2 *psf2;
|
||
|
if (SUCCEEDED(psf->QueryInterface(IID_PPV_ARG(IShellFolder2, &psf2))))
|
||
|
{
|
||
|
CLSID guidItem;
|
||
|
if (SUCCEEDED(GetItemCLSID(psf2, pidl, &guidItem)) &&
|
||
|
!IsEqualCLSID(CLSID_MyComputer, guidItem) &&
|
||
|
!IsEqualCLSID(CLSID_MyDocuments, guidItem) &&
|
||
|
!IsEqualCLSID(CLSID_NetworkPlaces, guidItem) &&
|
||
|
!IsEqualCLSID(CLSID_RecycleBin, guidItem))
|
||
|
{
|
||
|
// can't be the folder we're storing things in
|
||
|
TCHAR szName[MAX_PATH];
|
||
|
if (SUCCEEDED(DisplayNameOf(_psf,
|
||
|
pidl,
|
||
|
SHGDN_INFOLDER | SHGDN_FORPARSING,
|
||
|
szName,
|
||
|
ARRAYSIZE(szName))))
|
||
|
{
|
||
|
if (0 != lstrcmp(szName, _szFolderName))
|
||
|
{
|
||
|
fRetVal = (S_OK == _ShouldShow(psf, NULL, pidl));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
psf2->Release();
|
||
|
}
|
||
|
psf->Release();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return fRetVal;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Returns the type of the pidl.
|
||
|
// We are only interested in Links and Regitems, so we return FC_TYPE_OTHER for
|
||
|
// all other items.
|
||
|
//
|
||
|
STDMETHODIMP_(eFILETYPE) CCleanupWiz::_GetItemType(LPCITEMIDLIST pidl)
|
||
|
{
|
||
|
eFILETYPE eftVal = FC_TYPE_OTHER;
|
||
|
TCHAR szName[MAX_PATH];
|
||
|
IShellLink *psl;
|
||
|
|
||
|
ASSERT(_psf);
|
||
|
|
||
|
if (SUCCEEDED( _psf->GetUIObjectOf(NULL,
|
||
|
1,
|
||
|
&pidl,
|
||
|
IID_PPV_ARG_NULL(IShellLink, &psl))))
|
||
|
{
|
||
|
eftVal = FC_TYPE_LINK;
|
||
|
psl->Release();
|
||
|
}
|
||
|
else if (SUCCEEDED(DisplayNameOf(_psf,
|
||
|
pidl,
|
||
|
SHGDN_INFOLDER | SHGDN_FORPARSING,
|
||
|
szName,
|
||
|
ARRAYSIZE(szName))))
|
||
|
{
|
||
|
if(_IsRegItemName(szName))
|
||
|
{
|
||
|
|
||
|
eftVal = FC_TYPE_REGITEM;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//
|
||
|
// Maybe this item is a kind of .{GUID} object we created to restore
|
||
|
// regitems. In that case we want to actually restore the regitem
|
||
|
// at this point by marking it as unhidden.
|
||
|
//
|
||
|
|
||
|
LPTSTR pszExt = PathFindExtension(szName);
|
||
|
if (TEXT('.') == *pszExt // it is a file extension
|
||
|
&& lstrlen(++pszExt) == (GUIDSTR_MAX - 1) // AND the extension is of the right length
|
||
|
// note: GUIDSTR_MAX includes the terminating NULL
|
||
|
// while lstrlen does not, hence the expression
|
||
|
&& TEXT('{') == *pszExt) // AND looks like it is a guid...
|
||
|
{
|
||
|
// we most prob have a bonafide guid string
|
||
|
// pszExt now points to the beginning of the GUID string
|
||
|
TCHAR szGUIDName[ARRAYSIZE(TEXT("::")) + GUIDSTR_MAX];
|
||
|
|
||
|
// put it in the regitem SHGDN_FORPARSING name format, which is like
|
||
|
//
|
||
|
// "::{16B280C6-EE70-11D1-9066-00C04FD9189D}"
|
||
|
//
|
||
|
wnsprintf(szGUIDName, ARRAYSIZE(szGUIDName), TEXT("::%s"), pszExt );
|
||
|
|
||
|
LPITEMIDLIST pidlGUID;
|
||
|
DWORD dwAttrib = SFGAO_NONENUMERATED;
|
||
|
|
||
|
//
|
||
|
// get the pidl of the regitem, if this call succeeds, it means we do have
|
||
|
// a corresponding regitem in the desktop's namespace
|
||
|
//
|
||
|
if (SUCCEEDED(_psf->ParseDisplayName(NULL,
|
||
|
NULL,
|
||
|
szGUIDName,
|
||
|
NULL,
|
||
|
&pidlGUID,
|
||
|
&dwAttrib)))
|
||
|
{
|
||
|
//
|
||
|
// check if the regitem is marked as hidden
|
||
|
//
|
||
|
if (dwAttrib & SFGAO_NONENUMERATED)
|
||
|
{
|
||
|
//
|
||
|
// One last check before we enable the regitem:
|
||
|
// Does the regitem have the same display name as the .CLSID file.
|
||
|
// In case the user has restored this .CLSID file and renamed it we will
|
||
|
// not attempt to restore the regitem as it may confuse the user.
|
||
|
//
|
||
|
TCHAR szNameRegItem[MAX_PATH];
|
||
|
|
||
|
if (SUCCEEDED((DisplayNameOf(_psf,
|
||
|
pidl,
|
||
|
SHGDN_NORMAL,
|
||
|
szName,
|
||
|
ARRAYSIZE(szName)))) &&
|
||
|
SUCCEEDED((DisplayNameOf(_psf,
|
||
|
pidlGUID,
|
||
|
SHGDN_NORMAL,
|
||
|
szNameRegItem,
|
||
|
ARRAYSIZE(szNameRegItem)))) &&
|
||
|
lstrcmp(szName, szNameRegItem) == 0)
|
||
|
{
|
||
|
if (SUCCEEDED(_HideRegPidl(pidlGUID, FALSE)))
|
||
|
{
|
||
|
// delete the file corresponding to the regitem
|
||
|
if (SUCCEEDED(DisplayNameOf(_psf,
|
||
|
pidl,
|
||
|
SHGDN_NORMAL | SHGDN_FORPARSING,
|
||
|
szName,
|
||
|
ARRAYSIZE(szName))))
|
||
|
{
|
||
|
DeleteFile(szName); // too bad if we fail, we will just
|
||
|
// have two identical icons on the desktop
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Log the current time as the last used time of the regitem.
|
||
|
// We just re-enabled this regitem but we do not have the
|
||
|
// usage info for the corresponding .{CLSID} which the user had
|
||
|
// been using so far. So we will be conservative and say that
|
||
|
// it was used right now, so that it does not become a candidate
|
||
|
// for removal soon. As this is a regitem that the user restored
|
||
|
// after the wizard removed it, so it is a fair assumption that
|
||
|
// the user has used it after restoring it and is not a candidate
|
||
|
// for cleanup right now.
|
||
|
//
|
||
|
UEMFireEvent(&UEMIID_SHELL, UEME_RUNPATH, UEMF_XEVENT, -1, (LPARAM)szGUIDName);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
ILFree(pidlGUID);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
return eftVal;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Determines if a filename is that of a regitem
|
||
|
//
|
||
|
// a regitem's SHGDN_INFOLDER | SHGDN_FORPARSING name is always "::{someguid}"
|
||
|
//
|
||
|
// CDefview::_LogDesktopLinksAndRegitems() uses the same test to determine
|
||
|
// if a given pidl is a regitem. This case can lead to false positives if
|
||
|
// you have other items on the desktop which have infoder parsing names
|
||
|
// beginning with "::{", but as ':' is not presently allowed in filenames
|
||
|
// it should not be a problem.
|
||
|
//
|
||
|
STDMETHODIMP_(BOOL) CCleanupWiz::_IsRegItemName(LPTSTR pszName)
|
||
|
{
|
||
|
return (pszName[0] == TEXT(':') && pszName[1] == TEXT(':') && pszName[2] == TEXT('{'));
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP_(BOOL) CCleanupWiz::_CreateFakeRegItem(LPCTSTR pszDestPath, LPCTSTR pszName, LPCTSTR pszGUID)
|
||
|
{
|
||
|
BOOL fRetVal = FALSE;
|
||
|
|
||
|
TCHAR szLinkName[MAX_PATH];
|
||
|
lstrcpyn(szLinkName, pszDestPath, ARRAYSIZE(szLinkName));
|
||
|
PathAppend(szLinkName, pszName);
|
||
|
StrCatBuff(szLinkName, TEXT("."), ARRAYSIZE(szLinkName));
|
||
|
StrCatBuff(szLinkName, pszGUID, ARRAYSIZE(szLinkName));
|
||
|
|
||
|
//
|
||
|
// We use the CREATE_ALWAYS flag so that if the file already exists
|
||
|
// in the Unused Desktop Files folder, we will go ahead and hide the
|
||
|
// regitem.
|
||
|
//
|
||
|
HANDLE hFile = CreateFile(szLinkName, GENERIC_READ, FILE_SHARE_READ, NULL,
|
||
|
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||
|
if (hFile != INVALID_HANDLE_VALUE)
|
||
|
{
|
||
|
// we created/opened the shortcut, now hide the regitem and close
|
||
|
// the shortcut file
|
||
|
fRetVal = TRUE;
|
||
|
CloseHandle(hFile);
|
||
|
}
|
||
|
|
||
|
return fRetVal;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Given path to an exe, returns the UEM hit count and last used date for it
|
||
|
//
|
||
|
STDMETHODIMP CCleanupWiz::_GetUEMInfo(WPARAM wParam, LPARAM lParam, int * pcHit, FILETIME * pftLastUsed)
|
||
|
{
|
||
|
UEMINFO uei;
|
||
|
uei.cbSize = sizeof(uei);
|
||
|
uei.dwMask = UEIM_HIT | UEIM_FILETIME;
|
||
|
|
||
|
|
||
|
HRESULT hr = UEMQueryEvent(&UEMIID_SHELL, UEME_RUNPATH, wParam, lParam, &uei);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
*pcHit = uei.cHit;
|
||
|
*pftLastUsed = uei.ftExecute;
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
STDMETHODIMP_(BOOL) CCleanupWiz::_ShouldProcess()
|
||
|
{
|
||
|
BOOL fRetVal = FALSE;
|
||
|
if (_dwCleanMode == CLEANUP_MODE_SILENT)
|
||
|
{
|
||
|
fRetVal = TRUE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
int cItems = DSA_GetItemCount(_hdsaItems);
|
||
|
for (int i = 0; i < cItems; i++)
|
||
|
{
|
||
|
FOLDERITEMDATA * pfid = (FOLDERITEMDATA *) DSA_GetItemPtr(_hdsaItems, i);
|
||
|
if (pfid->bSelected)
|
||
|
{
|
||
|
fRetVal = TRUE;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return fRetVal;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Process the list of items. At this point _hdsaItems only contains the
|
||
|
// items that the user wants to delete
|
||
|
//
|
||
|
STDMETHODIMP CCleanupWiz::_ProcessItems()
|
||
|
{
|
||
|
TCHAR szFolderLocation[MAX_PATH]; // desktop folder
|
||
|
HRESULT hr = S_OK;
|
||
|
|
||
|
if (_ShouldProcess())
|
||
|
{
|
||
|
LPITEMIDLIST pidlCommonDesktop = NULL;
|
||
|
|
||
|
ASSERT(_psf);
|
||
|
// use the archive folder on the desktop
|
||
|
if (CLEANUP_MODE_SILENT != _dwCleanMode)
|
||
|
{
|
||
|
hr = DisplayNameOf(_psf, NULL, SHGDN_FORPARSING, szFolderLocation, ARRAYSIZE(szFolderLocation));
|
||
|
}
|
||
|
else // use the archive folder in Program Files
|
||
|
{
|
||
|
hr = SHGetFolderLocation(NULL, CSIDL_PROGRAM_FILES , NULL, 0, &pidlCommonDesktop);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = DisplayNameOf(_psf, pidlCommonDesktop, SHGDN_FORPARSING, szFolderLocation, ARRAYSIZE(szFolderLocation));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
ASSERTMSG(*_szFolderName, "Desktop Cleaner: Archive Folder Name not present");
|
||
|
|
||
|
// create the full path of the archive folder
|
||
|
TCHAR szFolderPath[MAX_PATH];
|
||
|
lstrcpyn(szFolderPath, szFolderLocation, ARRAYSIZE(szFolderPath));
|
||
|
PathAppend(szFolderPath, _szFolderName);
|
||
|
|
||
|
//
|
||
|
// We have to make sure that this folder exists, as otherwise, if we try to move
|
||
|
// a single shortcut using SHFileOperation, that file will be renamed to the target
|
||
|
// name instead of being put in a folder with that name.
|
||
|
//
|
||
|
SECURITY_ATTRIBUTES sa;
|
||
|
sa.nLength = sizeof (sa);
|
||
|
sa.lpSecurityDescriptor = NULL; // we get the default attributes for this process
|
||
|
sa.bInheritHandle = FALSE;
|
||
|
|
||
|
int iRetVal = SHCreateDirectoryEx(NULL, szFolderPath, &sa);
|
||
|
if (ERROR_SUCCESS == iRetVal || ERROR_FILE_EXISTS == iRetVal || ERROR_ALREADY_EXISTS == iRetVal)
|
||
|
{
|
||
|
DblNulTermList dnSourceFiles;
|
||
|
TCHAR szFileName[MAX_PATH + 1]; // to pad an extra null char for SHFileOpStruct
|
||
|
|
||
|
//
|
||
|
//
|
||
|
// now we can start on the files we need to move
|
||
|
//
|
||
|
int cItems = DSA_GetItemCount(_hdsaItems);
|
||
|
for (int i = 0; i < cItems; i++)
|
||
|
{
|
||
|
FOLDERITEMDATA * pfid = (FOLDERITEMDATA *) DSA_GetItemPtr(_hdsaItems, i);
|
||
|
|
||
|
if ((pfid->bSelected || _dwCleanMode == CLEANUP_MODE_SILENT) &&
|
||
|
SUCCEEDED(DisplayNameOf(_psf,pfid->pidl,
|
||
|
SHGDN_FORPARSING,
|
||
|
szFileName,
|
||
|
ARRAYSIZE(szFileName) - 1)))
|
||
|
{
|
||
|
if (_IsRegItemName(szFileName))
|
||
|
{
|
||
|
// if its a regitem, we create a "Item Name.{GUID}" file
|
||
|
// and mark the regitem as hidden.
|
||
|
//
|
||
|
if (_CreateFakeRegItem(szFolderPath, pfid->pszName, szFileName+2))
|
||
|
{
|
||
|
_HideRegPidl(pfid->pidl, TRUE);
|
||
|
}
|
||
|
}
|
||
|
else // not a regitem, will move it
|
||
|
{
|
||
|
dnSourceFiles.AddString(szFileName);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
if (dnSourceFiles.Count() > 0)
|
||
|
{
|
||
|
DblNulTermList dnTargetFolder;
|
||
|
dnTargetFolder.AddString(szFolderPath);
|
||
|
|
||
|
SHFILEOPSTRUCT sfo;
|
||
|
sfo.hwnd = NULL;
|
||
|
sfo.wFunc = FO_MOVE;
|
||
|
sfo.pFrom = (LPCTSTR) dnSourceFiles;
|
||
|
sfo.pTo = (LPCTSTR) dnTargetFolder;
|
||
|
sfo.fFlags = FOF_NORECURSION | FOF_NOCONFIRMMKDIR | FOF_ALLOWUNDO ;
|
||
|
hr = SHFileOperation(&sfo);
|
||
|
}
|
||
|
|
||
|
SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_PATH, (LPCVOID) szFolderPath, 0);
|
||
|
SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_PATH, (LPCVOID) szFolderLocation, 0);
|
||
|
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// we failed to create the Unused Desktop Files folder
|
||
|
hr = E_FAIL;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// DialogProcs
|
||
|
//
|
||
|
// TODO: test for accessibilty issues
|
||
|
//
|
||
|
////////////////////////////////////////////////////////
|
||
|
|
||
|
INT_PTR STDMETHODCALLTYPE CCleanupWiz::_IntroPageDlgProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
INT_PTR ipRet = FALSE;
|
||
|
|
||
|
switch (wMsg)
|
||
|
{
|
||
|
case WM_INITDIALOG:
|
||
|
{
|
||
|
HWND hWnd = GetDlgItem(hDlg, IDC_TEXT_TITLE_WELCOME);
|
||
|
SetWindowFont(hWnd, _hTitleFont, TRUE);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case WM_NOTIFY :
|
||
|
{
|
||
|
LPNMHDR lpnm = (LPNMHDR) lParam;
|
||
|
|
||
|
switch (lpnm->code)
|
||
|
{
|
||
|
case PSN_SETACTIVE:
|
||
|
PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_NEXT);
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return ipRet;
|
||
|
}
|
||
|
|
||
|
INT_PTR STDMETHODCALLTYPE CCleanupWiz::_ChooseFilesPageDlgProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
INT_PTR ipRet = FALSE;
|
||
|
HWND hwLV = NULL;
|
||
|
|
||
|
switch (wMsg)
|
||
|
{
|
||
|
case WM_INITDIALOG:
|
||
|
_InitChoosePage(hDlg);
|
||
|
ipRet = TRUE;
|
||
|
break;
|
||
|
|
||
|
case WM_NOTIFY :
|
||
|
LPNMHDR lpnm = (LPNMHDR) lParam;
|
||
|
switch (lpnm->code)
|
||
|
{
|
||
|
case PSN_SETACTIVE:
|
||
|
PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_NEXT | PSWIZB_BACK);
|
||
|
hwLV = GetDlgItem(hDlg, IDC_LV_PROMPT);
|
||
|
_SetCheckedState(hwLV);
|
||
|
break;
|
||
|
|
||
|
case PSN_WIZNEXT:
|
||
|
// remember the items the user selected
|
||
|
hwLV = GetDlgItem(hDlg, IDC_LV_PROMPT);
|
||
|
_MarkSelectedItems(hwLV);
|
||
|
break;
|
||
|
|
||
|
case PSN_WIZBACK:
|
||
|
// remember the items the user selected
|
||
|
hwLV = GetDlgItem(hDlg, IDC_LV_PROMPT);
|
||
|
_MarkSelectedItems(hwLV);
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
return ipRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
INT_PTR STDMETHODCALLTYPE CCleanupWiz::_FinishPageDlgProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
INT_PTR ipRet = FALSE;
|
||
|
|
||
|
switch (wMsg)
|
||
|
{
|
||
|
case WM_INITDIALOG:
|
||
|
_InitFinishPage(hDlg);
|
||
|
ipRet = TRUE;
|
||
|
break;
|
||
|
|
||
|
case WM_NOTIFY :
|
||
|
{
|
||
|
LPNMHDR lpnm = (LPNMHDR) lParam;
|
||
|
|
||
|
switch (lpnm->code)
|
||
|
{
|
||
|
case PSN_SETACTIVE:
|
||
|
PropSheet_SetWizButtons(GetParent(hDlg), _cItemsOnDesktop ? PSWIZB_BACK | PSWIZB_FINISH : PSWIZB_FINISH);
|
||
|
// selection can change so need to do this everytime you come to this page
|
||
|
_RefreshFinishPage(hDlg);
|
||
|
break;
|
||
|
|
||
|
case PSN_WIZFINISH:
|
||
|
// process the items now
|
||
|
_ProcessItems();
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return ipRet;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// stub dialog proc which redirects calls to the right dialog procs
|
||
|
//
|
||
|
INT_PTR CALLBACK CCleanupWiz::s_StubDlgProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
PDLGPROCINFO pInfo = (PDLGPROCINFO) GetWindowLongPtr(hDlg, DWLP_USER);
|
||
|
|
||
|
if (WM_INITDIALOG == wMsg)
|
||
|
{
|
||
|
pInfo = (PDLGPROCINFO) ((LPPROPSHEETPAGE) lParam) -> lParam;
|
||
|
SetWindowLongPtr(hDlg, DWLP_USER, (LPARAM) pInfo);
|
||
|
}
|
||
|
|
||
|
if (pInfo)
|
||
|
{
|
||
|
CCleanupWiz * pThis = pInfo->pcfc;
|
||
|
PCFC_DlgProcFn pfn = pInfo->pfnDlgProc;
|
||
|
|
||
|
return (pThis->*pfn)(hDlg, wMsg, wParam, lParam);
|
||
|
}
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
STDMETHODIMP CCleanupWiz::_InitListBox(HWND hWndListView)
|
||
|
{
|
||
|
ListView_SetExtendedListViewStyle(hWndListView, LVS_EX_SUBITEMIMAGES);
|
||
|
//
|
||
|
// add the columns
|
||
|
//
|
||
|
LVCOLUMN lvcDate;
|
||
|
TCHAR szDateHeader[c_MAX_HEADER_LEN];
|
||
|
|
||
|
lvcDate.mask = LVCF_SUBITEM | LVCF_WIDTH | LVCF_TEXT ;
|
||
|
lvcDate.iSubItem = FC_COL_SHORTCUT;
|
||
|
lvcDate.cx = 200;
|
||
|
LoadString(g_hInst, IDS_HEADER_ITEM, szDateHeader, ARRAYSIZE(szDateHeader));
|
||
|
lvcDate.pszText = szDateHeader;
|
||
|
ListView_InsertColumn(hWndListView, FC_COL_SHORTCUT, &lvcDate);
|
||
|
|
||
|
lvcDate.mask = LVCF_SUBITEM | LVCF_FMT | LVCF_WIDTH | LVCF_TEXT ;
|
||
|
lvcDate.iSubItem = FC_COL_DATE;
|
||
|
lvcDate.fmt = LVCFMT_LEFT;
|
||
|
lvcDate.cx = 1;
|
||
|
LoadString(g_hInst, IDS_HEADER_DATE, szDateHeader, ARRAYSIZE(szDateHeader));
|
||
|
lvcDate.pszText = szDateHeader;
|
||
|
ListView_InsertColumn(hWndListView, FC_COL_DATE, &lvcDate);
|
||
|
ListView_SetColumnWidth(hWndListView, FC_COL_DATE, LVSCW_AUTOSIZE_USEHEADER);
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CCleanupWiz::_InitChoosePage(HWND hDlg)
|
||
|
{
|
||
|
HWND hWndListView = GetDlgItem(hDlg, IDC_LV_PROMPT);
|
||
|
|
||
|
_InitListBox(hWndListView);
|
||
|
|
||
|
//
|
||
|
// add the images
|
||
|
//
|
||
|
HIMAGELIST hSmall = ImageList_Create(GetSystemMetrics(SM_CXSMICON),
|
||
|
GetSystemMetrics(SM_CYSMICON),
|
||
|
ILC_MASK | ILC_COLOR32 , c_GROWBYSIZE, c_GROWBYSIZE);
|
||
|
|
||
|
int cItems = DSA_GetItemCount(_hdsaItems);
|
||
|
for (int i = 0; i < cItems; i++)
|
||
|
{
|
||
|
FOLDERITEMDATA * pfid = (FOLDERITEMDATA *) DSA_GetItemPtr(_hdsaItems, i);
|
||
|
ImageList_AddIcon(hSmall, pfid->hIcon);
|
||
|
}
|
||
|
ListView_SetImageList(hWndListView, hSmall, LVSIL_SMALL);
|
||
|
|
||
|
//
|
||
|
// set the checkboxes style
|
||
|
//
|
||
|
ListView_SetExtendedListViewStyleEx(hWndListView, LVS_EX_CHECKBOXES, LVS_EX_CHECKBOXES);
|
||
|
|
||
|
_PopulateListView(hWndListView);
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CCleanupWiz::_InitFinishPage(HWND hDlg)
|
||
|
{
|
||
|
HWND hWnd = GetDlgItem(hDlg, IDC_TEXT_TITLE_WELCOME);
|
||
|
|
||
|
SetWindowFont(hWnd, _hTitleFont, TRUE);
|
||
|
|
||
|
HIMAGELIST hSmall = ImageList_Create(GetSystemMetrics(SM_CXSMICON),
|
||
|
GetSystemMetrics(SM_CYSMICON),
|
||
|
ILC_MASK | ILC_COLOR32, c_GROWBYSIZE, c_GROWBYSIZE);
|
||
|
|
||
|
int cItems = DSA_GetItemCount(_hdsaItems);
|
||
|
for (int i = 0; i < cItems; i++)
|
||
|
{
|
||
|
FOLDERITEMDATA * pfid = (FOLDERITEMDATA *) DSA_GetItemPtr(_hdsaItems, i);
|
||
|
ImageList_AddIcon(hSmall, pfid->hIcon);
|
||
|
}
|
||
|
|
||
|
ListView_SetImageList(GetDlgItem(hDlg, IDC_LV_INFORM), hSmall, LVSIL_SMALL);
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CCleanupWiz::_RefreshFinishPage(HWND hDlg)
|
||
|
{
|
||
|
HWND hWndListView = GetDlgItem(hDlg, IDC_LV_INFORM);
|
||
|
ListView_DeleteAllItems(hWndListView);
|
||
|
|
||
|
int cMovedItems = _PopulateListViewFinish(hWndListView);
|
||
|
|
||
|
// set the informative text to reflect how many items were moved
|
||
|
HWND hWnd = GetDlgItem(hDlg, IDC_TEXT_INFORM);
|
||
|
TCHAR szDisplayText[c_MAX_PROMPT_TEXT];
|
||
|
|
||
|
ShowWindow(GetDlgItem(hDlg, IDC_LV_INFORM), BOOLIFY(cMovedItems));
|
||
|
ShowWindow(GetDlgItem(hDlg, IDC_TEXT_SHORTCUTS), BOOLIFY(cMovedItems));
|
||
|
ShowWindow(GetDlgItem(hDlg, IDC_TEXT_CHANGE), BOOLIFY(cMovedItems));
|
||
|
|
||
|
if ( 0 == cMovedItems)
|
||
|
{
|
||
|
LoadString(g_hInst, _cItemsOnDesktop ? IDS_INFORM_NONE : IDS_INFORM_NONEFOUND, szDisplayText, ARRAYSIZE(szDisplayText));
|
||
|
}
|
||
|
else if (1 == cMovedItems)
|
||
|
{
|
||
|
LoadString(g_hInst, IDS_INFORM_SINGLE, szDisplayText, ARRAYSIZE(szDisplayText));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TCHAR szRawText[c_MAX_PROMPT_TEXT];
|
||
|
LoadString(g_hInst, IDS_INFORM, szRawText, ARRAYSIZE(szRawText));
|
||
|
wnsprintf(szDisplayText, ARRAYSIZE(szDisplayText), szRawText, cMovedItems);
|
||
|
}
|
||
|
SetWindowText(hWnd, szDisplayText);
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
STDMETHODIMP_(int) CCleanupWiz::_PopulateListView(HWND hWndListView)
|
||
|
{
|
||
|
LVITEM lvi = {0};
|
||
|
int cRet = 0;
|
||
|
int cItems = DSA_GetItemCount(_hdsaItems);
|
||
|
for (int i = 0; i < cItems; i++)
|
||
|
{
|
||
|
FOLDERITEMDATA * pfid = (FOLDERITEMDATA *) DSA_GetItemPtr(_hdsaItems, i);
|
||
|
|
||
|
lvi.mask = LVIF_TEXT | LVIF_IMAGE;
|
||
|
lvi.pszText = pfid->pszName;
|
||
|
lvi.iImage = i;
|
||
|
lvi.iItem = i;
|
||
|
lvi.iSubItem = FC_COL_SHORTCUT;
|
||
|
ListView_InsertItem(hWndListView, &lvi);
|
||
|
cRet++;
|
||
|
|
||
|
// set the last used date
|
||
|
TCHAR szDate[c_MAX_DATE_LEN];
|
||
|
if (SUCCEEDED(_GetDateFromFileTime(pfid->ftLastUsed, szDate, ARRAYSIZE(szDate))))
|
||
|
{
|
||
|
ListView_SetItemText(hWndListView, i, FC_COL_DATE, szDate);
|
||
|
}
|
||
|
}
|
||
|
return cRet;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP_(int) CCleanupWiz::_PopulateListViewFinish(HWND hWndListView)
|
||
|
{
|
||
|
LVITEM lvi = {0};
|
||
|
lvi.mask = LVIF_TEXT | LVIF_IMAGE ;
|
||
|
int cRet = 0;
|
||
|
int cItems = DSA_GetItemCount(_hdsaItems);
|
||
|
for (int i = 0; i < cItems; i++)
|
||
|
{
|
||
|
FOLDERITEMDATA * pfid = (FOLDERITEMDATA *) DSA_GetItemPtr(_hdsaItems, i);
|
||
|
|
||
|
//
|
||
|
// it's the Finish Page, we only show the items we were asked to move
|
||
|
//
|
||
|
if (pfid->bSelected)
|
||
|
{
|
||
|
lvi.pszText = pfid->pszName;
|
||
|
lvi.iImage = i;
|
||
|
lvi.iItem = i;
|
||
|
ListView_InsertItem(hWndListView, &lvi);
|
||
|
cRet++;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
return cRet;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Converts a given FILETIME date into s displayable string
|
||
|
//
|
||
|
STDMETHODIMP CCleanupWiz::_GetDateFromFileTime(FILETIME ftLastUsed, LPTSTR pszDate, int cch )
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
if (0 == ftLastUsed.dwHighDateTime && 0 == ftLastUsed.dwLowDateTime)
|
||
|
{
|
||
|
if (0 == LoadString(g_hInst, IDS_NEVER, pszDate, cch))
|
||
|
{
|
||
|
hr = E_FAIL;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DWORD dwFlags = FDTF_SHORTDATE;
|
||
|
if (0 == SHFormatDateTime(&ftLastUsed, &dwFlags, pszDate, cch))
|
||
|
{
|
||
|
hr = E_FAIL;
|
||
|
}
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Marks listview items as checked or unchecked
|
||
|
//
|
||
|
STDMETHODIMP CCleanupWiz::_SetCheckedState(HWND hWndListView)
|
||
|
{
|
||
|
int cItems = DSA_GetItemCount(_hdsaItems);
|
||
|
for (int i = 0; i < cItems; i++)
|
||
|
{
|
||
|
FOLDERITEMDATA * pfid = (FOLDERITEMDATA *) DSA_GetItemPtr(_hdsaItems, i);
|
||
|
ListView_SetCheckState(hWndListView, i, pfid->bSelected);
|
||
|
}
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Reverse of above, updates our list based on user selection.
|
||
|
//
|
||
|
STDMETHODIMP CCleanupWiz::_MarkSelectedItems(HWND hWndListView)
|
||
|
{
|
||
|
int cItems = ListView_GetItemCount(hWndListView);
|
||
|
for (int iLV = 0; iLV < cItems; iLV++)
|
||
|
{
|
||
|
FOLDERITEMDATA * pfid = (FOLDERITEMDATA *) DSA_GetItemPtr(_hdsaItems, iLV);
|
||
|
pfid->bSelected = ListView_GetCheckState(hWndListView, iLV);
|
||
|
}
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// These methods clean up _hdsaItems and free the allocated memory
|
||
|
//
|
||
|
STDMETHODIMP_(void) CCleanupWiz::_CleanUpDSA()
|
||
|
{
|
||
|
if (_hdsaItems != NULL)
|
||
|
{
|
||
|
for (int i = DSA_GetItemCount(_hdsaItems)-1; i >= 0; i--)
|
||
|
{
|
||
|
FOLDERITEMDATA * pfid = (FOLDERITEMDATA *) DSA_GetItemPtr(_hdsaItems,i);
|
||
|
_CleanUpDSAItem(pfid);
|
||
|
}
|
||
|
DSA_Destroy(_hdsaItems);
|
||
|
_hdsaItems = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CCleanupWiz::_CleanUpDSAItem(FOLDERITEMDATA * pfid)
|
||
|
{
|
||
|
|
||
|
if (pfid->pidl)
|
||
|
{
|
||
|
ILFree(pfid->pidl);
|
||
|
}
|
||
|
|
||
|
if (pfid->pszName)
|
||
|
{
|
||
|
Str_SetPtr(&(pfid->pszName), NULL);
|
||
|
}
|
||
|
|
||
|
if (pfid->hIcon)
|
||
|
{
|
||
|
DestroyIcon(pfid->hIcon);
|
||
|
}
|
||
|
|
||
|
ZeroMemory(pfid, sizeof(*pfid));
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
//////////////////////
|
||
|
//
|
||
|
// Hide regitems
|
||
|
//
|
||
|
//////////////////////
|
||
|
|
||
|
|
||
|
//
|
||
|
// Helper routines used below.
|
||
|
// Cloned from shell32/util.cpp
|
||
|
//
|
||
|
STDMETHODIMP GetItemCLSID(IShellFolder2 *psf, LPCITEMIDLIST pidlLast, CLSID *pclsid)
|
||
|
{
|
||
|
VARIANT var;
|
||
|
HRESULT hr = psf->GetDetailsEx(pidlLast, &SCID_DESCRIPTIONID, &var);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
SHDESCRIPTIONID did;
|
||
|
hr = VariantToBuffer(&var, (void *)&did, sizeof(did));
|
||
|
if (SUCCEEDED(hr))
|
||
|
*pclsid = did.clsid;
|
||
|
|
||
|
VariantClear(&var);
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Given a regitem, it sets the SFGAO_NONENUMERATED bit on it so that
|
||
|
// that it no longer shows up in the folder.
|
||
|
//
|
||
|
// Since we are primarily only interested in cleaning up desktop clutter,
|
||
|
// that means we don't have to worry about all possible kinds of regitems.
|
||
|
// Our main target is apps like Outlook which create regitems instead of
|
||
|
// .lnk shortcuts. So our code does not have to be as complex as the
|
||
|
// regfldr.cpp version for deleting regitems, which has to account for
|
||
|
// everything, from legacy regitems to delegate folders.
|
||
|
//
|
||
|
//
|
||
|
STDMETHODIMP CCleanupWiz::_HideRegPidl(LPCITEMIDLIST pidlr, BOOL fHide)
|
||
|
{
|
||
|
IShellFolder2 *psf2;
|
||
|
HRESULT hr = _psf->QueryInterface(IID_PPV_ARG(IShellFolder2, &psf2));
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
CLSID clsid;
|
||
|
hr = GetItemCLSID(psf2, pidlr, &clsid);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = _HideRegItem(&clsid, fHide, NULL);
|
||
|
}
|
||
|
psf2->Release();
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CCleanupWiz::_HideRegItem(CLSID* pclsid, BOOL fHide, BOOL* pfWasVisible)
|
||
|
{
|
||
|
HKEY hkey;
|
||
|
|
||
|
if (pfWasVisible)
|
||
|
{
|
||
|
*pfWasVisible = FALSE;
|
||
|
}
|
||
|
|
||
|
HRESULT hr = SHRegGetCLSIDKey(*pclsid, TEXT("ShellFolder"), FALSE, TRUE, &hkey);
|
||
|
if(SUCCEEDED(hr))
|
||
|
{
|
||
|
DWORD dwAttr, dwErr;
|
||
|
DWORD dwType = 0;
|
||
|
DWORD cbSize = sizeof(dwAttr);
|
||
|
|
||
|
if (ERROR_SUCCESS == RegQueryValueEx(hkey, TEXT("Attributes"), NULL, &dwType, (BYTE *) &dwAttr, &cbSize))
|
||
|
{
|
||
|
if (pfWasVisible)
|
||
|
{
|
||
|
*pfWasVisible = !(dwAttr & SFGAO_NONENUMERATED);
|
||
|
}
|
||
|
fHide ? dwAttr |= SFGAO_NONENUMERATED : dwAttr &= ~SFGAO_NONENUMERATED;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// attributes do not exist, so we will try creating them
|
||
|
fHide ? dwAttr = SFGAO_NONENUMERATED : dwAttr = 0;
|
||
|
}
|
||
|
dwErr = RegSetValueEx(hkey, TEXT("Attributes"), NULL, dwType, (BYTE *) &dwAttr, cbSize);
|
||
|
hr = HRESULT_FROM_WIN32(dwErr);
|
||
|
RegCloseKey(hkey);
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Method writes out the last used time in the registry and the
|
||
|
// number of days it was checkin for
|
||
|
//
|
||
|
STDMETHODIMP CCleanupWiz::_LogUsage()
|
||
|
{
|
||
|
FILETIME ft;
|
||
|
SYSTEMTIME st;
|
||
|
|
||
|
GetLocalTime(&st);
|
||
|
|
||
|
SystemTimeToFileTime(&st, &ft);
|
||
|
|
||
|
//
|
||
|
// we ignore if any of these calls fail, as we cannot really do anything
|
||
|
// in that case. the next time we run, we will run maybe sooner that expected.
|
||
|
//
|
||
|
SHRegSetUSValue(REGSTR_PATH_CLEANUPWIZ, REGSTR_VAL_TIME,
|
||
|
REG_BINARY, &ft, sizeof(ft),
|
||
|
SHREGSET_FORCE_HKCU);
|
||
|
|
||
|
SHRegSetUSValue(REGSTR_PATH_CLEANUPWIZ, REGSTR_VAL_DELTA_DAYS,
|
||
|
REG_DWORD,(DWORD *) &_iDeltaDays, sizeof(_iDeltaDays),
|
||
|
SHREGSET_FORCE_HKCU);
|
||
|
|
||
|
//
|
||
|
// TODO: also write out to log file here
|
||
|
//
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// returns the current value from the policy key or the user settings
|
||
|
//
|
||
|
STDMETHODIMP_(int) CCleanupWiz::GetNumDaysBetweenCleanup()
|
||
|
{
|
||
|
DWORD dwData;
|
||
|
DWORD dwType;
|
||
|
DWORD cch = sizeof (DWORD);
|
||
|
|
||
|
int iDays = -1; // if the value does not exist
|
||
|
|
||
|
//
|
||
|
// ISSUE-2000/12/01-AIDANL Removed REGSTR_POLICY_CLEANUP, don't think we need both, but want to
|
||
|
// leave this note in case issues come up later.
|
||
|
//
|
||
|
if (ERROR_SUCCESS == (SHRegGetUSValue(REGSTR_PATH_CLEANUPWIZ, REGSTR_VAL_DELTA_DAYS,
|
||
|
&dwType, &dwData, &cch,FALSE, NULL, 0)))
|
||
|
{
|
||
|
iDays = dwData;
|
||
|
}
|
||
|
|
||
|
return iDays;
|
||
|
}
|
||
|
|
||
|
// helper functions
|
||
|
STDAPI_(BOOL) IsUserAGuest()
|
||
|
{
|
||
|
return SHTestTokenMembership(NULL, DOMAIN_ALIAS_RID_GUESTS);
|
||
|
}
|