Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

5796 lines
176 KiB

#include "shellprv.h"
#pragma hdrstop
#include "copy.h"
// idldata.c
HRESULT CIDLData_CloneForMoveCopy(LPDATAOBJECT pdtobjIn, LPDATAOBJECT *ppdtobjOut);
#ifdef DEBUG
//#define BBDEBUG
#endif
#ifdef BBDEBUG
BOOL g_fbbdebug = TRUE;
#endif
HINSTANCE g_hinstMM = NULL;
#define MM_DONTLOAD ((HINSTANCE)-1)
typedef struct _fsselchangeinfo * PFSSELCHANGEINFO;
void FOUndo_AddInfo(LPUNDOATOM lpua, LPTSTR lpszSrc, LPTSTR lpszDest, DWORD dwAttributes);
void FOUndo_FileReallyDeleted(LPTSTR lpszFile);
void CALLBACK FOUndo_Release(LPUNDOATOM lpua);
void SetDateTimeText(HWND hdlg, int id, const FILETIME *pftUTC);
BOOL CheckFolderSizeAndDeleteability(LPCTSTR pszDir, FOLDERCONTENTSINFO *pfci);
BOOL IsDirectoryDeletable(LPCTSTR lpszDir);
BOOL IsFileDeletable(LPCTSTR lpszFile);
#define DELETEMAX 100000
#define MAX_BITBUCKETS 27
#define MAX_DRIVES 26
#define OPENFILERETRYTIME 500
#define OPENFILERETRYCOUNT 10
#define SERVERDRIVE 26
#define Desktop_IsReg(_pidl) (SIL_GetType(_pidl) == SHID_ROOT_REGITEM)
#define BITBUCKET_DATAFILE_VERSION 0 // Ansi Win95 wastebasket
#define BITBUCKET_DATAFILE_VERSION2 2 // Unicode Win96/Cairo wastebasket
#define PIDLTODATAENTRYID(pidl) ((LPBBDATAENTRYIDA)((LPBYTE)pidl + (pidl->mkid.cb - SIZEOF(BBDATAENTRYIDA))))
#define BB_IsRealID(pidl) ( ((LPBYTE)pidl) < ((LPBYTE)PIDLTODATAENTRYID(pidl)))
#define DATAENTRYIDATOW(pbbidl) ((LPBBDATAENTRYIDW)((LPBYTE)pbbidl - FIELD_OFFSET(BBDATAENTRYIDW,cb)))
// datafile format:
//
// (binary writes)
// BITBUCKETDATAHEADER
// array of BBDATAENTRY
typedef struct _BitBucketDataHeader {
int idVersion;
int cNumFiles; // number of entries
int cCurrent; // the current file number.
UINT cbDataEntrySize; // size of each entry
DWORD dwSize;
} BBDATAHEADER, * LPBBDATAHEADER;
typedef struct _BitBucketDataEntryA {
CHAR szOriginal[MAX_PATH]; // original filename
int iIndex; // index (key to name)
int idDrive; // which drive bucket it's currently in
FILETIME ft;
DWORD dwSize;
// shouldn't need file attributes because we did a move file
// which should have preserved them.
} BBDATAENTRYA, * LPBBDATAENTRYA;
typedef struct _BitBucketDataEntryW {
CHAR szShortName[MAX_PATH]; // original filename shortened
int iIndex; // index (key to name)
int idDrive; // which drive bucket it's currently in
FILETIME ft;
DWORD dwSize;
WCHAR szOriginal[MAX_PATH]; // original filename
} BBDATAENTRYW, * LPBBDATAENTRYW;
#ifdef UNICODE
#define BBDATAENTRY BBDATAENTRYW
#define LPBBDATAENTRY LPBBDATAENTRYW
#else
#define BBDATAENTRY BBDATAENTRYA
#define LPBBDATAENTRY LPBBDATAENTRYA
#endif
typedef struct _BitBucketDriveInfo {
LPITEMIDLIST pidl; // points to the BitBucket dir for this drive
int cFiles; // counts the current "delete operation"
DWORD dwSize; // how much stuff is in this bit bucket
int iPercent;
ULONGLONG cbMaxSize; // maximum size of bitbucket (in bytes)
DWORD dwClusterSize;
BOOL fNukeOnDelete;
// for the write cache
HDPA hdpaDeleteCache; // deleted files. not yet written to info file
// for the read cache
LPBBDATAENTRY lpbbdeRead; // cached read of bitbucket
int cReadCount;
BOOL fReadDirty;
UINT cbDataEntrySize; // size of each data entry
} BBDRIVEINFO, *LPBBDRIVEINFO;
typedef struct _BitBucektDataEntryIDA {
WORD cb;
BBDATAENTRYA bbde;
} BBDATAENTRYIDA;
typedef UNALIGNED BBDATAENTRYIDA * LPBBDATAENTRYIDA;
typedef struct _BitBucektDataEntryIDW {
WCHAR wszOriginal[MAX_PATH];
WORD cb;
BBDATAENTRYA bbde;
} BBDATAENTRYIDW;
typedef UNALIGNED BBDATAENTRYIDW * LPBBDATAENTRYIDW;
#ifdef UNICODE
#define BBDATAENTRYID BBDATAENTRYIDW
#define LPBBDATAENTRYID LPBBDATAENTRYIDW
#else
#define BBDATAENTRYID BBDATAENTRYIDA
#define LPBBDATAENTRYID LPBBDATAENTRYIDA
#endif
// for the pidls
typedef struct _CBitBucket // dxi
{
IPersistFolder ipf;
IShellFolder isf;
IContextMenu icm;
IShellPropSheetExt ips;
IShellExtInit isei;
UINT cRef;
LPITEMIDLIST pidl;
DWORD dwSize ;
} CShellBitBucket, * LPSHELLBITBUCKET;
#pragma data_seg(DATASEG_READONLY)
enum
{
ICOL_NAME = 0,
ICOL_ORIGINAL,
ICOL_MODIFIED,
ICOL_TYPE,
ICOL_SIZE,
ICOL_MAX, // Make sure this is the last enum item
} ;
#define ICOL_FIRST ICOL_NAME
const COL_DATA c_bb_cols[] = {
{ICOL_NAME, IDS_NAME_COL, 20, LVCFMT_LEFT},
{ICOL_ORIGINAL, IDS_DELETEDFROM_COL, 20, LVCFMT_LEFT},
{ICOL_MODIFIED, IDS_DATEDELETED_COL, 18, LVCFMT_LEFT},
{ICOL_TYPE, IDS_TYPE_COL, 20, LVCFMT_LEFT},
{ICOL_SIZE, IDS_SIZE_COL, 8, LVCFMT_RIGHT},
};
#pragma data_seg()
const TCHAR c_szInfo[] = TEXT("INFO");
const TCHAR c_szDeletedFileTemplate[] = TEXT("D%c%d%s");
const TCHAR c_szDeathRow[] = TEXT("RECYCLED");
const TCHAR c_szSolitaryRow[] = TEXT("RECYCLER");
const TCHAR c_szPurgeInfo[] = TEXT("PurgeInfo");
const TCHAR c_szSSS[] = TEXT("%s\\%s\\%s");
const TCHAR c_szSSSS[] = TEXT("%s\\%s\\%s\\%s");
const TCHAR c_szBITBUCKET_CLASSID[] = TEXT("{645FF040-5081-101B-9F08-00AA002F954E}");
const TCHAR c_szFull[] = TEXT("Full");
const TCHAR c_szEmpty[] = TEXT("Empty");
const TCHAR c_szDStarDotStar[] = TEXT("D*.*");
#ifdef SBS_DEBUG
#define TEMPSTR TEXT("Chee")
#else
#define TEMPSTR
#endif
TCHAR const c_szBitBucket[] = TEXT("BitBucket");
TCHAR const c_szRegRealMode[] = TEXT("Network\\Real Mode Net") TEMPSTR;
TCHAR const c_szSetup[] = TEXT("Setup");
TCHAR const c_szSetupN[] = REGSTR_VAL_SETUPN;
TCHAR const c_szWinDir[] = REGSTR_VAL_WINDIR TEMPSTR;
LPBBDRIVEINFO g_pBitBucket[MAX_BITBUCKETS] = {0};
int g_iCheckPurgeCount = 0;
LPTSTR g_szNetHomeDir = NULL;
#define CHECK_PURGE_MAX 500
// the maximum we'll write to the info file at one time
#define MAX_WRITE_SIZE (((CHECK_PURGE_MAX + 1) * SIZEOF(BBDATAENTRY)) + SIZEOF(BBDATAHEADER))
HRESULT CShellBitBucket_SF_GetDisplayNameOf(LPSHELLFOLDER psf, LPCITEMIDLIST pidl, DWORD uFlags, LPSTRRET pStrRet);
HFILE BitBucketLoadHeader(LPTSTR lpszPath, LPBBDATAHEADER lpbbdh, BOOL fClose, BOOL fCreateBitBucket, DWORD dwRWStyle);
int BBLoadFileData(int idDrive);
int BBFileToIndex(LPCTSTR lpcszFile);
LPBBDRIVEINFO MakeBitBucket(int idDrive);
LPITEMIDLIST BBDataEntryToPidl(int idDrive, LPBBDATAENTRY lpbbde, WIN32_FIND_DATA *pfd);
void BBNukeFileList(LPCTSTR lpszSrc);
DWORD BBCheckPurgeFiles(int idDrive);
void BBNukeFileInfo(LPCTSTR lpszPath);
BOOL SaveDeletedFileInfo(LPBBDRIVEINFO lpbbdi, BOOL fForceCleanRead);
void FOUndo_FileRestored(LPTSTR lpszFile);
BOOL InitializeRecycledDirectory(int idDrive);
HRESULT CShellBitBucket_PS_AddPages(IShellPropSheetExt * pspx,
LPFNADDPROPSHEETPAGE lpfnAddPage,
LPARAM lParam);
int BBDeletedFilesOnDrive(int idDrive);
// extern
HRESULT CCommonShellPropSheetExt_ReplacePage(IShellPropSheetExt * pspx,
UINT uPageID,
LPFNADDPROPSHEETPAGE lpfnReplaceWith,
LPARAM lParam);
#define NONETHOMEDIR ((LPTSTR)-1)
/*
This is how the network setup stuff works...
"Drive 26" specifies the network homedir
g_szNetHomeDir can be: NULL = unknown setup
NONETHOMEDIR = no network homedir
otherwise it's a string ( global ) pointing to the homedir (lfn)
*/
// Helpers
const TCHAR c_szRegSetup[] = REGSTR_PATH_SETUP;
LPCTSTR GetNetHomeDir()
{
if (g_szNetHomeDir == NULL) {
HKEY hkeyCurrentVersion;
// we haven't checked yet... do so now
ENTERCRITICAL;
// the default:
g_szNetHomeDir = NONETHOMEDIR;
if (RegOpenKey(HKEY_LOCAL_MACHINE, c_szRegSetup, &hkeyCurrentVersion) == ERROR_SUCCESS) {
HKEY hkeyRealMode;
if (RegOpenKey(hkeyCurrentVersion, c_szRegRealMode, &hkeyRealMode) == ERROR_SUCCESS) {
DWORD dwType;
TCHAR szBuffer[30];
LPDWORD lpdw = ((LPDWORD)szBuffer);
LONG cbData = SIZEOF(szBuffer);
*lpdw = 0;
if ((RegQueryValueEx(hkeyRealMode, c_szSetupN, 0, &dwType, (LPBYTE)szBuffer, &cbData) == ERROR_SUCCESS)
&& cbData &&
(((dwType == REG_SZ) && StrToInt(szBuffer)) ||
((dwType == REG_BINARY || dwType == REG_DWORD) &&
(*lpdw)))) {
HKEY hkeySetup;
TCHAR szPath[MAX_PATH];
cbData = SIZEOF(szPath);
// now get the home dir
if (RegOpenKey(hkeyCurrentVersion, c_szSetup, &hkeySetup) == ERROR_SUCCESS) {
if ((RegQueryValueEx(hkeySetup, c_szWinDir, 0, &dwType, (LPBYTE)szPath, &cbData) == ERROR_SUCCESS)
&& cbData && (dwType == REG_SZ)) {
GetWindowsDirectory(szPath, ARRAYSIZE(szPath));
g_szNetHomeDir = NULL;
Str_SetPtr(&g_szNetHomeDir, szPath);
if (!g_szNetHomeDir) {
Assert(0);
g_szNetHomeDir = NONETHOMEDIR;
}
}
}
}
RegCloseKey(hkeyRealMode);
}
RegCloseKey(hkeyCurrentVersion);
}
LEAVECRITICAL;
}
return g_szNetHomeDir;
}
BOOL BitBucketableDrive(int idDrive)
{
return (RealDriveType(idDrive, FALSE) == DRIVE_FIXED ||
((idDrive == SERVERDRIVE) && (GetNetHomeDir() != NONETHOMEDIR )));
}
void BBPathBuildRoot(LPTSTR szPath, int idDrive)
{
Assert(idDrive >= 0);
if (idDrive == 26) {
if (GetNetHomeDir() == NONETHOMEDIR) {
szPath[0] = 0;
Assert(0);
} else
lstrcpy(szPath, g_szNetHomeDir);
} else {
PathBuildRoot(szPath, idDrive);
}
}
LPCTSTR PathFindChild(LPCTSTR lpszParent, LPCTSTR lpszChild)
{
int iLen = lstrlen(lpszParent);
if (PathCommonPrefix(lpszParent, lpszChild, NULL) == iLen) {
return lpszChild + iLen;
} else
return NULL;
}
INT BBPathGetDriveNumber(LPNCTSTR lpszPath)
{
LPCTSTR lpszTmpPath;
INT idDrive;
#ifdef ALIGNMENT_SCENARIO
{
TCHAR szTmp[MAX_PATH];
ualstrcpyn(szTmp, lpszPath, ARRAYSIZE(szTmp));
lpszTmpPath = szTmp;
}
#else
{
lpszTmpPath = lpszPath;
}
#endif
idDrive = PathGetDriveNumber(lpszTmpPath);
if ((GetNetHomeDir() != NONETHOMEDIR)) {
if (PathFindChild(g_szNetHomeDir, lpszTmpPath))
return SERVERDRIVE;
}
return idDrive;
}
void DriveIDToBBPath(int idDrive, LPTSTR lpszPath)
{
BBPathBuildRoot(lpszPath, idDrive);
#ifdef WINNT
if (DriveIsSecure(idDrive))
{
LPTSTR lpszInmate;
lpszInmate = GetCurrentUserSid();
if (lpszInmate)
{
PathAppend(lpszPath, c_szSolitaryRow);
PathAppend(lpszPath, lpszInmate);
LocalFree((HLOCAL)lpszInmate);
return;
}
}
#endif
PathAppend(lpszPath, c_szDeathRow);
}
void BBGetDeletedFileName(LPTSTR lpszFileName, int cFiles, LPNTSTR lpszOriginal)
{
TCHAR c = TEXT('A') + (TCHAR) BBPathGetDriveNumber(lpszOriginal);
if (c == (TEXT('A') + SERVERDRIVE))
c = TEXT('@');
#ifdef ALIGNMENT_SCENARIO
{
TCHAR szTmp[MAX_PATH];
ualstrcpyn(szTmp, lpszOriginal, ARRAYSIZE(szTmp));
wsprintf(lpszFileName, c_szDeletedFileTemplate, c, cFiles,
PathFindExtension(szTmp));
}
#else
{
wsprintf(lpszFileName, c_szDeletedFileTemplate, c, cFiles,
PathFindExtension(lpszOriginal));
}
#endif
}
void UpdateIcon(BOOL fFull)
{
LONG cbData;
DWORD dwType;
LONG err;
HKEY hkey;
HRESULT hres;
BOOL fUserSpecific;
TCHAR szNewValue[MAX_PATH];
TCHAR szValue[MAX_PATH];
#ifdef BB_DEBUG
DebugMsg(DM_TRACE, TEXT("BitBucket: UpdateIcon %s"), fFull ? TEXT("Full") : TEXT("Empty"));
#endif
// Loop through user specific, then non-user specific (win95 compatible)
fUserSpecific = TRUE;
do {
hres = SHRegGetCLSIDKey(&CLSID_ShellBitBucket, c_szDefaultIcon,
fUserSpecific, &hkey);
if (SUCCEEDED(hres))
{
cbData = SIZEOF(szValue);
err = RegQueryValueEx(hkey, NULL,
0, &dwType, (LPBYTE)szValue, &cbData);
if (err != ERROR_SUCCESS)
szValue[0] = TEXT('\0');
cbData = SIZEOF(szNewValue);
err = RegQueryValueEx(hkey, fFull ? c_szFull : c_szEmpty,
0, &dwType, (LPBYTE)szNewValue, &cbData);
if (err == ERROR_SUCCESS)
goto UpdateIt;
RegCloseKey(hkey);
}
fUserSpecific = !fUserSpecific; // Iterate TRUE,FALSE, then stop.
} while (!fUserSpecific);
// If we can't open any registry keys, then blow it off...
return;
UpdateIt:
if (lstrcmpi(szNewValue,szValue) != 0)
{
LPTSTR lpsz;
RegSetValueEx(hkey, NULL, 0, dwType, (LPBYTE)szNewValue, cbData);
lpsz = StrRChr(szValue, NULL, TEXT(','));
if (lpsz) {
int id;
*lpsz = 0;
// ..and tell anyone viewing this image index to update
id = LookupIconIndex(PathFindFileName(szValue), StrToInt(lpsz + 1) , 0);
SHChangeNotify(SHCNE_UPDATEIMAGE, SHCNF_DWORD | SHCNF_FLUSH | SHCNF_FLUSHNOWAIT, (LPCVOID)id, NULL);
// now make sure our cache is updated.
//BUGBUG do we need this
FlushFileClass();
}
}
RegCloseKey(hkey);
}
#ifdef BBDEBUG
void DebugDumpBB(LPTSTR lpszText, LPBBDRIVEINFO lpbbdi)
{
TCHAR szPath[MAX_PATH];
int i;
if (!lpbbdi) {
DebugMsg(DM_TRACE, TEXT("sh - tr - BITBUCKETDUMP - %s : NULL BITBUCKET"), lpszText);
return;
}
if (!g_fbbdebug)
return;
SHGetPathFromIDList(lpbbdi->pidl, szPath);
DebugMsg(DM_TRACE, TEXT("sh - tr - BITBUCKETDUMP - %s : %s"), lpszText, szPath);
DebugMsg(DM_TRACE, TEXT(" cFiles = %d"), lpbbdi->cFiles);
DebugMsg(DM_TRACE, TEXT(" dwSize = %d"), lpbbdi->dwSize);
DebugMsg(DM_TRACE, TEXT(" iPercent = %d"), lpbbdi->iPercent);
DebugMsg(DM_TRACE, TEXT(" cbMaxSize = %d"), lpbbdi->cbMaxSize);
DebugMsg(DM_TRACE, TEXT(" cReadCount = %d"), lpbbdi->cReadCount);
DebugMsg(DM_TRACE, TEXT(" fReadDirty = %d"), lpbbdi->fReadDirty);
DebugMsg(DM_TRACE, TEXT(" lpbbdeRead = %x"), lpbbdi->lpbbdeRead);
for (i = 0 ; i < lpbbdi->cReadCount; i++) {
if ( lpbbdi->cbDataEntrySize == SIZEOF(BBDATAENTRYW)) {
LPBBDATAENTRYW lpbbdew = (LPBBDATAENTRYW)lpbbdi->lpbbdeRead;
DebugMsg(DM_TRACE, TEXT(" %d - %d"), i, lpbbdew[i].iIndex);
DebugMsg(DM_TRACE, TEXT(" %s"), lpbbdew[i].szOriginal);
} else {
LPBBDATAENTRYA lpbbdea = (LPBBDATAENTRYA)lpbbdi->lpbbdeRead;
DebugMsg(DM_TRACE, TEXT(" %d - %d"), i, lpbbdea[i].iIndex);
DebugMsg(DM_TRACE, TEXT(" %s"), lpbbdea[i].szOriginal);
}
}
}
#endif
//----------------------------------------------------------------------------
// Sort of a registry equivalent of the profile API's.
BOOL Reg_GetStructEx(HKEY hkey, LPCTSTR pszSubKey, LPCTSTR pszValue, LPVOID pData, DWORD *pcbData, UINT uFlags)
{
HKEY hkeyNew;
BOOL fRet = FALSE;
DWORD dwType;
// BUGBUG: do we really want to ignore settings on clean boot?
if (((uFlags & RGS_IGNORECLEANBOOT) || (!GetSystemMetrics(SM_CLEANBOOT))) &&
(RegOpenKey(hkey, pszSubKey, &hkeyNew) == ERROR_SUCCESS))
{
if (RegQueryValueEx(hkeyNew, (LPVOID)pszValue, 0, &dwType, pData, pcbData) == ERROR_SUCCESS)
{
fRet = TRUE;
}
RegCloseKey( hkeyNew );
}
return fRet;
}
BOOL Reg_GetStruct(HKEY hkey, LPCTSTR pszSubKey, LPCTSTR pszValue, LPVOID pData, DWORD *pcbData)
{
return Reg_GetStructEx(hkey, pszSubKey, pszValue, pData, pcbData, 0);
}
//----------------------------------------------------------------------------
// Sort of a registry equivalent of the profile API's.
BOOL Reg_SetStruct(HKEY hkey, LPCTSTR pszSubKey, LPCTSTR pszValue, LPVOID lpData, DWORD cbData)
{
HKEY hkeyNew;
BOOL fRet = FALSE;
if (RegCreateKey(hkey, pszSubKey, &hkeyNew) == ERROR_SUCCESS)
{
if (RegSetValueEx(hkeyNew, pszValue, 0, REG_BINARY, lpData, cbData) == ERROR_SUCCESS)
{
fRet = TRUE;
}
RegCloseKey(hkeyNew);
}
return fRet;
}
typedef struct {
DWORD dwSize;
BOOL fGlobal;
WORD wUsePercent[MAX_BITBUCKETS + 1];
DWORD dwNukeOnDelete; // BITFIELD
DWORD dwDummy; // to force a new size
} BBREGINFO;
#define BitBucketLoadRegistryInfo(lpbbdi, idDrive, lpcbDiskSpace) \
BBLoadRegistryStuff(lpbbdi, idDrive, lpcbDiskSpace, TRUE)
#define BitBucketGetDriveSettings(lpbbdi, idDrive, lpcbDiskSpace) \
BBLoadRegistryStuff(lpbbdi, idDrive, lpcbDiskSpace, FALSE)
// returns fGlobal;
BOOL BBLoadRegistryStuff(LPBBDRIVEINFO lpbbdi, int idDrive, ULONGLONG *lpcbDiskSpace, BOOL fIgnoreGlobal)
{
BBREGINFO bbreginfo;
DWORD cbData = SIZEOF(bbreginfo);
TCHAR szDrive[MAX_PATH];
DWORD dwSectorsPerCluster;
DWORD dwBytesPerSector;
DWORD dwFreeClusters;
DWORD dwTotalClusters;
HKEY hkeyExp;
hkeyExp = GetExplorerHkey(FALSE);
if (hkeyExp && Reg_GetStructEx(hkeyExp, c_szBitBucket, c_szPurgeInfo, &bbreginfo, &cbData, RGS_IGNORECLEANBOOT) &&
(cbData == SIZEOF(bbreginfo)) && (bbreginfo.dwSize == SIZEOF(bbreginfo)))
{
lpbbdi->iPercent = (int)bbreginfo.wUsePercent[idDrive];
lpbbdi->fNukeOnDelete = ((bbreginfo.dwNukeOnDelete & (1 << idDrive)) ? 1 : 0);
if (!fIgnoreGlobal) {
if (bbreginfo.fGlobal) {
lpbbdi->fNukeOnDelete = (bbreginfo.dwNukeOnDelete & (1 << MAX_BITBUCKETS)) ? 1 : 0;
lpbbdi->iPercent = (int)bbreginfo.wUsePercent[MAX_BITBUCKETS];
}
}
} else {
// defaults
lpbbdi->fNukeOnDelete = FALSE;
lpbbdi->iPercent = 10;
bbreginfo.fGlobal = TRUE;
}
if (idDrive != MAX_BITBUCKETS) {
// build x:\ string
BBPathBuildRoot(szDrive, idDrive);
PathStripToRoot(szDrive);
// REVIEW, if this gets really expensive, save it to the registry
if (GetDiskFreeSpace(szDrive, &dwSectorsPerCluster, &dwBytesPerSector,
&dwFreeClusters, &dwTotalClusters)) {
// parens are to try to avoid overflow... which could happen on >400M drive
// BUGBUG - BobDay - I think the parens are wrong (or could be better)
// MaxSize = (total size/100)*iPercent
// This point is probably moot in quadword arith.
lpbbdi->dwClusterSize = dwSectorsPerCluster * dwBytesPerSector;
lpbbdi->cbMaxSize = (dwTotalClusters * ((lpbbdi->dwClusterSize * lpbbdi->iPercent) / 100));
if (lpcbDiskSpace)
*lpcbDiskSpace = (dwTotalClusters * lpbbdi->dwClusterSize);
} else {
if (idDrive == SERVERDRIVE) {
lpbbdi->dwClusterSize = 2048;
lpbbdi->cbMaxSize = ((0x7FFFFFFF / 1024) * ((1024 * lpbbdi->iPercent) / 100));
#ifdef BB_DEBUG
DebugMsg(DM_TRACE, TEXT("cbMaxSize = %d"), lpbbdi->cbMaxSize);
#endif
if (lpcbDiskSpace)
*lpcbDiskSpace = 0x7FFFFFFF;
} else {
lpbbdi->cbMaxSize = 0;
lpbbdi->dwClusterSize = 0;
if (lpcbDiskSpace)
*lpcbDiskSpace = 0;
}
}
}
#ifdef BB_DEBUG
DebugMsg(DM_TRACE, TEXT("LoadRegistryStuff %d %d %d %d"), idDrive, lpbbdi->iPercent, lpbbdi->fNukeOnDelete, fIgnoreGlobal);
#endif
return bbreginfo.fGlobal;
}
void BBSaveRegistryStuff(int idDrive, int iPercent, BOOL fNukeOnDelete, BOOL fGlobal)
{
DWORD dwBit;
BBREGINFO bbreginfo;
DWORD cbData = SIZEOF(bbreginfo);
HKEY hkeyExp;
hkeyExp = GetExplorerHkey(TRUE);
if (!hkeyExp) {
Assert(0);
return;
}
if (!(Reg_GetStructEx(hkeyExp, c_szBitBucket, c_szPurgeInfo, &bbreginfo, &cbData, RGS_IGNORECLEANBOOT) &&
(cbData == SIZEOF(bbreginfo)) && (bbreginfo.dwSize == SIZEOF(bbreginfo))))
{
// defaults
int i;
for (i = 0; i < MAX_BITBUCKETS + 1; i++)
bbreginfo.wUsePercent[i] = 10;
bbreginfo.dwNukeOnDelete = 0;
bbreginfo.fGlobal = TRUE;
}
bbreginfo.dwSize = SIZEOF(bbreginfo);
if (idDrive == MAX_BITBUCKETS ) {
bbreginfo.fGlobal = fGlobal;
}
dwBit = (1 << idDrive);
if (fNukeOnDelete) {
bbreginfo.dwNukeOnDelete |= dwBit;
} else {
bbreginfo.dwNukeOnDelete &= ~dwBit;
}
bbreginfo.wUsePercent[idDrive] = (WORD)iPercent;
Reg_SetStruct(hkeyExp, c_szBitBucket, c_szPurgeInfo, &bbreginfo, SIZEOF(bbreginfo));
}
//========================================================================
// CShellBitBucket members
//========================================================================
HRESULT CShellBitBucket_SF_QueryInterface(LPSHELLFOLDER psf, REFIID riid, LPVOID * ppvObj)
{
LPSHELLBITBUCKET this = IToClass(CShellBitBucket, isf, psf);
//
// HACK: This block of code is added to allow us to test the custom
// shell view mechanism by putting a CLSID of bitbucket to replace
// the view of arbitray folder with Control panel.
//
#ifdef TEST_SHELLVIEW_REPLACEMENT
if (IsEqualIID(riid, &IID_IShellView))
{
LPSHELLFOLDER psfCtrl;
HRESULT hres = CControls_CreateInstance(NULL, &IID_IShellFolder, &psfCtrl);
if (SUCCEEDED(hres))
{
hres = psfCtrl->lpVtbl->CreateViewObject(psfCtrl, NULL, riid, ppvObj);
psfCtrl->lpVtbl->Release(psfCtrl);
}
return hres;
}
#endif
if (IsEqualIID(riid, &IID_IShellFolder))
{
*ppvObj = &this->isf;
this->cRef++;
return NOERROR;
}
else if (IsEqualIID(riid, &IID_IPersistFolder) ||
IsEqualIID(riid, &IID_IPersist) ||
IsEqualIID(riid, &IID_IUnknown))
{
*ppvObj = &this->ipf;
this->cRef++;
return NOERROR;
}
else if (IsEqualIID(riid, &IID_IContextMenu) )
{
*ppvObj = &this->icm;
this->cRef++;
return NOERROR;
} else if (IsEqualIID(riid, &IID_IShellPropSheetExt) )
{
*ppvObj = &this->ips;
this->cRef++;
return NOERROR;
} else if (IsEqualIID(riid, &IID_IShellExtInit)) {
*ppvObj = &this->isei;
this->cRef++;
return NOERROR;
}
*ppvObj = NULL;
return(E_NOINTERFACE);
}
ULONG CShellBitBucket_SF_Release(LPSHELLFOLDER psf)
{
LPSHELLBITBUCKET this = IToClass(CShellBitBucket, isf, psf);
this->cRef--;
if (this->cRef > 0)
{
return this->cRef;
}
ILFree(this->pidl);
LocalFree((HLOCAL)this);
return 0;
}
ULONG CShellBitBucket_SF_AddRef(LPSHELLFOLDER psf)
{
LPSHELLBITBUCKET this = IToClass(CShellBitBucket, isf, psf);
this->cRef++;
return this->cRef;
}
//
// We need to be able to compare the names of two bbpidls. Since either of
// them could be a unicode name, we might have to convert both to unicode.
//
int _BBCompareOriginal(LPBBDATAENTRYIDA lpbbidl1, LPBBDATAENTRYIDA lpbbidl2 )
{
BOOL fUnicode = FALSE;
// Convert both strings to a like format
if ( lpbbidl1->cb == SIZEOF(BBDATAENTRYIDW))
fUnicode = TRUE;
if ( lpbbidl2->cb == SIZEOF(BBDATAENTRYIDW))
fUnicode = TRUE;
if ( fUnicode ) {
WCHAR szTemp[MAX_PATH];
LPNWSTR lpsz1;
LPNWSTR lpsz2;
if ( lpbbidl1->cb == SIZEOF(BBDATAENTRYIDW)) {
LPBBDATAENTRYIDW lpbbidlw = DATAENTRYIDATOW(lpbbidl1);
lpsz1 = lpbbidlw->wszOriginal;
} else {
MultiByteToWideChar(CP_ACP, 0,
lpbbidl1->bbde.szOriginal, -1,
szTemp, ARRAYSIZE(szTemp));
lpsz1 = szTemp;
}
if ( lpbbidl2->cb == SIZEOF(BBDATAENTRYIDW)) {
LPBBDATAENTRYIDW lpbbidlw = DATAENTRYIDATOW(lpbbidl2);
lpsz2 = lpbbidlw->wszOriginal;
} else {
MultiByteToWideChar(CP_ACP, 0,
lpbbidl2->bbde.szOriginal, -1,
szTemp, ARRAYSIZE(szTemp));
lpsz2 = szTemp;
}
return ualstrcmpiW(lpsz1,lpsz2);
} else {
return lstrcmpiA(lpbbidl1->bbde.szOriginal,lpbbidl2->bbde.szOriginal);
}
}
HRESULT CShellBitBucket_SF_CompareIDs(LPSHELLFOLDER psf, LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
{
HRESULT hres = ResultFromShort(-1);
LPBBDATAENTRYIDA pbbidl1 = PIDLTODATAENTRYID(pidl1);
LPBBDATAENTRYIDA pbbidl2 = PIDLTODATAENTRYID(pidl2);
short nCmp;
// don't worry about recursing down because we don't have children.
switch(lParam) {
case ICOL_TYPE:
nCmp = _CompareFileTypes(psf, (LPIDFOLDER)pidl1, (LPIDFOLDER)pidl2);
if (nCmp)
{
return(ResultFromShort(nCmp));
} else
goto CompareNames;
case ICOL_SIZE:
{
ULONGLONG ull1;
ULONGLONG ull2;
FS_GetSize(NULL, (LPIDFOLDER)pidl1, &ull1);
FS_GetSize(NULL, (LPIDFOLDER)pidl2, &ull2);
if (ull1 < ull2)
return ResultFromShort(-1);
if (ull1 > ull2)
return ResultFromShort(1);
} // else fall through
CompareNames:
case ICOL_NAME:
hres = FS_CompareItemIDs((LPSHITEMID)pidl1, (LPSHITEMID)pidl2);
// compare the real filenames first, if they are different,
// try comparing the display name
if ((hres != ResultFromShort(0)) && (BB_IsRealID(pidl1) && BB_IsRealID(pidl2))) {
HRESULT hresOld = hres;
TCHAR szName1[MAX_PATH];
TCHAR szName2[MAX_PATH];
STRRET strret;
CShellBitBucket_SF_GetDisplayNameOf(psf, pidl1, 0, &strret);
StrRetToStrN(szName1, ARRAYSIZE(szName1), &strret, pidl1);
CShellBitBucket_SF_GetDisplayNameOf(psf, pidl2, 0, &strret);
StrRetToStrN(szName2, ARRAYSIZE(szName2), &strret, pidl1);
hres = ResultFromShort(lstrcmpi(szName1, szName2));
// if they are from the same location, sort them by delete times
if (hres == ResultFromShort(0)) {
// if the items are same in title, sort by drive
hres = ResultFromShort(pbbidl1->bbde.idDrive - pbbidl2->bbde.idDrive);
if (hres == ResultFromShort(0)) {
// if the items are still the same, sort by index
hres = ResultFromShort(pbbidl1->bbde.iIndex - pbbidl2->bbde.iIndex);
// once we're not equal, we can never be equal again
Assert(hres != ResultFromShort(0));
if (hres == ResultFromShort(0)) {
hres = hresOld;
}
}
}
}
#ifdef TEST_BB_COMPARE
if (hres == ResultFromShort(0)) {
if (pidl1 != pidl2) {
#ifdef BB_DEBUG
DebugMsg(DM_TRACE, TEXT("pidl1(%d) != pidl2(%d)"), pidl1, pidl2);
#endif
}
}
#endif
break;
case ICOL_ORIGINAL:
hres = ResultFromShort(_BBCompareOriginal(pbbidl1, pbbidl2));
break;
case ICOL_MODIFIED:
if (pbbidl1->bbde.ft.dwHighDateTime < pbbidl2->bbde.ft.dwHighDateTime) {
hres = ResultFromShort(-1);
} else if (pbbidl1->bbde.ft.dwHighDateTime > pbbidl2->bbde.ft.dwHighDateTime) {
hres = ResultFromShort(1);
} else {
if (pbbidl1->bbde.ft.dwLowDateTime < pbbidl2->bbde.ft.dwLowDateTime) {
hres = ResultFromShort(-1);
} else if (pbbidl1->bbde.ft.dwLowDateTime > pbbidl2->bbde.ft.dwLowDateTime) {
hres = ResultFromShort(1);
} else
return 0;
}
}
return hres;
}
HRESULT CShellBitBucket_SF_GetAttributesOf(LPSHELLFOLDER psf, UINT cidl, LPCITEMIDLIST * apidl, ULONG * rgfOut)
{
LPSHELLBITBUCKET this = IToClass(CShellBitBucket, isf, psf);
ULONG gfOut;
gfOut = *rgfOut;
// asking about the root itself
if (cidl == 0)
gfOut = SFGAO_HASPROPSHEET;
else
{
gfOut &= (SFGAO_CANMOVE | SFGAO_CANDELETE | SFGAO_HASPROPSHEET | SFGAO_FILESYSANCESTOR);
if (*rgfOut & SFGAO_LINK)
{
if (SHGetClassFlags((LPIDFOLDER)apidl[0], FALSE) & SHCF_IS_LINK)
gfOut |= SFGAO_LINK;
}
}
*rgfOut = gfOut;
return NOERROR;
}
int DataObjToFileOpString(IDataObject * pdtobj, LPTSTR * ppszSrc, LPTSTR * ppszDest)
{
int cItems = 0;
int i;
int cchSrc, cchDest;
LPTSTR lpszSrc, lpszDest;
STGMEDIUM medium;
LPIDA pida = DataObj_GetHIDA(pdtobj, &medium);
if (medium.hGlobal)
cItems = HIDA_GetCount(medium.hGlobal);
cchSrc = cchDest = 1;
if (ppszSrc)
lpszSrc = (void*)LocalAlloc(LPTR, 1);
if (ppszDest)
lpszDest = (void*)LocalAlloc(LPTR, 1);
for (i = 0 ; i < cItems ; i++) {
int cchSrcFile;
int cchDestFile;
TCHAR szBitBucket[MAX_PATH];
LPBBDATAENTRYIDA pbbidl;
LPCITEMIDLIST pidl;
pidl = IDA_GetIDListPtr(pida, i);
pbbidl = PIDLTODATAENTRYID(pidl);
if (ppszSrc) {
DriveIDToBBPath(pbbidl->bbde.idDrive, szBitBucket);
{
TCHAR szTmp[MAX_PATH];
FS_CopyName((LPIDFOLDER)pidl,szTmp,ARRAYSIZE(szTmp));
PathAppend(szBitBucket, szTmp);
}
cchSrcFile = lstrlen(szBitBucket) + 1;
lpszSrc = (void*)LocalReAlloc((HLOCAL)lpszSrc,
(cchSrc + cchSrcFile) * SIZEOF(TCHAR), LMEM_MOVEABLE|LMEM_ZEROINIT);
if (!lpszSrc) {
// out of memory!
// bugbug: do something real
if (ppszDest) {
LocalFree((HLOCAL)lpszDest);
}
cItems = 0;
goto Bail;
}
lstrcpy(lpszSrc + cchSrc - 1, szBitBucket);
cchSrc += cchSrcFile;
}
if (ppszDest) {
#ifdef UNICODE
if (pbbidl->cb == SIZEOF(BBDATAENTRYIDW)) {
LPBBDATAENTRYIDW pbbidlw = DATAENTRYIDATOW(pbbidl);
cchDestFile = ualstrlenW(pbbidlw->wszOriginal) + 1;
} else {
cchDestFile = lstrlenA(pbbidl->bbde.szOriginal) + 1;
}
#else
cchDestFile = lstrlen(pbbidl->bbde.szOriginal) + 1;
#endif
lpszDest = (void*)LocalReAlloc((HLOCAL)lpszDest,
(cchDest + cchDestFile) * SIZEOF(TCHAR), LMEM_MOVEABLE|LMEM_ZEROINIT);
if (!lpszDest) {
// out of memory!
// bugbug: do something real
if (ppszSrc) {
LocalFree((HLOCAL)lpszSrc);
}
cItems = 0;
goto Bail;
}
#ifdef UNICODE
if (pbbidl->cb == SIZEOF(BBDATAENTRYIDW)) {
LPBBDATAENTRYIDW pbbidlw = DATAENTRYIDATOW(pbbidl);
ualstrcpy(lpszDest + cchDest - 1, pbbidlw->wszOriginal);
} else {
MultiByteToWideChar(CP_ACP, 0,
pbbidl->bbde.szOriginal, -1,
lpszDest + cchDest - 1, cchDestFile );
}
#else
lstrcpy(lpszDest + cchDest - 1, pbbidl->bbde.szOriginal);
#endif
cchDest += cchDestFile;
}
}
if (ppszSrc) {
*ppszSrc = lpszSrc;
}
if (ppszDest) {
*ppszDest = lpszDest;
}
Bail:
if (medium.hGlobal)
HIDA_ReleaseStgMedium(pida, &medium);
return cItems;
}
void BBCheckRestoredFiles(LPTSTR lpszSrc)
{
LPTSTR lpszTemp;
if (lpszSrc && IsFileInBitBucket(lpszSrc)) {
lpszTemp = lpszSrc;
while (*lpszTemp) {
FOUndo_FileRestored(lpszTemp);
lpszTemp += (lstrlen(lpszTemp) + 1);
}
SHUpdateRecycleBinIcon();
}
}
void BBRestore(LPSHELLBITBUCKET this, HWND hwndOwner, IDataObject * pdtobj)
{
LPTSTR lpszSrc;
LPTSTR lpszDest;
if (DataObjToFileOpString(pdtobj, &lpszSrc, &lpszDest))
{
// now do the actual restore.
SHFILEOPSTRUCT sFileOp =
{
hwndOwner,
FO_MOVE,
lpszSrc,
lpszDest,
FOF_MULTIDESTFILES | FOF_SIMPLEPROGRESS | FOF_NOCONFIRMMKDIR,
FALSE,
NULL,
(LPCTSTR)IDS_BB_RESTORINGFILES
} ;
DECLAREWAITCURSOR;
SetWaitCursor();
SHFileOperation(&sFileOp);
SHChangeNotifyHandleEvents();
BBCheckRestoredFiles(lpszSrc);
LocalFree((HLOCAL)lpszSrc);
LocalFree((HLOCAL)lpszDest);
BitBucketFlushCacheCheckPurge(TRUE);
ResetWaitCursor();
}
}
typedef BOOL (WINAPI *PLAYSOUNDFN)(LPCTSTR lpsz, HANDLE hMod, DWORD dwFlags);
typedef UINT (WINAPI *UINTVOIDFN)();
TCHAR const c_szWinMMDll[] = TEXT("winmm.dll");
#ifdef UNICODE
char const c_szPlaySound[] = "PlaySoundW";
#else
char const c_szPlaySound[] = "PlaySoundA";
#endif
char const c_szwaveOutGetNumDevs[] = "waveOutGetNumDevs";
TCHAR const c_szSoundAliasRegKey[] = TEXT("AppEvents\\Schemes\\Apps");
TCHAR const c_szDotCurrent[] = TEXT(".current");
extern TCHAR const c_szExplorer[];
void SHPlaySound(LPCTSTR lpszName)
{
DWORD cbSize = 0;
TCHAR szKey[CCH_KEYMAX];
// check the registry first
// if there's nothing registered, we blow off the play,
// but we don't set the MM_DONTLOAD flag so taht if they register
// something we will play it
wsprintf(szKey, c_szSSSS, c_szSoundAliasRegKey, c_szExplorer, lpszName, c_szDotCurrent);
if ((RegQueryValue(HKEY_CURRENT_USER, szKey, NULL, &cbSize) == ERROR_SUCCESS) &&
cbSize > sizeof(TCHAR)) {
if (g_hinstMM != MM_DONTLOAD) {
PLAYSOUNDFN pfnPlaySound;
HINSTANCE hinst;
UINTVOIDFN pfnwaveOutGetNumDevs;
// have we been loaded?
if (!g_hinstMM) {
hinst = LoadLibrary(c_szWinMMDll);
pfnwaveOutGetNumDevs = (UINTVOIDFN)GetProcAddress(hinst, c_szwaveOutGetNumDevs);
if (pfnwaveOutGetNumDevs && pfnwaveOutGetNumDevs()) {
g_hinstMM = hinst;
} else {
FreeLibrary(hinst);
g_hinstMM = MM_DONTLOAD;
return;
}
}
pfnPlaySound = (PLAYSOUNDFN)GetProcAddress(g_hinstMM, c_szPlaySound);
if (pfnPlaySound) {
pfnPlaySound(lpszName, NULL, SND_ALIAS | SND_APPLICATION | SND_ASYNC);
} else {
FreeLibrary(hinst);
g_hinstMM = MM_DONTLOAD;
}
}
}
}
// returns the number of files in the bitbucket, and optionally the drive id
// if there's only one file.
int BBTotalCount(LPINT pidDrive)
{
int i;
int idDrive;
int nFiles = 0;
for (i = 0; i < MAX_BITBUCKETS; i++) {
int nFilesOld = nFiles;
nFiles += BBDeletedFilesOnDrive(i);
if (nFilesOld == 0 && nFiles == 1) {
idDrive = i;
} else if (nFiles != 1) {
idDrive = -1;
}
}
if (pidDrive)
*pidDrive = idDrive;
return nFiles;
}
TCHAR const c_szBitBucketFlush[] = TEXT("EmptyRecycleBin");
void ResetDriveInfoStruct(LPBBDRIVEINFO lpbbdi)
{
int j;
if (lpbbdi) {
ENTERCRITICAL;
// clean out g_pBitBuckets of these files;
// nothing fancy, just free it.
if (lpbbdi->hdpaDeleteCache) {
int iMax;
iMax = DPA_GetPtrCount(lpbbdi->hdpaDeleteCache);
for (j = 0 ; j < iMax; j++) {
Free(DPA_FastGetPtr(lpbbdi->hdpaDeleteCache, j));
}
DPA_Destroy(lpbbdi->hdpaDeleteCache);
lpbbdi->hdpaDeleteCache = NULL;
}
// free read cache.
if (lpbbdi->lpbbdeRead) {
Free(lpbbdi->lpbbdeRead);
lpbbdi->lpbbdeRead = NULL;
lpbbdi->cReadCount = 0;
lpbbdi->fReadDirty = FALSE;
}
lpbbdi->cFiles = 0;
lpbbdi->dwSize = 0;
LEAVECRITICAL;
}
}
void BBPurgeAll(LPSHELLBITBUCKET this, HWND hwndOwner)
{
int i;
TCHAR szPath[MAX_PATH * 2 + 1];
int nFiles;
int idDrive;
SHFILEOPSTRUCT sFileOp =
{
hwndOwner,
FO_DELETE,
szPath,
NULL,
FOF_NOCONFIRMATION | FOF_SIMPLEPROGRESS,
FALSE,
NULL,
(LPCTSTR)IDS_BB_EMPTYINGWASTEBASKET
} ;
CONFIRM_DATA cd = {
CONFIRM_DELETE_FILE | CONFIRM_DELETE_FOLDER | CONFIRM_MULTIPLE,
0
};
WIN32_FIND_DATA fd;
LPTSTR lpszSrc;
LPBBDRIVEINFO lpbbdi;
TCHAR szSrcName[MAX_PATH];
// first do the confirmation thing
fd.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
// find out how many files we have...
nFiles = BBTotalCount(&idDrive);
if (!nFiles) {
return; // no files to delete
}
if (nFiles == 1) {
int iMax;
Assert(idDrive != -1);
iMax = BBLoadFileData(idDrive);
if (iMax == 0)
{
lpszSrc = (LPTSTR)c_szNULL; // Go with no name (try delete later)
}
else
{
lpbbdi = g_pBitBucket[idDrive];
if ( lpbbdi->cbDataEntrySize == SIZEOF(BBDATAENTRYW)) {
LPBBDATAENTRYW lpbbdew = (LPBBDATAENTRYW)&lpbbdi->lpbbdeRead[0];
#ifdef UNICODE
lpszSrc = lpbbdew->szOriginal;
#else
WideCharToMultiByte(CP_ACP, 0,
lpbbdew->szOriginal, -1,
szSrcName, ARRAYSIZE(szSrcName),
NULL, NULL);
lpszSrc = szSrcName;
#endif
} else {
LPBBDATAENTRYA lpbbdea = (LPBBDATAENTRYA)&lpbbdi->lpbbdeRead[0];
#ifdef UNICODE
MultiByteToWideChar(CP_ACP, 0,
lpbbdea->szOriginal, -1,
szSrcName, ARRAYSIZE(szSrcName));
lpszSrc = szSrcName;
#else
lpszSrc = lpbbdea->szOriginal;
#endif
}
}
} else {
lpszSrc = (LPTSTR)c_szNULL;
}
if (ConfirmFileOp(hwndOwner, NULL, &cd, nFiles, 0, CONFIRM_DELETE_FILE | CONFIRM_WASTEBASKET_PURGE, lpszSrc, &fd, NULL, &fd) == IDYES) {
DECLAREWAITCURSOR;
SetWaitCursor();
for (i = 0; i < MAX_BITBUCKETS; i++ ) {
LPBBDRIVEINFO lpbbdi;
lpbbdi = g_pBitBucket[i];
ResetDriveInfoStruct(lpbbdi);
if (lpbbdi) {
LPTSTR lpszTemp;
DriveIDToBBPath(i, szPath);
PathAppend(szPath, c_szDStarDotStar);
lpszTemp = szPath + lstrlen(szPath) + 1;
DriveIDToBBPath(i, lpszTemp);
PathAppend(lpszTemp, c_szStarDotStar);
lpszTemp[lstrlen(lpszTemp) + 1] = 0; // double null terminate
// turn off redraw for now.
ShellFolderView_SetRedraw(hwndOwner, FALSE);
// now do the actual delete.
SHFileOperation(&sFileOp);
ShellFolderView_SetRedraw(hwndOwner, TRUE);
// this will put the desktop ini back... we didn't
// delete the directory itself so we don't do a MakeBitBucket
InitializeRecycledDirectory(i);
}
}
SHPlaySound(c_szBitBucketFlush);
SHUpdateRecycleBinIcon();
ResetWaitCursor();
}
}
void BBPurge(LPSHELLBITBUCKET this, HWND hwndOwner, IDataObject * pdtobj)
{
LPTSTR lpszSrc;
LPTSTR lpszDest;
int iItems;
int nFiles = DataObjToFileOpString(pdtobj, &lpszSrc, &lpszDest);
if (nFiles)
{
// now do the actual restore.
SHFILEOPSTRUCT sFileOp =
{
hwndOwner,
FO_DELETE,
lpszSrc,
NULL,
FOF_NOCONFIRMATION | FOF_SIMPLEPROGRESS,
FALSE,
NULL,
(LPCTSTR)IDS_BB_DELETINGWASTEBASKETFILES
} ;
CONFIRM_DATA cd = {
CONFIRM_DELETE_FILE | CONFIRM_MULTIPLE,
0
};
WIN32_FIND_DATA fd;
DECLAREWAITCURSOR;
SetWaitCursor();
fd.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
if (ConfirmFileOp(hwndOwner, NULL, &cd, nFiles, 0, CONFIRM_DELETE_FILE | CONFIRM_WASTEBASKET_PURGE, lpszDest, &fd, NULL, &fd) == IDYES)
{
SHFileOperation(&sFileOp);
// clean out g_pBitBuckets of these files;
BBNukeFileList(lpszSrc);
SHChangeNotifyHandleEvents();
// update the icon if there are objects left in the list
iItems = SHShellFolderView_Message(hwndOwner, SFVM_GETOBJECTCOUNT, 0L);
UpdateIcon(iItems);
}
LocalFree((HLOCAL)lpszSrc);
LocalFree((HLOCAL)lpszDest);
ResetWaitCursor();
}
}
typedef struct _bbpropsheetinfo {
// Magic prop sheet stuff... see Eric Flo
#ifdef UNICODE
DWORD dwInternalFlags;
LPVOID lpANSIPage;
#endif
PROPSHEETPAGE psp;
int idDrive;
ULONGLONG cbDiskSpace; // disk space on idDrive;
// "globals"
BOOL fGlobal; // use one global setting
DWORD dwChanged; // one of the pages has changed. bitfield.. global set by each page
int iPercent;
int iOldPercent;
struct _bbpropsheetinfo *pGlobal;
} BBPROPSHEETINFO, *LPBBPROPSHEETINFO;
void EnableTrackbarAndFamily(HWND hDlg, BOOL f)
{
EnableWindow(GetDlgItem(hDlg, IDC_BBSIZE), f);
EnableWindow(GetDlgItem(hDlg, IDC_BBSIZETEXT), f);
EnableWindow(GetDlgItem(hDlg, IDC_TEXT), f);
}
void _BB_PropOnCommand(HWND hDlg, int id, HWND hwndCtl,
UINT codeNotify)
{
LPBBPROPSHEETINFO lppsi = (LPBBPROPSHEETINFO)GetWindowLong(hDlg, DWL_USER);
switch (id)
{
case IDC_GLOBAL:
case IDC_INDEPENDENT:
SendMessage(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0L);
lppsi->fGlobal = IsDlgButtonChecked(hDlg, IDC_GLOBAL) ? 1 : 0;
EnableWindow(GetDlgItem(hDlg, IDC_NUKEONDELETE), lppsi->fGlobal);
EnableTrackbarAndFamily(hDlg, lppsi->fGlobal && !IsDlgButtonChecked(hDlg, IDC_NUKEONDELETE));
EnableWindow(GetDlgItem(hDlg, IDC_CONFIRMDELETE), !lppsi->fGlobal || !IsDlgButtonChecked(hDlg, IDC_NUKEONDELETE));
break;
case IDC_NUKEONDELETE:
EnableTrackbarAndFamily(hDlg, !IsDlgButtonChecked(hDlg, IDC_NUKEONDELETE));
EnableWindow(GetDlgItem(hDlg, IDC_CONFIRMDELETE), !lppsi->fGlobal || !IsDlgButtonChecked(hDlg, IDC_NUKEONDELETE));
case IDC_CONFIRMDELETE:
SendMessage(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0L);
break;
}
}
void RelayMessageToChildren(HWND hwnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
{
HWND hwndChild;
for (hwndChild = GetWindow(hwnd, GW_CHILD); hwndChild != NULL;
hwndChild = GetWindow(hwndChild, GW_HWNDNEXT))
{
SendMessage(hwndChild, uMessage, wParam, lParam);
}
}
#pragma data_seg(DATASEG_READONLY)
const static DWORD aBitBucketPropHelpIDs[] = { // Context Help IDs
IDD_ATTR_GROUPBOX, IDH_COMM_GROUPBOX,
IDC_INDEPENDENT, IDH_RECYCLE_CONFIG_INDEP,
IDC_GLOBAL, IDH_RECYCLE_CONFIG_ALL,
IDC_DISKSIZE, IDH_RECYCLE_DRIVE_SIZE,
IDC_DISKSIZEDATA, IDH_RECYCLE_DRIVE_SIZE,
IDC_BYTESIZE, IDH_RECYCLE_BIN_SIZE,
IDC_BYTESIZEDATA, IDH_RECYCLE_BIN_SIZE,
IDC_NUKEONDELETE, IDH_RECYCLE_PURGE_ON_DEL,
IDC_BBSIZE, IDH_RECYCLE_MAX_SIZE,
IDC_BBSIZETEXT, IDH_RECYCLE_MAX_SIZE,
IDC_CONFIRMDELETE, IDH_DELETE_CONFIRM_DLG,
IDC_TEXT, NO_HELP,
0, 0
};
#pragma data_seg()
BOOL CALLBACK BBGenPropDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
LPBBPROPSHEETINFO lppsi = (LPBBPROPSHEETINFO)GetWindowLong(hDlg, DWL_USER);
switch (uMsg) {
HANDLE_MSG(hDlg, WM_COMMAND, _BB_PropOnCommand);
case WM_INITDIALOG: {
HWND hwndTrack = GetDlgItem(hDlg, IDC_BBSIZE);
BBDRIVEINFO bbdi;
SHELLSTATE ss;
lppsi = (LPBBPROPSHEETINFO)lParam;
SetWindowLong(hDlg, DWL_USER, lParam);
lppsi->fGlobal = (BitBucketLoadRegistryInfo(&bbdi, MAX_BITBUCKETS, NULL) ? 1 : 0);
lppsi->iOldPercent = bbdi.iPercent;
SendMessage(hwndTrack, TBM_SETTICFREQ, 10, 0);
SendMessage(hwndTrack, TBM_SETRANGE, FALSE, MAKELONG(0, 100));
SendMessage(hwndTrack, TBM_SETPOS, TRUE, bbdi.iPercent);
EnableWindow(GetDlgItem(hDlg, IDC_NUKEONDELETE), lppsi->fGlobal);
EnableTrackbarAndFamily(hDlg, lppsi->fGlobal && !bbdi.fNukeOnDelete);
CheckDlgButton(hDlg, IDC_NUKEONDELETE, bbdi.fNukeOnDelete);
CheckRadioButton(hDlg, IDC_INDEPENDENT, IDC_GLOBAL, lppsi->fGlobal ? IDC_GLOBAL : IDC_INDEPENDENT);
SHGetSetSettings(&ss, SSF_NOCONFIRMRECYCLE, FALSE);
CheckDlgButton(hDlg, IDC_CONFIRMDELETE, !ss.fNoConfirmRecycle);
EnableWindow(GetDlgItem(hDlg, IDC_CONFIRMDELETE), !lppsi->fGlobal || !bbdi.fNukeOnDelete);
}
// fall through
case WM_HSCROLL: {
TCHAR szPercent[20];
int iPercent;
HWND hwndTrack = GetDlgItem(hDlg, IDC_BBSIZE);
iPercent = SendMessage(hwndTrack, TBM_GETPOS, 0, 0);
wsprintf(szPercent, TEXT("%d%%"),iPercent);
SetDlgItemText(hDlg, IDC_BBSIZETEXT, szPercent);
lppsi->iPercent = iPercent;
if (iPercent != lppsi->iOldPercent) {
SendMessage(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0L);
}
return TRUE;
}
case WM_HELP:
WinHelp((HWND)((LPHELPINFO) lParam)->hItemHandle, NULL,
HELP_WM_HELP, (DWORD)(LPTSTR) aBitBucketPropHelpIDs);
return TRUE;
case WM_CONTEXTMENU:
WinHelp((HWND) wParam, NULL, HELP_CONTEXTMENU,
(DWORD)(LPVOID) aBitBucketPropHelpIDs);
return TRUE;
case WM_WININICHANGE:
case WM_SYSCOLORCHANGE:
RelayMessageToChildren(hDlg, uMsg, wParam, lParam);
break;
case WM_DESTROY:
{
int i;
if (!lppsi->fGlobal) {
for (i = 0; i < MAX_BITBUCKETS; i++) {
if (g_pBitBucket[i]) {
BBCheckPurgeFiles(i);
}
}
}
break;
}
case WM_NOTIFY:
switch (((NMHDR *)lParam)->code) {
case PSN_APPLY: {
// save settings here
int i;
SHELLSTATE ss;
int iPercent = SendMessage( GetDlgItem(hDlg, IDC_BBSIZE), TBM_GETPOS, 0, 0L);
BOOL fNukeOnDelete = IsDlgButtonChecked(hDlg, IDC_NUKEONDELETE) ? 1 : 0;
BBSaveRegistryStuff(MAX_BITBUCKETS, iPercent, fNukeOnDelete, lppsi->fGlobal);
// reload all the fields. (psn_apply set the info into the registry only)
// and do any purging necessary due to new states
for (i = 0; i < MAX_BITBUCKETS; i++) {
if (g_pBitBucket[i]) {
BitBucketGetDriveSettings(g_pBitBucket[i], i, NULL);
// only check purge now if we're global.. otherwise
// do it after everyone else gets a chance to set their value
if (lppsi->fGlobal) {
BBCheckPurgeFiles(i);
}
}
}
SHUpdateRecycleBinIcon();
ss.fNoConfirmRecycle = !IsDlgButtonChecked(hDlg, IDC_CONFIRMDELETE);
SHGetSetSettings(&ss, SSF_NOCONFIRMRECYCLE, TRUE);
break;
}
case PSN_KILLACTIVE:
case PSN_SETACTIVE:
// SetDlgMsgResult(hDlg, WM_NOTIFY, 0); <-- Done below
break;
}
SetDlgMsgResult(hDlg, WM_NOTIFY, 0);
return TRUE;
}
return FALSE;
}
BOOL CALLBACK BBPropDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
LPBBPROPSHEETINFO lppsi = (LPBBPROPSHEETINFO)GetWindowLong(hDlg, DWL_USER);
TCHAR szDiskSpace[40];
HWND hwndTrack;
switch (uMsg) {
case WM_INITDIALOG: {
BBDRIVEINFO bbdi;
hwndTrack = GetDlgItem(hDlg, IDC_BBSIZE);
lppsi = (LPBBPROPSHEETINFO)lParam;
SetWindowLong(hDlg, DWL_USER, lParam);
lppsi->fGlobal = (BitBucketLoadRegistryInfo(&bbdi, lppsi->idDrive, &lppsi->cbDiskSpace) ? 1 : 0);
lppsi->iOldPercent = bbdi.iPercent;
SendMessage(hwndTrack, TBM_SETTICFREQ, 10, 0);
SendMessage(hwndTrack, TBM_SETRANGE, FALSE, MAKELONG(0, 100));
SendMessage(hwndTrack, TBM_SETPOS, TRUE, bbdi.iPercent);
CheckDlgButton(hDlg, IDC_NUKEONDELETE, bbdi.fNukeOnDelete);
// set the disk space info
ShortSizeFormat64(lppsi->cbDiskSpace, szDiskSpace);
SetDlgItemText(hDlg, IDC_DISKSIZEDATA, szDiskSpace);
ShortSizeFormat64(bbdi.iPercent * (lppsi->cbDiskSpace/100), szDiskSpace);
SetDlgItemText(hDlg, IDC_BYTESIZEDATA, szDiskSpace);
wParam = 0;
}
// fall through
case WM_HSCROLL: {
int iPercent;
hwndTrack = GetDlgItem(hDlg, IDC_BBSIZE);
iPercent = SendMessage(hwndTrack, TBM_GETPOS, 0, 0);
ShortSizeFormat64(iPercent * (lppsi->cbDiskSpace/100), szDiskSpace);
SetDlgItemText(hDlg, IDC_BYTESIZEDATA, szDiskSpace);
wsprintf(szDiskSpace, TEXT("%d%%"),iPercent);
SetDlgItemText(hDlg, IDC_BBSIZETEXT, szDiskSpace);
if (iPercent != lppsi->iOldPercent) {
SendMessage(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0L);
}
return TRUE;
}
case WM_HELP:
WinHelp((HWND)((LPHELPINFO) lParam)->hItemHandle, NULL,
HELP_WM_HELP, (DWORD)(LPTSTR) aBitBucketPropHelpIDs);
return TRUE;
case WM_CONTEXTMENU:
WinHelp((HWND) wParam, NULL, HELP_CONTEXTMENU,
(DWORD)(LPVOID) aBitBucketPropHelpIDs);
return TRUE;
case WM_COMMAND:
switch (GET_WM_COMMAND_ID(wParam, lParam)) {
case IDC_NUKEONDELETE:
SendMessage(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0L);
EnableTrackbarAndFamily(hDlg, !IsDlgButtonChecked(hDlg, IDC_NUKEONDELETE));
EnableWindow(GetDlgItem(hDlg, IDC_BYTESIZE), !IsDlgButtonChecked(hDlg, IDC_NUKEONDELETE));
EnableWindow(GetDlgItem(hDlg, IDC_BYTESIZEDATA), !IsDlgButtonChecked(hDlg, IDC_NUKEONDELETE));
break;
}
break;
case WM_NOTIFY:
switch (((NMHDR *)lParam)->code) {
case PSN_APPLY:
{
// MUST use 1 and 0 because we do == checks with them
BOOL fNukeOnDelete = IsDlgButtonChecked(hDlg, IDC_NUKEONDELETE) ? 1 : 0;
int iPercent;
hwndTrack = GetDlgItem(hDlg, IDC_BBSIZE);
iPercent = SendMessage( hwndTrack, TBM_GETPOS, 0, 0L);
BBSaveRegistryStuff(lppsi->idDrive, iPercent, fNukeOnDelete, 0);
BitBucketGetDriveSettings(g_pBitBucket[lppsi->idDrive], lppsi->idDrive, NULL);
// save settings here
break;
}
case PSN_SETACTIVE:
EnableWindow(GetDlgItem(hDlg, IDC_NUKEONDELETE), !lppsi->pGlobal->fGlobal);
EnableTrackbarAndFamily(hDlg, (!lppsi->pGlobal->fGlobal) && (!IsDlgButtonChecked(hDlg, IDC_NUKEONDELETE)));
EnableWindow(GetDlgItem(hDlg, IDC_BYTESIZE), !IsDlgButtonChecked(hDlg, IDC_NUKEONDELETE));
EnableWindow(GetDlgItem(hDlg, IDC_BYTESIZEDATA), !IsDlgButtonChecked(hDlg, IDC_NUKEONDELETE));
SendMessage(hDlg, WM_HSCROLL, 0, 0);
break;
}
SetDlgMsgResult(hDlg, WM_NOTIFY, 0);
return TRUE;
}
return FALSE;
}
typedef struct {
PROPSHEETPAGE psp;
HIDA hida;
int i;
LPCITEMIDLIST pidl;
} BBFILEPROPINFO;
#pragma data_seg(DATASEG_READONLY)
const static DWORD aBitBucketHelpIDs[] = { // Context Help IDs
IDD_LINE_1, NO_HELP,
IDD_LINE_2, NO_HELP,
IDD_ITEMICON, IDH_FPROP_GEN_ICON,
IDD_NAME, IDH_FPROP_GEN_NAME,
IDD_FILETYPE_TXT, IDH_FPROP_GEN_TYPE,
IDD_FILETYPE, IDH_FPROP_GEN_TYPE,
IDD_FILESIZE_TXT, IDH_FPROP_GEN_SIZE,
IDD_FILESIZE, IDH_FPROP_GEN_SIZE,
IDD_LOCATION_TXT, IDH_FCAB_DELFILEPROP_LOCATION,
IDD_LOCATION, IDH_FCAB_DELFILEPROP_LOCATION,
IDD_DELETED_TXT, IDH_FCAB_DELFILEPROP_DELETED,
IDD_DELETED, IDH_FCAB_DELFILEPROP_DELETED,
IDD_CREATED_TXT, IDH_FPROP_GEN_DATE_CREATED,
IDD_CREATED, IDH_FPROP_GEN_DATE_CREATED,
IDD_READONLY, IDH_FCAB_DELFILEPROP_READONLY,
IDD_HIDDEN, IDH_FCAB_DELFILEPROP_HIDDEN,
IDD_ARCHIVE, IDH_FCAB_DELFILEPROP_ARCHIVE,
IDD_SYSTEM, IDH_FCAB_DELFILEPROP_SYSTEM,
IDD_COMPRESSED, IDH_FCAB_DELFILEPROP_COMPRESSED,
IDD_ATTR_GROUPBOX, IDH_COMM_GROUPBOX,
0, 0
};
#pragma data_seg()
BOOL CALLBACK BBFilePropDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
BBFILEPROPINFO * pbbfpi = (BBFILEPROPINFO *)GetWindowLong(hDlg, DWL_USER);
TCHAR szTemp[MAX_PATH];
switch (uMsg) {
case WM_INITDIALOG:
{
LPCITEMIDLIST pidl;
LPBBDATAENTRYIDA pbbidl;
HICON hIcon;
ULONGLONG cbSize;
pbbfpi = (BBFILEPROPINFO *)lParam;
SetWindowLong(hDlg, DWL_USER, lParam);
pidl = pbbfpi->pidl;
pbbidl = PIDLTODATAENTRYID(pidl);
// Name
#ifdef UNICODE
if (pbbidl->cb == SIZEOF(BBDATAENTRYIDW)) {
LPBBDATAENTRYIDW pbbidlw = DATAENTRYIDATOW(pbbidl);
ualstrcpyn(szTemp, pbbidlw->wszOriginal, ARRAYSIZE(szTemp));
} else {
MultiByteToWideChar(CP_ACP, 0,
pbbidl->bbde.szOriginal, -1,
szTemp, ARRAYSIZE(szTemp));
}
#else
lstrcpyn(szTemp, pbbidl->bbde.szOriginal, ARRAYSIZE(szTemp));
#endif
#ifdef WINNT
//
// We don't allow user to change compression attribute on a deleted file
// but we do show the current compressed state
//
{
TCHAR szRoot[_MAX_PATH + 1];
DWORD dwVolumeFlags = 0;
//
// If file's volume doesn't support compression, don't show
// "Compressed" checkbox.
// If compression is supported, show the checkbox and check/uncheck
// it to indicate compression state of the file.
// Perform this operation while szTemp contains complete path name.
//
lstrcpy(szRoot, szTemp);
PathQualify(szRoot);
PathStripToRoot(szRoot);
if (GetVolumeInformation(szRoot, NULL, 0, NULL, NULL, &dwVolumeFlags, NULL, 0) &&
(dwVolumeFlags & FS_FILE_COMPRESSION))
{
DWORD dwFileAttributes = 0;
if ( ((dwFileAttributes = GetFileAttributes(szTemp)) != (DWORD)-1) &&
(dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED))
{
CheckDlgButton(hDlg, IDD_COMPRESSED, 1);
}
ShowWindow(GetDlgItem(hDlg, IDD_COMPRESSED), SW_SHOW);
}
}
#else
//
// Win95 doesn't use Compressed checkbox in property page.
//
#endif
PathRemoveExtension(szTemp);
SetDlgItemText(hDlg, IDD_NAME, PathFindFileName(szTemp));
// origin
SetDlgItemText(hDlg, IDD_LOCATION, PathFindFileName(szTemp));
// Type
FS_GetTypeName((LPIDFOLDER)pidl, szTemp, ARRAYSIZE(szTemp));
SetDlgItemText(hDlg, IDD_FILETYPE, szTemp);
// Size
if (FS_IsFolderI((LPIDFOLDER)pidl))
cbSize = pbbidl->bbde.dwSize;
else
FS_GetSize(NULL, (LPIDFOLDER)pidl, &cbSize);
ShortSizeFormat64(cbSize, szTemp);
SetDlgItemText(hDlg, IDD_FILESIZE, szTemp);
// deleted time
{
FILETIME ft = pbbidl->bbde.ft;
SetDateTimeText(hDlg, IDD_DELETED, &ft);
}
{
HANDLE hfind;
WIN32_FIND_DATA fd;
#ifdef UNICODE
WCHAR szOriginal[MAX_PATH];
LPNTSTR lpszOriginal;
#endif
// creation time
#ifdef UNICODE
if (pbbidl->cb == SIZEOF(BBDATAENTRYIDW)) {
LPBBDATAENTRYIDW pbbidlw = DATAENTRYIDATOW(pbbidl);
lpszOriginal = pbbidlw->wszOriginal;
} else {
MultiByteToWideChar(CP_ACP, 0,
pbbidl->bbde.szOriginal, -1,
szOriginal, ARRAYSIZE(szOriginal));
lpszOriginal = szOriginal;
}
DriveIDToBBPath(BBPathGetDriveNumber(lpszOriginal), szTemp);
#else
DriveIDToBBPath(BBPathGetDriveNumber(pbbidl->bbde.szOriginal), szTemp);
#endif
{
TCHAR szName[MAX_PATH];
FS_CopyName((LPIDFOLDER)pidl,szName,ARRAYSIZE(szName));
PathAppend(szTemp, szName);
}
hfind = FindFirstFile(szTemp, &fd);
if (hfind != INVALID_HANDLE_VALUE)
{
SetDateTimeText(hDlg, IDD_CREATED, &fd.ftCreationTime);
FindClose(hfind);
}
// file attributes
if (fd.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
CheckDlgButton(hDlg, IDD_READONLY, 1);
if (fd.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)
CheckDlgButton(hDlg, IDD_ARCHIVE, 1);
if (fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
CheckDlgButton(hDlg, IDD_HIDDEN, 1);
if (fd.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)
CheckDlgButton(hDlg, IDD_SYSTEM, 1);
// icon
if (NULL != (hIcon = SHGetFileIcon(NULL, szTemp, fd.dwFileAttributes, SHGFI_LARGEICON|SHGFI_USEFILEATTRIBUTES)))
{
hIcon = (HICON)SendDlgItemMessage(hDlg, IDD_ITEMICON, STM_SETICON, (WPARAM)hIcon, 0L);
if (hIcon)
DestroyIcon(hIcon);
}
}
break;
}
case WM_WININICHANGE:
case WM_SYSCOLORCHANGE:
RelayMessageToChildren(hDlg, uMsg, wParam, lParam);
break;
case WM_NOTIFY:
switch (((NMHDR *)lParam)->code) {
case PSN_APPLY:
case PSN_SETACTIVE:
case PSN_KILLACTIVE:
return TRUE;
}
break;
case WM_HELP:
WinHelp((HWND)((LPHELPINFO) lParam)->hItemHandle, NULL,
HELP_WM_HELP, (DWORD)(LPTSTR) aBitBucketHelpIDs);
return TRUE;
case WM_CONTEXTMENU:
WinHelp((HWND) wParam, NULL, HELP_CONTEXTMENU,
(DWORD)(LPVOID) aBitBucketHelpIDs);
return TRUE;
}
return FALSE;
}
BOOL _BBDeleteFile(LPCTSTR szPath, DWORD dwAttribs)
{
BOOL fRet = FALSE;
// verify that the file exists
// clear readonly and system bit if set
if (dwAttribs & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM)) {
SetFileAttributes(szPath, dwAttribs & ~(FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM));
}
fRet = Win32DeleteFile(szPath);
// if everything was successful, we need to notify any undo stuff about this
if (fRet) {
FOUndo_FileReallyDeleted((LPTSTR)szPath);
}
return fRet;
}
BOOL BBDeleteFolder(LPCTSTR pszDir)
{
TCHAR szPath[MAX_PATH];
WIN32_FIND_DATA fd;
BOOL fRet;
if (PathCombine(szPath, pszDir, c_szStarDotStar))
{
HANDLE hfind = FindFirstFile(szPath, &fd);
if (hfind != INVALID_HANDLE_VALUE)
{
do
{
LPTSTR lpszFileName;
lpszFileName = fd.cAlternateFileName[0] ?
fd.cAlternateFileName : fd.cFileName;
if (lpszFileName[0] != TEXT('.'))
{
// use the short path name so that we
// don't have to worry about max path
PathCombine(szPath, pszDir, lpszFileName);
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
// even if this fails, we keep going.
// we want to delete as much as possible
BBDeleteFolder(szPath);
}
else
{
_BBDeleteFile(szPath, fd.dwFileAttributes);
}
}
} while (FindNextFile(hfind, &fd));
FindClose(hfind);
}
}
fRet = Win32RemoveDirectory(pszDir);
// if everything was successful, we need to notify any undo stuff about this
if (fRet) {
FOUndo_FileReallyDeleted((LPTSTR)szPath);
}
return fRet;
}
BOOL BBDelete(LPCTSTR szPath)
{
BOOL fRet = FALSE;
// verify that the file exists
DWORD dwAttribs = GetFileAttributes(szPath);
if (dwAttribs != (UINT)-1) {
// this was a directory, we need to recurse in and delete everything inside
if (dwAttribs & FILE_ATTRIBUTE_DIRECTORY) {
fRet = BBDeleteFolder(szPath);
} else {
fRet = _BBDeleteFile(szPath, dwAttribs);
}
}
return fRet;
}
// Passed a list of bitbucket filenames to delete, deletes them
//
// Created as a new thread from BBCheckPurgeFiles
DWORD BBNukemThread(LPVOID pVoid)
{
TCHAR ** apszFilesToNuke = (TCHAR **) pVoid;
while(*apszFilesToNuke)
{
BBDelete(*apszFilesToNuke);
LocalFree(*apszFilesToNuke);
apszFilesToNuke++;
}
LocalFree(pVoid);
return 0;
}
#define BitBucketDeleteCacheIsDirty(lpbbdi) ((lpbbdi)->hdpaDeleteCache && DPA_GetPtrCount((lpbbdi)->hdpaDeleteCache))
DWORD BBCheckPurgeFiles(int idDrive)
{
LPBBDRIVEINFO lpbbdi = g_pBitBucket[idDrive];
TCHAR szPath[MAX_PATH];
DWORD dwLastKnownSize;
int iMax = 0;
if (!lpbbdi)
return 0;
dwLastKnownSize = lpbbdi->dwSize;
#ifdef BB_DEBUG
DebugMsg(DM_TRACE, TEXT("BBCheckPurgeFiles %d = idDrive, %d = iPercent, %d = fNukeOnDelete"), idDrive, lpbbdi->iPercent, lpbbdi->fNukeOnDelete);
#endif
SaveDeletedFileInfo(lpbbdi, FALSE);
if (lpbbdi->dwSize > lpbbdi->cbMaxSize)
{
// Build a list of stuff to delete inside of the critical section,
// then delete it when we're done
TCHAR ** apszFilesToNuke;
INT cFilesNuked = 0;
INT cOldReadCount;
iMax = BBLoadFileData(idDrive);
cOldReadCount = lpbbdi->cReadCount;
// Couldn't read the database, so invalidate it and bail
if (NULL == lpbbdi->lpbbdeRead)
{
Assert(0);
lpbbdi->dwSize = 0;
return 0;
}
// Allocate on extra slot to ensure the list is NUL terminated
apszFilesToNuke = (TCHAR **) LocalAlloc(LPTR, (iMax + 1) * SIZEOF(TCHAR *));
if (NULL == apszFilesToNuke)
{
// Not enough memory to purge right now, return current size
return lpbbdi->dwSize;
}
ENTERCRITICAL;
// Walk the list of files in the database from bottom (oldest)
// up, and delete stuff until we've cleared out enough room
// or run out of things to delete
while (lpbbdi->dwSize > lpbbdi->cbMaxSize && cFilesNuked < cOldReadCount)
{
TCHAR szFile[MAX_PATH];
LPBBDATAENTRY lpbbde;
LPTSTR lpszOriginal;
lpbbde = (LPBBDATAENTRY) ((BYTE *)lpbbdi->lpbbdeRead + lpbbdi->cbDataEntrySize * cFilesNuked);
// get the file name that we're going to nuke
if ( lpbbdi->cbDataEntrySize == SIZEOF(BBDATAENTRYW))
{
LPBBDATAENTRYW lpbbdew = (LPBBDATAENTRYW)lpbbde;
#ifdef UNICODE
lpszOriginal = lpbbdew->szOriginal;
#else
lpszOriginal = lpbbdew->szShortName;
#endif
}
else
{
LPBBDATAENTRYA lpbbdea = (LPBBDATAENTRYA)lpbbde;
#ifdef UNICODE
// Use szPath just to save stack
MultiByteToWideChar(CP_ACP, 0,
lpbbdea->szOriginal, -1,
szPath, ARRAYSIZE(szPath));
lpszOriginal = szPath;
#else
lpszOriginal = lpbbdea->szOriginal;
#endif
}
BBGetDeletedFileName(szFile, lpbbde->iIndex, lpszOriginal);
DriveIDToBBPath(idDrive, szPath);
PathAppend(szPath, szFile);
// Add this file to our list of stuff to be deleted
apszFilesToNuke[cFilesNuked] = LocalAlloc(LMEM_FIXED,
SIZEOF(TCHAR) * (lstrlen(szPath) + 1));
if (NULL == apszFilesToNuke[cFilesNuked])
{
// Mem alloc failure: break out and fix what we've done so far
break;
}
lstrcpy(apszFilesToNuke[cFilesNuked], szPath);
// This file goes, go update the counts, sizes, etc
if (lpbbdi->dwSize >= lpbbde->dwSize) // Remove it from the byte count
lpbbdi->dwSize -= lpbbde->dwSize;
if (lpbbdi->cReadCount > 0) // Dec entry count
lpbbdi->cReadCount--;
cFilesNuked++;
#ifdef BB_DEBUG
DebugMsg(DM_TRACE, TEXT("*** Auto purge of %s originally %s***"), szPath, lpbbde->szOriginal);
#endif
}
// Now we have cFile that we are going to nuke,
// Clean up the database records (count already have been updated)
if (cFilesNuked)
{
MoveMemory(lpbbdi->lpbbdeRead,
((BYTE *)lpbbdi->lpbbdeRead + lpbbdi->cbDataEntrySize * cFilesNuked),
lpbbdi->cbDataEntrySize * (cOldReadCount - cFilesNuked));
}
// It's real dirty now...
lpbbdi->fReadDirty = TRUE;
dwLastKnownSize = lpbbdi->dwSize;
// Its OK for others to party on the database now
LEAVECRITICAL;
// Spin off a thread to do the actual deletion of
// the orphaned bitbucket files
{
DWORD dwID;
HANDLE hThread = CreateThread(NULL,
0,
BBNukemThread,
apszFilesToNuke,
CREATE_SUSPENDED,
&dwID);
if (hThread)
{
SetThreadPriority(hThread, THREAD_PRIORITY_IDLE);
ResumeThread(hThread);
CloseHandle(hThread);
}
else
{
// If we don't have enough mem to start the thread,
// we'd better clean them up here. Only downside is
// that the bitbucket icon won't get updated until
// we're done and return from this function
BBNukemThread( (LPVOID) apszFilesToNuke );
}
}
}
// BUGBUG we left the critical section a while ago, so someone else
// might have changed the size on us since then'
return dwLastKnownSize;
}
// this goes through a null separated/ null terminated list and calls
//BBNukeFileInfo on each
void BBNukeFileList(LPCTSTR lpszSrc)
{
while (*lpszSrc) {
BBNukeFileInfo(lpszSrc);
lpszSrc += lstrlen(lpszSrc) + 1;
}
}
HRESULT Drives_GetDriveName(LPCITEMIDLIST pidd, LPTSTR lpszName, UINT cchMax);
void BBGetDriveName(int idDrive, LPTSTR lpszName, UINT cchSize)
{
TCHAR szDrive[MAX_PATH];
LPITEMIDLIST pidl;
VDATEINPUTBUF(lpszName, TCHAR, cchSize);
if (idDrive == SERVERDRIVE) {
lstrcpy(lpszName, PathFindFileName(GetNetHomeDir()));
} else {
BBPathBuildRoot(szDrive, idDrive);
pidl = ILCreateFromPath(szDrive);
Drives_GetDriveName(ILFindLastID(pidl), lpszName, cchSize);
ILFree(pidl);
}
}
BOOL CALLBACK _BB_AddPage(HPROPSHEETPAGE psp, LPARAM lParam)
{
LPPROPSHEETHEADER ppsh = (LPPROPSHEETHEADER)lParam;
ppsh->phpage[ppsh->nPages++] = psp;
return TRUE;
}
HANDLE BBFindStuffOtherPropSheet(HWND hwndParent, LPITEMIDLIST pidl)
{
HWND hwnd;
if (pidl && (NULL != (hwnd = FindStubForPidl(pidl)))) {
SwitchToThisWindow(GetLastActivePopup(hwnd), TRUE);
} else {
// stuff the stub window with our pidl
if (hwndParent)
return StuffStubWindowWithPidl(hwndParent, pidl);
}
return NULL;
}
DWORD CALLBACK _BB_PropertiesThread(IDataObject * pdtobj)
{
HPROPSHEETPAGE ahpage[MAXPROPPAGES];
TCHAR szTitle[80];
PROPSHEETHEADER psh;
HWND hwnd;
HANDLE hClassIdl = (HANDLE)NULL;
int iPage;
LPITEMIDLIST pidlBitBucket = SHCloneSpecialIDList(NULL, CSIDL_BITBUCKET, FALSE);
hwnd = _CreateStubWindow();
// bugbug, we need to put our pidl into the stub window
psh.dwSize = SIZEOF(psh);
psh.dwFlags = PSH_PROPTITLE;
psh.hInstance = HINST_THISDLL;
psh.hwndParent = hwnd;
psh.nStartPage = 0;
psh.phpage = ahpage;
psh.nPages = 0;
if (pdtobj) {
BBFILEPROPINFO bbfpi;
STGMEDIUM medium;
LPITEMIDLIST pidlSave;
int iMax;
LPIDA pida;
pida = DataObj_GetHIDA(pdtobj, &medium);
iMax = HIDA_GetCount(medium.hGlobal);
// key off the first pidl for finding other prop sheets
pidlSave = ILCombine(pidlBitBucket, IDA_GetIDListPtr(pida, 0));
hClassIdl = BBFindStuffOtherPropSheet(psh.hwndParent, pidlSave);
ILFree(pidlSave);
if (!hClassIdl) // found other window
goto Cleanup;
if (GetAsyncKeyState(VK_SHIFT) < 0) {
if (iMax >= MAXPROPPAGES) {
ShellMessageBox(HINST_THISDLL, NULL, MAKEINTRESOURCE(IDS_BITBUCKET_TOOMANYFILES),
MAKEINTRESOURCE(IDS_WASTEBASKET), MB_OK | MB_ICONEXCLAMATION | MB_SETFOREGROUND);
iMax = MAXPROPPAGES;
}
bbfpi.psp.dwFlags = PSP_USETITLE;
}
else {
bbfpi.psp.dwFlags = 0;
iMax = 1;
}
bbfpi.psp.dwSize = SIZEOF(bbfpi);
bbfpi.psp.hInstance = HINST_THISDLL;
bbfpi.psp.pszTemplate = MAKEINTRESOURCE(DLG_DELETEDFILEPROP);
bbfpi.psp.pfnDlgProc = BBFilePropDlgProc;
bbfpi.psp.pszTitle = szTitle;
bbfpi.hida = medium.hGlobal;
for (iPage = 0; iPage < iMax; iPage++) {
LPBBDATAENTRYIDA lpbbidl;
bbfpi.i = iPage;
bbfpi.pidl = IDA_GetIDListPtr(pida, iPage);
lpbbidl = PIDLTODATAENTRYID(bbfpi.pidl);
#ifdef UNICODE
if ( lpbbidl->cb == SIZEOF(BBDATAENTRYIDW)) {
LPBBDATAENTRYIDW lpbbidlw = DATAENTRYIDATOW(lpbbidl);
WCHAR szTemp[MAX_PATH];
ualstrcpyn(szTemp, lpbbidlw->wszOriginal, ARRAYSIZE(szTemp));
lstrcpyn(szTitle, PathFindFileName(szTemp), ARRAYSIZE(szTitle));
} else {
MultiByteToWideChar(CP_ACP, 0,
PathFindFileNameA(lpbbidl->bbde.szOriginal), -1,
szTitle, ARRAYSIZE(szTitle));
}
#else
lstrcpyn(szTitle, PathFindFileName(lpbbidl->bbde.szOriginal), ARRAYSIZE(szTitle));
#endif
PathRemoveExtension(szTitle);
psh.phpage[iPage] = CreatePropertySheetPage(&bbfpi.psp);
}
HIDA_ReleaseStgMedium(pida, &medium);
psh.nPages = iPage; // incremented in callback
if (iPage == 1)
psh.pszCaption = szTitle;
else {
psh.pszCaption = MAKEINTRESOURCE(IDS_DELETEDFILES);
}
} else {
// first activate the other property sheet if there's already one
// up for this
hClassIdl = BBFindStuffOtherPropSheet(psh.hwndParent, pidlBitBucket);
if (!hClassIdl)
goto Cleanup;
CShellBitBucket_PS_AddPages(NULL, _BB_AddPage, (LPARAM)&psh);
psh.pszCaption = MAKEINTRESOURCE(IDS_WASTEBASKET);
}
PropertySheet(&psh);
Cleanup:
if (pidlBitBucket)
ILFree(pidlBitBucket);
if (hClassIdl)
SHFreeShared(hClassIdl,GetCurrentProcessId());
if (psh.hwndParent)
DestroyWindow(psh.hwndParent);
return TRUE;
}
typedef struct _bb_threaddata {
LPSHELLBITBUCKET this;
HWND hwndOwner;
IDataObject * pdtobj;
UINT idCmd;
POINT ptDrop;
BOOL fSameHwnd;
BOOL fDragDrop;
} BBTHREADDATA;
int FS_CreateMoveCopyList(IDataObject *pdtobj, LPVOID hNameMappings, LPITEMIDLIST **pppidl);
void FS_PositionItems(HWND hwndOwner, UINT cidl, const LPITEMIDLIST *ppidl, IDataObject *pdtobj, POINT *pptOrigin, BOOL fMove);
void FS_FreeMoveCopyList(LPITEMIDLIST *ppidl, UINT cidl);
DWORD WINAPI BB_DropThreadInit(BBTHREADDATA *pbbtd)
{
IDataObject * pDataObj = pbbtd->pdtobj;
STGMEDIUM medium;
FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
if (SUCCEEDED(pDataObj->lpVtbl->GetData(pDataObj, &fmte, &medium))) {
// call delete here so that files will be moved in
// their respective bins, not necessarily this one.
DRAGINFO di;
UINT fOptions = 0;
di.uSize = SIZEOF(DRAGINFO);
if (DragQueryInfo(medium.hGlobal, &di))
{
if (!BitBucketWillRecycle(di.lpFileList))
fOptions = SD_USERCONFIRMATION;
}
if (IsFileInBitBucket(di.lpFileList)) {
LPITEMIDLIST *ppidl = NULL;
int cidl;
cidl = FS_CreateMoveCopyList(pDataObj, NULL, &ppidl);
if (ppidl) {
FS_PositionItems(pbbtd->hwndOwner, cidl, ppidl, pDataObj, &pbbtd->ptDrop, pbbtd->fDragDrop);
FS_FreeMoveCopyList(ppidl, cidl);
}
} else {
_TransferDelete(pbbtd->hwndOwner, medium.hGlobal, fOptions);
}
SHChangeNotifyHandleEvents();
SHReleaseStgMedium(&medium);
SHFree(di.lpFileList);
}
#ifdef DEBUG
{
extern UINT g_cRefExtra;
g_cRefExtra--;
}
#endif
return 0;
}
DWORD CALLBACK _BB_ThreadDispatch(BBTHREADDATA *pbbtd)
{
switch(pbbtd->idCmd)
{
case DFM_CMD_MOVE:
BB_DropThreadInit(pbbtd);
break;
case DFM_CMD_PROPERTIES:
case FSIDM_PROPERTIESBG:
_BB_PropertiesThread(pbbtd->pdtobj);
break;
case DFM_CMD_DELETE:
BBPurge(pbbtd->this, pbbtd->hwndOwner, pbbtd->pdtobj);
break;
case FSIDM_RESTORE:
BBRestore(pbbtd->this, pbbtd->hwndOwner, pbbtd->pdtobj);
break;
}
if (pbbtd->this)
pbbtd->this->isf.lpVtbl->Release(&pbbtd->this->isf);
if (pbbtd->pdtobj)
pbbtd->pdtobj->lpVtbl->Release(pbbtd->pdtobj);
LocalFree((HLOCAL)pbbtd);
return 0;
}
HRESULT BB_LaunchThread(LPSHELLBITBUCKET this, HWND hwndOwner, IDataObject * pdtobj, UINT idCmd)
{
BBTHREADDATA *pbbtd = (BBTHREADDATA *)LocalAlloc(LPTR, SIZEOF(BBTHREADDATA));
if (pbbtd)
{
HANDLE hThread;
DWORD idThread;
pbbtd->this = this;
pbbtd->pdtobj = pdtobj;
pbbtd->hwndOwner = hwndOwner;
pbbtd->idCmd = idCmd;
if (idCmd == DFM_CMD_MOVE) {
pbbtd->fDragDrop = ShellFolderView_GetDropPoint(hwndOwner, &pbbtd->ptDrop);
}
if (this)
this->isf.lpVtbl->AddRef(&this->isf);
if (pdtobj)
pdtobj->lpVtbl->AddRef(pdtobj);
hThread = CreateThread(NULL, 0, _BB_ThreadDispatch, pbbtd, 0, &idThread);
if (hThread)
{
#ifdef DEBUG
if (idCmd == DFM_CMD_MOVE)
{
extern UINT g_cRefExtra;
g_cRefExtra++;
}
#endif
CloseHandle(hThread);
return NOERROR;
}
else
{
if (pdtobj)
pdtobj->lpVtbl->Release(pdtobj);
if (this)
this->isf.lpVtbl->Release(&this->isf);
LocalFree((HLOCAL)pbbtd);
return E_UNEXPECTED;
}
}
else
return E_OUTOFMEMORY;
}
//
// Callback from SHCreateShellFolderViewEx
//
HRESULT CALLBACK CShellBitBucket_DFMCallBack(LPSHELLFOLDER psf, HWND hwndOwner,
IDataObject * pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
LPSHELLBITBUCKET this = IToClass(CShellBitBucket, isf, psf);
HRESULT hres = NOERROR; // assume no error
switch(uMsg)
{
case DFM_MERGECONTEXTMENU:
CDefFolderMenu_MergeMenu(HINST_THISDLL, POPUP_BITBUCKET_ITEM, 0, (LPQCMINFO)lParam);
hres = ResultFromShort(-1); // return 1 so default reg commands won't be added
break;
case DFM_INVOKECOMMAND:
switch (wParam) {
case FSIDM_RESTORE:
case DFM_CMD_DELETE:
case DFM_CMD_PROPERTIES:
hres = BB_LaunchThread(this, hwndOwner, pdtobj, wParam);
break;
default:
hres = S_FALSE;
break;
}
break;
case DFM_GETHELPTEXT:
LoadStringA(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPSTR)lParam, HIWORD(wParam));;
break;
case DFM_GETHELPTEXTW:
LoadStringW(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPWSTR)lParam, HIWORD(wParam));;
break;
default:
hres = E_NOTIMPL;
break;
}
return hres;
}
STDMETHODIMP CShellBitBucket_GetDetailsOf(LPSHELLBITBUCKET this, LPCITEMIDLIST pidl,
UINT iColumn, LPSHELLDETAILS lpDetails)
{
HRESULT hres;
DWORD dwSize;
#ifdef UNICODE
TCHAR szTemp[MAX_PATH];
#endif
if (iColumn >= ICOL_MAX)
return (E_NOTIMPL);
lpDetails->str.uType = STRRET_CSTR;
lpDetails->str.cStr[0] = '\0';
if (!pidl) {
// getting the headers
#ifdef UNICODE
LoadString(HINST_THISDLL, c_bb_cols[iColumn].ids,
szTemp, ARRAYSIZE(szTemp));
lpDetails->str.pOleStr = SHAlloc((lstrlen(szTemp)+1)*SIZEOF(TCHAR));
if (lpDetails->str.pOleStr == NULL)
{
return E_OUTOFMEMORY;
}
else
{
lpDetails->str.uType = STRRET_OLESTR;
lstrcpy(lpDetails->str.pOleStr,szTemp);
}
#else
LoadString(HINST_THISDLL, c_bb_cols[iColumn].ids,
lpDetails->str.cStr, ARRAYSIZE(lpDetails->str.cStr));
#endif
lpDetails->fmt = c_bb_cols[iColumn].iFmt;
lpDetails->cxChar = c_bb_cols[iColumn].cchCol;
return(NOERROR);
} else {
UNALIGNED BBDATAENTRYIDA * pbbidl = PIDLTODATAENTRYID(pidl);
hres = NOERROR;
switch (iColumn) {
case ICOL_NAME:
hres = CShellBitBucket_SF_GetDisplayNameOf(&this->isf, pidl, SHGDN_NORMAL, &lpDetails->str);
break;
case ICOL_SIZE:
{
ULONGLONG cbSize;
TCHAR szNum[MAX_COMMA_AS_K_SIZE];
if (FS_IsFolderI((LPIDFOLDER)pidl))
cbSize = pbbidl->bbde.dwSize;
else
FS_GetSize(NULL, (LPIDFOLDER)pidl, &cbSize);
SizeFormatAsK64(cbSize, szNum);
#ifdef UNICODE
lpDetails->str.pOleStr = (LPOLESTR)SHAlloc((lstrlen(szNum)+1)*SIZEOF(TCHAR));
if ( lpDetails->str.pOleStr != NULL ) {
lpDetails->str.uType = STRRET_OLESTR;
lstrcpy(lpDetails->str.pOleStr, szNum);
} else {
return E_OUTOFMEMORY;
}
#else
lstrcpy(lpDetails->str.cStr, szNum);
#endif
}
break;
case ICOL_ORIGINAL:
{
#ifdef UNICODE
if ( pbbidl->cb == SIZEOF(BBDATAENTRYW)) {
LPBBDATAENTRYIDW pbbidlw = DATAENTRYIDATOW(pbbidl);
ualstrcpyn(szTemp,pbbidlw->wszOriginal,ARRAYSIZE(szTemp));
} else {
MultiByteToWideChar(CP_ACP, 0,
pbbidl->bbde.szOriginal, -1,
szTemp, ARRAYSIZE(szTemp));
}
PathRemoveFileSpec(szTemp);
lpDetails->str.pOleStr = (LPOLESTR)SHAlloc((lstrlen(szTemp)+1)*SIZEOF(TCHAR));
if ( lpDetails->str.pOleStr != NULL ) {
lpDetails->str.uType = STRRET_OLESTR;
lstrcpy(lpDetails->str.pOleStr, szTemp);
hres = S_OK;
} else {
hres = E_OUTOFMEMORY;
}
#else
lstrcpy(lpDetails->str.cStr, pbbidl->bbde.szOriginal);
PathRemoveFileSpec(lpDetails->str.cStr);
#endif
break;
}
case ICOL_TYPE:
#ifdef UNICODE
FS_GetTypeName((LPIDFOLDER)pidl, szTemp, ARRAYSIZE(szTemp));
lpDetails->str.pOleStr = (LPOLESTR)SHAlloc((lstrlen(szTemp)+1)*SIZEOF(TCHAR));
if ( lpDetails->str.pOleStr != NULL ) {
lpDetails->str.uType = STRRET_OLESTR;
lstrcpy(lpDetails->str.pOleStr, szTemp);
hres = S_OK;
} else {
hres = E_OUTOFMEMORY;
}
#else
FS_GetTypeName((LPIDFOLDER)pidl, lpDetails->str.cStr, ARRAYSIZE(lpDetails->str.cStr));
#endif
break;
case ICOL_MODIFIED:
{
FILETIME ft = pbbidl->bbde.ft;
#ifdef UNICODE
FileTimeToDateTimeString(&ft, szTemp);
lpDetails->str.pOleStr = (LPOLESTR)SHAlloc((lstrlen(szTemp)+1)*SIZEOF(TCHAR));
if ( lpDetails->str.pOleStr != NULL ) {
lpDetails->str.uType = STRRET_OLESTR;
lstrcpy(lpDetails->str.pOleStr, szTemp);
hres = S_OK;
} else {
hres = E_OUTOFMEMORY;
}
#else
FileTimeToDateTimeString(&ft, lpDetails->str.cStr);
#endif
}
break;
}
}
return hres;
}
//
// CBitBucketIDLDropTarget::DragEnter
//
// This function puts DROPEFFECT_LINK in *pdwEffect, only if the data object
// contains one or more net resource.
//
HRESULT CBitBucketIDLDropTarget_DragEnter(IDropTarget * pdropt, IDataObject * pDataObj, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect)
{
LPIDLDROPTARGET this = IToClass(CIDLDropTarget, dropt, pdropt);
#ifdef BB_DEBUG
DebugMsg(DM_TRACE, TEXT("sh - TR CBitBucketIDLDropTarget::DragEnter"));
#endif
// Call the base class first
CIDLDropTarget_DragEnter(pdropt, pDataObj, grfKeyState, pt, pdwEffect);
// we don't really care what is in the data object, as long as move
// is supported by the source we say you can move it to the wastbasket
// in the case of files we will do the regular recycle bin stuff, if
// it is not files we will just say it is moved and let the source delete it
*pdwEffect &= DROPEFFECT_MOVE;
this->dwEffectLastReturned = *pdwEffect;
return NOERROR;
}
//
// CBitBucketIDLDropTarget::Drop
//
// This function creates a connection to a dropped net resource object.
//
HRESULT CBitBucketIDLDropTarget_Drop(IDropTarget * pdropt, IDataObject * pDataObj, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect)
{
LPIDLDROPTARGET this = IToClass(CIDLDropTarget, dropt, pdropt);
HRESULT hres;
DWORD dwEffectPerformed = 0;
// only move operation is allowed
*pdwEffect &= DROPEFFECT_MOVE;
if (*pdwEffect)
{
hres = CIDLDropTarget_DragDropMenu(this, DROPEFFECT_MOVE, pDataObj,
pt, pdwEffect, NULL, NULL, POPUP_NONDEFAULTDD, grfKeyState);
if (hres == S_FALSE)
{
if (this->dwData & DTID_HDROP)
{
LPDATAOBJECT pdtobjCopy;
// Check if it's our object
if (!CIDLData_IsOurs(pDataObj))
{
// No, clone the data object (light-weight marshaling).
hres = CIDLData_CloneForMoveCopy(pDataObj, &pdtobjCopy);
}
else
{
// Yes, just copy the AddRef'ed pointer.
pdtobjCopy = pDataObj;
pdtobjCopy->lpVtbl->AddRef(pdtobjCopy);
hres = NOERROR;
}
if (SUCCEEDED(hres))
{
hres = BB_LaunchThread(NULL, this->hwndOwner, pdtobjCopy,
DFM_CMD_MOVE);
pdtobjCopy->lpVtbl->Release(pdtobjCopy);
// we did it so DON'T tell the drop source to do it too...
dwEffectPerformed = *pdwEffect;
*pdwEffect = 0;
}
}
else
{
// if it was not files, we just say we moved the data, letting the
// source deleted it. lets hope they support undo...
hres = NOERROR;
}
}
}
else
hres = E_FAIL;
CIDLDropTarget_DragLeave(pdropt);
if (*pdwEffect)
dwEffectPerformed = *pdwEffect;
if (dwEffectPerformed)
DataObj_SetPerformedEffect(pDataObj, dwEffectPerformed);
return hres;
}
#pragma data_seg(".text", "CODE")
IDropTargetVtbl c_CBBDropTargetVtbl =
{
CIDLDropTarget_QueryInterface,
CIDLDropTarget_AddRef,
CIDLDropTarget_Release,
CBitBucketIDLDropTarget_DragEnter,
CIDLDropTarget_DragOver,
CIDLDropTarget_DragLeave,
CBitBucketIDLDropTarget_Drop,
};
void SHChangeNotifyDeregisterWindow(HWND hwnd);
void BBInitializeViewWindow(HWND hwndView)
{
int i;
for (i = 0 ; i < MAX_BITBUCKETS; i++) {
SHChangeNotifyEntry fsne;
fsne.fRecursive = FALSE;
// make it if it's there so that we'll get any events
MakeBitBucket(i);
if (g_pBitBucket[i]) {
UINT u;
fsne.pidl = g_pBitBucket[i]->pidl;
u = SHChangeNotifyRegister(hwndView, SHCNRF_NewDelivery | SHCNRF_ShellLevel | SHCNRF_InterruptLevel,
SHCNE_DISKEVENTS, WM_DSV_FSNOTIFY, 1, &fsne);
#ifdef BB_DEBUG
DebugMsg(DM_TRACE, TEXT("SHChangeNotify gives: %d on hwnd %d, %d"), u , hwndView, IsWindow(hwndView));
#endif
}
}
}
#define ISDIGIT(c) ((c) >= TEXT('0') && (c) <= TEXT('9'))
int BBFileToIndex(LPCTSTR lpcszFile)
{
TCHAR szFile[30];
lstrcpyn(szFile, lpcszFile, ARRAYSIZE(szFile));
if (!lstrcmpi(szFile, c_szInfo) ||
!lstrcmpi(szFile, c_szDesktopIni) ||
!lstrcmpi(szFile, c_szDeathRow)) {
return -1;
}
PathRemoveExtension(szFile);
if (((szFile[0] == TEXT('D')) || (szFile[0] == TEXT('d'))) &&
ISDIGIT(szFile[2])) {
return StrToInt(szFile + 1 + 1); // plus 1 to skip the drive byte
} else {
Assert(0);
return -1;
}
}
// converts Dc19.foo to 19
int BBPathToIndex(LPCTSTR lpszPath)
{
TCHAR szFile[30];
lstrcpyn(szFile, PathFindFileName(lpszPath), ARRAYSIZE(szFile));
return BBFileToIndex(szFile);
}
// takes a full path and returns just the last item id
LPITEMIDLIST PathToBBPidl(LPTSTR lpszPath)
{
LPBBDATAENTRY lpbbde;
LPBBDRIVEINFO lpbbdi;
LPITEMIDLIST pidl = NULL;
int idDrive = BBPathGetDriveNumber(lpszPath);
int iIndex;
int iMax;
int i;
Assert(idDrive >= 0); // UNC will generate -1
iIndex = BBPathToIndex(lpszPath);
if (iIndex == -1)
return NULL;
ENTERCRITICAL;
lpbbdi= g_pBitBucket[idDrive];
if (lpbbdi && lpbbdi->hdpaDeleteCache) {
// check if it's in the deleted cache
iMax = DPA_GetPtrCount(lpbbdi->hdpaDeleteCache);
for (i = 0 ; i < iMax; i++) {
lpbbde = DPA_FastGetPtr(lpbbdi->hdpaDeleteCache, i);
if (lpbbde->iIndex == iIndex) {
pidl = BBDataEntryToPidl(idDrive, lpbbde, NULL);
break;
}
}
}
// not in the deleted cached hdpa
// now try the info file.
if (!pidl) {
i = BBLoadFileData(idDrive) - 1;
// BBLoadFileData might have created a drive info entry so reassign local
lpbbdi = g_pBitBucket[idDrive];
if (lpbbdi) {
for (; i >= 0; i--) {
lpbbde = (LPBBDATAENTRY)((LPBYTE)lpbbdi->lpbbdeRead +
(i*lpbbdi->cbDataEntrySize));
// found it!
if (lpbbde->iIndex == iIndex) {
pidl = BBDataEntryToPidl(idDrive, lpbbde, NULL);
break;
}
}
}
}
LEAVECRITICAL;
// not found..
return pidl;
}
BOOL BBNukeFileInfoByFileIndex(LPBBDRIVEINFO lpbbdi, int iIndex)
{
int i;
LPBBDATAENTRY lpbbde;
BOOL fFound = FALSE;
for (i = 0; i < lpbbdi->cReadCount ; i++ ) {
lpbbde = (LPBBDATAENTRY)((LPBYTE)lpbbdi->lpbbdeRead +
(i*lpbbdi->cbDataEntrySize));
if (lpbbde->iIndex == iIndex) {
if (lpbbdi->dwSize >= lpbbde->dwSize)
lpbbdi->dwSize -= lpbbde->dwSize;
hmemcpy(lpbbde, (LPBYTE)lpbbde + lpbbdi->cbDataEntrySize, lpbbdi->cbDataEntrySize * (lpbbdi->cReadCount - i - 1));
if (lpbbdi->cReadCount > 0)
lpbbdi->cReadCount--;
lpbbdi->fReadDirty = TRUE;
return TRUE;
}
}
return FALSE;
}
void BBNukeFileInfo(LPCTSTR lpszPath)
{
int iIndex;
int idDrive;
LPBBDRIVEINFO lpbbdi;
int iMax;
idDrive = BBPathGetDriveNumber(lpszPath);
lpbbdi = g_pBitBucket[idDrive];
if (!lpbbdi)
return;
ENTERCRITICAL;
SaveDeletedFileInfo(lpbbdi, FALSE); // this will ensure that nothing is in hdpaCache
iMax = BBLoadFileData(idDrive);
if (iMax != 0) // If we were able to load the data, then remove it
{
iIndex = BBPathToIndex(lpszPath);
BBNukeFileInfoByFileIndex(lpbbdi, iIndex);
}
LEAVECRITICAL;
}
HRESULT BBHandleFSNotify(HWND hwndOwner, LONG lEvent, LPITEMIDLIST pidl1, LPITEMIDLIST pidl2)
{
int idDrive;
HRESULT hres = NOERROR;
TCHAR szPath[MAX_PATH];
HWND hwndView = DV_HwndMain2HwndView(hwndOwner);
#ifdef BB_DEBUG
DebugMsg(DM_TRACE, TEXT("sh - TR - BBHandleFSNotify event %d"), lEvent);
#endif
// pidls must be child of drives or network
// (actually only drives work for right now)
// that way we won't get duplicate events
if ((!ILIsParent((LPCITEMIDLIST)&c_idlDrives, pidl1, FALSE) &&
!ILIsParent((LPCITEMIDLIST)&c_idlNet, pidl1, FALSE)) ||
(pidl2 && !ILIsParent((LPCITEMIDLIST)&c_idlDrives, pidl2, FALSE) &&
!ILIsParent((LPCITEMIDLIST)&c_idlNet, pidl2, FALSE)))
return S_FALSE;
SHGetPathFromIDList(pidl1, szPath);
switch (lEvent) {
case SHCNE_RENAMEFOLDER:
case SHCNE_RENAMEITEM: {
// if the rename's target is in a bitbucket, then do a create.
// otherwise, return NOERROR..
idDrive = BBPathGetDriveNumber(szPath);
if (g_pBitBucket[idDrive] && ILIsParent(g_pBitBucket[idDrive]->pidl, pidl1, TRUE)) {
return BBHandleFSNotify(hwndOwner, SHCNE_DELETE, pidl1, 0);
} else {
return BBHandleFSNotify(hwndOwner, SHCNE_CREATE, pidl2, 0);
}
break;
}
case SHCNE_CREATE:
case SHCNE_MKDIR: {
LPITEMIDLIST pidl;
pidl = PathToBBPidl(szPath);
#ifdef BB_DEBUG
DebugMsg(DM_TRACE, TEXT("sh - TR - BBHandleFSNotify SHCNE_CREATE pidl = %d"), pidl);
#endif
if (pidl){
ShellFolderView_AddObject(hwndOwner, pidl);
hres = S_FALSE;
}
break;
}
case SHCNE_DELETE:
case SHCNE_RMDIR: {
SHGetPathFromIDList(pidl1, szPath);
BBNukeFileInfo(szPath);
ShellFolderView_RemoveObject(hwndOwner, ILFindLastID(pidl1));
hres = S_FALSE;
break;
}
}
return hres;
}
void BBSort(HWND hwndOwner, int id)
{
switch(id) {
case FSIDM_SORTBYNAME:
ShellFolderView_ReArrange(hwndOwner, 0);
break;
case FSIDM_SORTBYORIGIN:
ShellFolderView_ReArrange(hwndOwner, 1);
break;
case FSIDM_SORTBYDELETEDDATE:
ShellFolderView_ReArrange(hwndOwner, 2);
break;
case FSIDM_SORTBYTYPE:
ShellFolderView_ReArrange(hwndOwner, 3);
break;
case FSIDM_SORTBYSIZE:
ShellFolderView_ReArrange(hwndOwner, 4);
break;
}
}
//
// Callback from SHCreateShellFolderViewEx
//
HRESULT CALLBACK ShellBitBucket_FNVCallBack(LPSHELLVIEW psvOuter,
LPSHELLFOLDER psf,
HWND hwndOwner,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
LPSHELLBITBUCKET this = IToClass(CShellBitBucket, isf, psf);
HRESULT hres = NOERROR; // assume no error
switch(uMsg)
{
case DVM_MERGEMENU:
CDefFolderMenu_MergeMenu(HINST_THISDLL, 0, POPUP_BITBUCKET_POPUPMERGE, (LPQCMINFO)lParam);
break;
case DVM_GETDETAILSOF:
#define pdi ((DETAILSINFO *)lParam)
return(CShellBitBucket_GetDetailsOf(this, pdi->pidl, wParam, (LPSHELLDETAILS)&pdi->fmt));
#undef pdi
case DVM_COLUMNCLICK:
ShellFolderView_ReArrange(hwndOwner, wParam);
break;
case DVM_INITMENUPOPUP:
EnableMenuItem((HMENU)lParam, LOWORD(wParam) + FSIDM_PURGEALL,
(BBTotalCount(NULL) ? MF_BYCOMMAND : MF_GRAYED|MF_BYCOMMAND));
break;
case DVM_INVOKECOMMAND:
#ifdef BB_DEBUG
DebugMsg(DM_TRACE, TEXT("sh TR - FS_FSNCallBack DVN_INVOKECOMMAND (id=%x)"), wParam);
#endif
switch(wParam)
{
case FSIDM_PURGEALL:
BBPurgeAll(this, hwndOwner);
break;
case FSIDM_SORTBYNAME:
case FSIDM_SORTBYORIGIN:
case FSIDM_SORTBYDELETEDDATE:
case FSIDM_SORTBYTYPE:
case FSIDM_SORTBYSIZE:
BBSort(hwndOwner, wParam);
break;
}
break;
case DVM_GETHELPTEXT:
LoadString(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPTSTR)lParam, HIWORD(wParam));;
break;
//
// Some cases we forward to the file system callback .
case DVM_GETCCHMAX:
break;
case DVM_SELCHANGE:
FSOnSelChange(NULL, (PDVSELCHANGEINFO)lParam);
break;
case DVM_RELEASE:
FSSetStatusText(hwndOwner, NULL, 0, 1);
if (lParam) {
LocalFree((HLOCAL)lParam);
}
break;
case DVM_FSNOTIFY: {
LPITEMIDLIST * ppidl = (LPITEMIDLIST*)wParam;
hres = BBHandleFSNotify(hwndOwner, lParam, ppidl[0], ppidl[1]);
break;
}
case DVM_UPDATESTATUSBAR:
FSUpdateStatusBar(hwndOwner, (PFSSELCHANGEINFO)lParam);
break;
case DVM_WINDOWCREATED:
BBInitializeViewWindow((HWND)wParam);
FSInitializeStatus(hwndOwner, -1, (PDVSELCHANGEINFO)lParam);
break;
case DVM_INSERTITEM:
case DVM_DELETEITEM:
{
PDVSELCHANGEINFO pdvsci = (PDVSELCHANGEINFO)lParam;
PFSSELCHANGEINFO pfssci;
pfssci = *((PFSSELCHANGEINFO*)pdvsci->plParam);
if (!pfssci) {
FSInitializeStatus(hwndOwner, -1, pdvsci);
}
FSOnInsertDeleteItem(NULL, (PDVSELCHANGEINFO)lParam, uMsg == DVM_INSERTITEM ? 1 : -1);
break;
}
case DVM_WINDOWDESTROY: {
// flush cache on close of a bitbucket
// this helps sync files in case of a gpf before we are able to shut down
// properly.
ENTERCRITICAL;
BitBucketFlushCacheCheckPurge(FALSE);
LEAVECRITICAL;
SHChangeNotifyDeregisterWindow((HWND)wParam);
break;
}
default:
hres = E_FAIL;
}
return hres;
}
HRESULT CALLBACK CBitBucket_DFMCallBackBG(LPSHELLFOLDER psf, HWND hwndOwner,
IDataObject * pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
LPSHELLBITBUCKET this = IToClass(CShellBitBucket, isf, psf);
HRESULT hres = NOERROR;
switch(uMsg)
{
case DFM_MERGECONTEXTMENU:
CDefFolderMenu_MergeMenu(HINST_THISDLL, POPUP_BITBUCKET_BACKGROUND, POPUP_BITBUCKET_POPUPMERGE, (LPQCMINFO)lParam);
break;
case DFM_MAPCOMMANDNAME:
if (lstrcmpi((LPCTSTR)lParam, c_szUnDelete) == 0)
{
*(int *)wParam = FSIDM_RESTORE;
}
else
{
// command not found
hres = E_FAIL;
}
break;
case DFM_GETHELPTEXT:
LoadStringA(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPSTR)lParam, HIWORD(wParam));
break;
case DFM_GETHELPTEXTW:
LoadStringW(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPWSTR)lParam, HIWORD(wParam));
break;
case DFM_INVOKECOMMAND:
switch(wParam)
{
case FSIDM_SORTBYNAME:
case FSIDM_SORTBYORIGIN:
case FSIDM_SORTBYDELETEDDATE:
case FSIDM_SORTBYTYPE:
case FSIDM_SORTBYSIZE:
BBSort(hwndOwner, wParam);
break;
case FSIDM_PROPERTIESBG:
hres = BB_LaunchThread(NULL, hwndOwner, NULL, FSIDM_PROPERTIESBG);
break;
case DFM_CMD_PASTE:
case DFM_CMD_PROPERTIES:
// GetAttributesOf cidl==0 has SFGAO_HASPROPSHEET,
// let defcm handle this
hres = S_FALSE;
break;
default:
// GetAttributesOf cidl==0 does not set _CANMOVE, _CANDELETE, etc,
// BUT accelerator keys will get these unavailable menu items...
// so we need to return failure here
hres = E_NOTIMPL;
break;
}
break;
default:
hres = E_NOTIMPL;
break;
}
return hres;
}
STDMETHODIMP CShellBitBucket_SF_CreateViewObject(LPSHELLFOLDER psf, HWND hwnd, REFIID riid, LPVOID * ppvOut)
{
HRESULT hres = (E_NOINTERFACE);
LPSHELLBITBUCKET this = IToClass(CShellBitBucket, isf, psf);
if (IsEqualIID(riid, &IID_IShellView))
{
CSFV csfv = {
SIZEOF(CSFV), // cbSize
psf, // pshf
NULL, // psvOuter
NULL, // pidl
0, // lEvents
ShellBitBucket_FNVCallBack, // pfnCallback
FVM_DETAILS, // default view mode
};
hres = SHCreateShellFolderViewEx(&csfv, (LPSHELLVIEW *)ppvOut);
}
else if (IsEqualIID(riid, &IID_IDropTarget))
{
hres = CIDLDropTarget_Create(hwnd, &c_CBBDropTargetVtbl, this->pidl, (IDropTarget **)ppvOut);
}
else if (IsEqualIID(riid, &IID_IContextMenu))
{
return CDefFolderMenu_Create(NULL, hwnd,
0, NULL, psf, CBitBucket_DFMCallBackBG,
NULL, NULL, (LPCONTEXTMENU *)ppvOut);
}
return hres;
}
HRESULT CShellBitBucket_SF_ParseDisplayName(LPSHELLFOLDER psf,
HWND hwndOwner, LPBC pbc, LPOLESTR pwszDisplayName,
ULONG * pchEaten, LPITEMIDLIST * ppidl, ULONG * pdwAttributes)
{
return E_NOTIMPL;
}
// subclass member function to support CF_HDROP and CF_NETRESOURCE
extern HRESULT CFSIDLData_QueryGetData(IDataObject * pdtobj, FORMATETC * pformatetc);
HRESULT CBBIDLData_QueryGetData(IDataObject * pdtobj, FORMATETC * pformatetc)
{
Assert(g_cfFileNameMap);
if (pformatetc->cfFormat == g_cfFileNameMap && (pformatetc->tymed & TYMED_HGLOBAL))
{
return NOERROR; // same as S_OK
}
return CFSIDLData_QueryGetData(pdtobj, pformatetc);
}
// subclass member function to support CF_HDROP and CF_NETRESOURCE
// in:
// hida bitbucket id array
//
// out:
// HGLOBAL with double NULL terminated string list of destination names
//
HGLOBAL BuildDestSpecs(LPIDA pida)
{
LPCITEMIDLIST pidl;
LPBBDATAENTRYIDA pbbidl;
LPTSTR pszRet;
UINT i, cbAlloc = SIZEOF(TCHAR); // for double NULL termination
#ifdef UNICODE
WCHAR szName[MAX_PATH];
#endif
for (i = 0; NULL != (pidl = IDA_GetIDListPtr(pida, i)); i++)
{
pbbidl = PIDLTODATAENTRYID(pidl);
#ifdef UNICODE
if (pbbidl->cb == SIZEOF(BBDATAENTRYIDW)) {
LPBBDATAENTRYIDW pbbidlw = DATAENTRYIDATOW(pbbidl);
ualstrcpy(szName,pbbidlw->wszOriginal);
cbAlloc += lstrlenW(PathFindFileName(szName)) * SIZEOF(TCHAR) + SIZEOF(TCHAR);
} else {
cbAlloc += lstrlenA(PathFindFileNameA(pbbidl->bbde.szOriginal)) * SIZEOF(TCHAR) + SIZEOF(TCHAR);
}
#else
cbAlloc += lstrlen(PathFindFileName(pbbidl->bbde.szOriginal)) * SIZEOF(TCHAR) + SIZEOF(TCHAR);
#endif
}
pszRet = LocalAlloc(LPTR, cbAlloc);
if (pszRet)
{
LPTSTR pszDest = pszRet;
for (i = 0; NULL != (pidl = IDA_GetIDListPtr(pida, i)); i++)
{
pbbidl = PIDLTODATAENTRYID(pidl);
#ifdef UNICODE
if ( pbbidl->cb == SIZEOF(BBDATAENTRYIDW)) {
LPBBDATAENTRYIDW pbbidlw = DATAENTRYIDATOW(pbbidl);
ualstrcpy(szName,pbbidlw->wszOriginal);
ualstrcpy(pszDest, PathFindFileName(szName));
} else {
LPSTR lpFileName = PathFindFileNameA(pbbidl->bbde.szOriginal);
MultiByteToWideChar(CP_ACP, 0,
lpFileName, -1,
pszDest, (lstrlenA(lpFileName)+1)*SIZEOF(TCHAR));
}
#else
lstrcpy(pszDest, PathFindFileName(pbbidl->bbde.szOriginal));
#endif
pszDest += lstrlen(pszDest) + 1;
Assert((UINT)((LPBYTE)pszDest - (LPBYTE)pszRet) < cbAlloc);
Assert(*(pszDest) == 0); // zero init alloc
}
Assert((LPTSTR)((LPBYTE)pszRet + cbAlloc - SIZEOF(TCHAR)) == pszDest);
Assert(*pszDest == 0); // zero init alloc
}
return pszRet;
}
extern HRESULT CFSIDLData_GetData(IDataObject * pdtobj, FORMATETC * pformatetcIn, STGMEDIUM * pmedium);
HRESULT CBBIDLData_GetData(IDataObject * pdtobj, FORMATETC * pformatetcIn, STGMEDIUM * pmedium)
{
HRESULT hres = E_INVALIDARG;
Assert(g_cfFileNameMap);
if (pformatetcIn->cfFormat == g_cfFileNameMap && (pformatetcIn->tymed & TYMED_HGLOBAL))
{
STGMEDIUM medium;
LPIDA pida = DataObj_GetHIDA(pdtobj, &medium);
if (medium.hGlobal)
{
pmedium->hGlobal = BuildDestSpecs(pida);
pmedium->tymed = TYMED_HGLOBAL;
pmedium->pUnkForRelease = NULL;
HIDA_ReleaseStgMedium(pida, &medium);
hres = pmedium->hGlobal ? NOERROR : E_OUTOFMEMORY;
}
}
else
{
hres = CFSIDLData_GetData(pdtobj, pformatetcIn, pmedium);
}
return hres;
}
#pragma data_seg(DATASEG_READONLY)
IDataObjectVtbl c_CBBIDLDataVtbl = {
CIDLData_QueryInterface,
CIDLData_AddRef,
CIDLData_Release,
CBBIDLData_GetData,
CIDLData_GetDataHere,
CBBIDLData_QueryGetData,
CIDLData_GetCanonicalFormatEtc,
CIDLData_SetData,
CIDLData_EnumFormatEtc,
CIDLData_Advise,
CIDLData_Unadvise,
CIDLData_EnumAdvise
};
#pragma data_seg()
HRESULT CShellBitBucket_SF_GetUIObjectOf(LPSHELLFOLDER psf, HWND hwndOwner, UINT cidl, LPCITEMIDLIST * apidl,
REFIID riid, UINT * prgfInOut, LPVOID * ppvOut)
{
LPSHELLBITBUCKET this = IToClass(CShellBitBucket, isf, psf);
HRESULT hres = E_NOTIMPL;
if ((cidl > 0) && IsEqualIID(riid, &IID_IDataObject))
{
hres = CIDLData_CreateFromIDArray2(&c_CBBIDLDataVtbl,
this->pidl, cidl, apidl, (IDataObject **)ppvOut);
}
else if ((cidl == 1) && (IsEqualIID(riid, &IID_IExtractIcon)
#ifdef UNICODE
|| IsEqualIID(riid, &IID_IExtractIconA)
#endif
))
{
hres = CFSFolder_CreateDefExtIcon(this->pidl, (UINT)-1, (LPCIDFOLDER)apidl[0], (LPEXTRACTICON *)ppvOut);
#ifdef UNICODE
// When UNICODE is defined the CFSFolder_CreateDefExtIcon can return
// either an IExtractIconW or an IExtractIconA pointer. We should QI
// for one that we wanted.
if (SUCCEEDED(hres))
{
LPEXTRACTICON pxicon = *ppvOut;
hres = pxicon->lpVtbl->QueryInterface(pxicon,riid,ppvOut);
pxicon->lpVtbl->Release(pxicon);
}
#endif
}
else if (IsEqualIID(riid, &IID_IContextMenu))
{
hres = CDefFolderMenu_Create(this->pidl, hwndOwner, cidl, apidl,
psf, CShellBitBucket_DFMCallBack, NULL, NULL, (LPCONTEXTMENU *)ppvOut);
}
return hres;
}
void BitBucketSaveHeader(HFILE hfile, int cNumFiles, LPBBDRIVEINFO lpbbdi)
{
BBDATAHEADER bbdh;
// write out the header back to the beginning with
// the new count in it.
if (lpbbdi->cbDataEntrySize == SIZEOF(BBDATAENTRYW)) {
bbdh.idVersion = BITBUCKET_DATAFILE_VERSION2;
} else {
bbdh.idVersion = BITBUCKET_DATAFILE_VERSION;
}
bbdh.cNumFiles = cNumFiles;
bbdh.cCurrent = lpbbdi->cFiles;
bbdh.cbDataEntrySize = lpbbdi->cbDataEntrySize;
bbdh.dwSize = lpbbdi->dwSize;
// just to make sure it's here.
_llseek(hfile, 0, 0); // go to the beginning
_lwrite(hfile, (LPSTR)&bbdh, SIZEOF(BBDATAHEADER));
#ifdef BB_DEBUG
DebugMsg(DM_TRACE, TEXT("BitBucket: Saving %d entries"), bbdh.cNumFiles);
#endif
}
void BitBucketSaveReadCache(LPBBDRIVEINFO lpbbdi)
{
TCHAR szPath[MAX_PATH];
BBDATAHEADER bbdh;
HFILE hFile;
LONG lWrite;
SHGetPathFromIDList(lpbbdi->pidl, szPath);
hFile = BitBucketLoadHeader(szPath, &bbdh, FALSE, FALSE, GENERIC_READ | GENERIC_WRITE);
#ifdef BBDEBUG
DebugDumpBB(TEXT("SavingBitBucket"), lpbbdi);
#endif
if (hFile != HFILE_ERROR) {
// this write should always succeed because we're always either truncating or saving/ never growing
lWrite = _lwrite(hFile, (LPSTR)lpbbdi->lpbbdeRead, lpbbdi->cbDataEntrySize * lpbbdi->cReadCount);
Assert(lWrite == ((LONG)(lpbbdi->cbDataEntrySize * lpbbdi->cReadCount)));
_lwrite(hFile, NULL, 0);
lpbbdi->fReadDirty = FALSE;
BitBucketSaveHeader(hFile, lpbbdi->cReadCount, lpbbdi);
_lclose(hFile);
} else {
Assert(0);
}
}
// returns -1 if nothing is cached.
// else returns the number of files cached
int BitBucketCachedFilesOnDrive(int idDrive)
{
LPBBDRIVEINFO lpbbdi;
// already cached
lpbbdi = g_pBitBucket[idDrive];
if (lpbbdi && lpbbdi->lpbbdeRead) {
#ifdef BB_DEBUG
DebugMsg(DM_TRACE, TEXT("LBB: Read cache hit"));
#endif
return lpbbdi->cReadCount;
} else
return -1;
}
// this finds out how many files are delted on this drive
int BBDeletedFilesOnDrive(int idDrive)
{
BBDATAHEADER bbdh;
int iFiles;
TCHAR szPath[MAX_PATH];
bbdh.cNumFiles = 0;
bbdh.dwSize = 0;
iFiles = BitBucketCachedFilesOnDrive(idDrive);
if (iFiles != -1)
return iFiles;
if (BitBucketableDrive(idDrive)) {
DriveIDToBBPath(idDrive, szPath);
BitBucketLoadHeader(szPath, &bbdh, TRUE, FALSE, GENERIC_READ);
return bbdh.cNumFiles;
}
return 0;
}
// returns the number of items
int BBLoadFileData(int idDrive)
{
HFILE hfile;
BBDATAHEADER bbdh;
TCHAR szPath[MAX_PATH];
bbdh.cNumFiles = 0;
bbdh.dwSize = 0;
#ifdef BB_DEBUG
DebugMsg(DM_TRACE, TEXT("BBLoadFileData: Enter"));
#endif
// allow only fixed disks
if (BitBucketableDrive(idDrive)) {
LPBBDRIVEINFO lpbbdi;
BOOL fSuccess = FALSE;
bbdh.cNumFiles = BitBucketCachedFilesOnDrive(idDrive);
if (bbdh.cNumFiles != -1)
return bbdh.cNumFiles;
DriveIDToBBPath(idDrive, szPath);
hfile = BitBucketLoadHeader(szPath, &bbdh, FALSE, FALSE, GENERIC_READ);
// LoadHeader might have created lpbbdi if it didn't exist. reassign local
lpbbdi = g_pBitBucket[idDrive];
if (hfile != HFILE_ERROR) {
if (bbdh.cNumFiles) {
int iret;
lpbbdi->lpbbdeRead = (void*)Alloc(lpbbdi->cbDataEntrySize * bbdh.cNumFiles);
if (lpbbdi->lpbbdeRead) {
int iSize = lpbbdi->cbDataEntrySize * bbdh.cNumFiles;
iret = _lread(hfile, lpbbdi->lpbbdeRead, iSize);
if (iret == iSize) {
lpbbdi->cReadCount = bbdh.cNumFiles;
fSuccess = TRUE;
}
}
}
else
{
fSuccess = TRUE;
}
_lclose(hfile);
}
if (lpbbdi && !fSuccess) {
Assert(hfile == HFILE_ERROR);
if (lpbbdi->lpbbdeRead)
Free(lpbbdi->lpbbdeRead);
lpbbdi->lpbbdeRead = NULL;
lpbbdi->fReadDirty = FALSE;
lpbbdi->cReadCount = 0;
lpbbdi->dwSize = 0;
bbdh.cNumFiles = 0;
bbdh.dwSize = 0;
}
#ifdef BBDEBUG
DebugDumpBB(TEXT("LBB: Read cache MISS"), lpbbdi);
#endif
}
return bbdh.cNumFiles;
}
LPITEMIDLIST BBDataEntryToPidl(int idDrive, LPBBDATAENTRY lpbbde, WIN32_FIND_DATA *pfd)
{
TCHAR szPath[MAX_PATH];
TCHAR szFileName[MAX_PATH];
LPITEMIDLIST pidl = NULL;
LPITEMIDLIST pidlRet = NULL;
LPBBDRIVEINFO lpbbdi = g_pBitBucket[idDrive];
BOOL fUnicode;
BBDATAENTRYIDW bbpidl;
LPVOID lpv;
LPTSTR lpszOriginal;
int iIndex;
if ( lpbbdi->cbDataEntrySize == SIZEOF(BBDATAENTRYW) ) {
LPBBDATAENTRYW lpbbdew = (LPBBDATAENTRYW)lpbbde;
WCHAR szTemp[MAX_PATH];
bbpidl.bbde.iIndex = lpbbdew->iIndex;
bbpidl.bbde.idDrive = lpbbdew->idDrive;
bbpidl.bbde.ft = lpbbdew->ft;
bbpidl.bbde.dwSize = lpbbdew->dwSize;
WideCharToMultiByte(CP_ACP, 0,
lpbbdew->szOriginal, -1,
bbpidl.bbde.szOriginal, ARRAYSIZE(bbpidl.bbde.szOriginal),
NULL, NULL );
MultiByteToWideChar(CP_ACP, 0,
bbpidl.bbde.szOriginal, -1,
szTemp, ARRAYSIZE(szTemp));
if ( lstrcmpW(lpbbdew->szOriginal,szTemp) == 0 ) {
// Create an ansi pidl
bbpidl.cb = SIZEOF(BBDATAENTRYIDA);
lpv = &bbpidl.cb;
} else {
// Create a full blown unicode pidl (both ansi and unicode names)
bbpidl.cb = SIZEOF(BBDATAENTRYIDW);
lstrcpyW(bbpidl.wszOriginal,lpbbdew->szOriginal);
lpv = &bbpidl;
}
iIndex = lpbbdew->iIndex;
#ifdef UNICODE
lpszOriginal = lpbbdew->szOriginal;
#else
lpszOriginal = bbpidl.bbde.szOriginal;
#endif
} else {
LPBBDATAENTRYA lpbbdea = (LPBBDATAENTRYA)lpbbde;
bbpidl.cb = SIZEOF(BBDATAENTRYIDA);
bbpidl.bbde = *lpbbdea;
lpv = &bbpidl.cb;
iIndex = lpbbdea->iIndex;
#ifdef UNICODE
// Use .wszOriginal as temporary location for unicode string
MultiByteToWideChar(CP_ACP, 0,
bbpidl.bbde.szOriginal, -1,
bbpidl.wszOriginal, ARRAYSIZE(bbpidl.wszOriginal));
lpszOriginal = bbpidl.wszOriginal;
#else
lpszOriginal = bbpidl.bbde.szOriginal;
#endif
}
DriveIDToBBPath(idDrive, szPath);
if (pfd) {
pidl = (LPITEMIDLIST)CFSFolder_FillIDFolder(pfd, szPath, 0L);
if (pidl) {
// NULL terminate the IDLIST
Assert(pidl && (*(USHORT UNALIGNED *)(((LPBYTE)pidl) + pidl->mkid.cb) == 0));
*(USHORT UNALIGNED *)(((LPBYTE)pidl) + pidl->mkid.cb) = 0;
}
} else {
BBGetDeletedFileName(szFileName, iIndex, lpszOriginal);
PathAppend(szPath, szFileName);
CFSFolder_CreateIDForItem(szPath, &pidl, FALSE);
}
if (pidl) {
UINT cbSize = ILGetSize(pidl);
pidlRet = _ILResize(pidl, cbSize+bbpidl.cb,0);
if (pidlRet) {
// Append this BBDATAENTRYID (A or W) onto the end
hmemcpy(_ILSkip(pidlRet,cbSize - SIZEOF(pidl->mkid.cb)),lpv,bbpidl.cb);
// And 0 terminate the thing
_ILSkip(pidlRet,cbSize+bbpidl.cb-SIZEOF(pidl->mkid.cb))->mkid.cb = 0;
// Now edit it into one larger id
ASSERT(bbpidl.cb < MAXUSHORT);
pidlRet->mkid.cb += (USHORT) bbpidl.cb;
Assert(ILGetSize(pidlRet) == cbSize+bbpidl.cb);
}
}
return pidlRet;
}
typedef struct {
LPSHELLBITBUCKET pbb;
int iBitBucket; // index into the g_pBitBucket
int iCurEntry;
int iLastEntry;
LPBBDATAENTRY lpbbde;
DWORD grfFlags;
// keep track of files not found but in info file
LPBBDATAENTRY lpbbdeWrite;
DWORD dwReduceSize;
HDPA hdpaFD;
int iLastFound;
} ENUMDELETED;
typedef struct _BB_FIND_DATA {
int iIndex;
DWORD dwFileAttributes;
FILETIME ftCreationTime;
FILETIME ftLastAccessTime;
FILETIME ftLastWriteTime;
DWORD nFileSizeHigh;
DWORD nFileSizeLow;
TCHAR szType[ 5 ]; // Only the short filetype really (".xyz" + Nul)
} BBFINDDATA, *LPBBFINDDATA;
// this is called to resync the read cache
// called from within enumerate if it finds that the directory is missing
// files...
void BitBucketSyncInfoFile(int idDrive, LPBBDATAENTRY lpbbde, DWORD dwReduceSize, int cFiles)
{
LPBBDRIVEINFO lpbbdi;
LPVOID lpTemp;
Assert(g_pBitBucket[idDrive]);
if (NULL != (lpbbdi = g_pBitBucket[idDrive])) {
if (lpbbdi->lpbbdeRead) {
lpTemp = (void*)ReAlloc(lpbbdi->lpbbdeRead,
lpbbdi->cbDataEntrySize * cFiles);
#ifdef BB_DEBUG
DebugMsg(DM_TRACE, TEXT("*********Syncing %d Files (%d bytes)... was %d"), cFiles, dwReduceSize, lpbbdi->cReadCount);
#endif
} else {
lpTemp = (void*)Alloc(SIZEOF(BBDATAENTRY) * cFiles);
#ifdef BB_DEBUG
DebugMsg(DM_TRACE, TEXT("**********Syncing %d Files (%d bytes)... there was no previous read cache"), cFiles, dwReduceSize);
#endif
}
if (lpTemp) {
ENTERCRITICAL;
hmemcpy(lpTemp, lpbbde, lpbbdi->cbDataEntrySize * cFiles);
lpbbdi->lpbbdeRead = lpTemp;
lpbbdi->cReadCount = cFiles;
lpbbdi->fReadDirty = TRUE;
if (lpbbdi->dwSize >= dwReduceSize)
lpbbdi->dwSize -= dwReduceSize;
else
lpbbdi->dwSize = 0;
LEAVECRITICAL;
}
}
}
int CALLBACK BBFDCompare(LPVOID p1, LPVOID p2, LPARAM lParam)
{
return ((LPBBFINDDATA)p1)->iIndex - ((LPBBFINDDATA)p2)->iIndex;
}
BOOL BBFillFindCache(ENUMDELETED* ped)
{
Assert(!ped->hdpaFD);
ped->iLastFound = -1;
ped->hdpaFD = DPA_Create(16);
if (ped->hdpaFD) {
HANDLE hfind;
WIN32_FIND_DATA fd;
BOOL fIsNet;
TCHAR szPath[MAX_PATH];
DriveIDToBBPath(ped->iBitBucket, szPath);
PathAppend(szPath, c_szDStarDotStar);
hfind = FindFirstFileRetry(NULL, szPath, &fd, &fIsNet);
if (hfind != INVALID_HANDLE_VALUE) {
do {
LPBBFINDDATA pbbfd;
int iIndex;
LPTSTR lpszType;
// use the long file name because the short one might have
// ~1 crap in it if the extension was long
iIndex = BBFileToIndex(fd.cFileName);
// coune have been desktop.ini or other random junk
if (iIndex == -1)
continue;
pbbfd = LocalAlloc(LPTR, SIZEOF(BBFINDDATA));
if (!pbbfd)
break;
if (DPA_InsertPtr(ped->hdpaFD, 0x7FFFFFFF, pbbfd) == -1) {
#ifdef BB_DEBUG
DebugMsg(DM_TRACE, TEXT("AAAAAAAAARRRRRRRGGGGGGGHHHHH!!!!"));
#endif
LocalFree(pbbfd);
Assert(0);
break;
}
// DO ME BABY.
pbbfd->dwFileAttributes = fd.dwFileAttributes;
pbbfd->ftCreationTime = fd.ftCreationTime;
pbbfd->ftLastAccessTime = fd.ftLastAccessTime;
pbbfd->ftLastWriteTime = fd.ftLastWriteTime;
pbbfd->nFileSizeHigh = fd.nFileSizeHigh;
pbbfd->nFileSizeLow = fd.nFileSizeLow;
lpszType = PathFindExtension(fd.cFileName);
lstrcpyn(pbbfd->szType, lpszType, ARRAYSIZE(pbbfd->szType));
pbbfd->iIndex = iIndex;
#ifdef BB_DEBUG
DebugMsg(DM_TRACE, TEXT("BBGetFindData: SURF'S UP %s!"),pbbfd->szName);
#endif
} while (FindNextFile(hfind, &fd));
FindClose(hfind);
}
DPA_Sort(ped->hdpaFD, BBFDCompare, 0);
}
return (BOOL)ped->hdpaFD;
}
BOOL BBGetFindData(ENUMDELETED* ped, WIN32_FIND_DATA* pfd)
{
TCHAR szFileName[MAX_PATH];
LPBBDATAENTRY lpbbde;
LPBBFINDDATA pbbfd;
LPBBDRIVEINFO lpbbdi;
LPTSTR lpszOriginal;
int iIndex;
#ifdef UNICODE
TCHAR szOriginal[MAX_PATH];
#endif
if (!ped->hdpaFD) {
if (!BBFillFindCache(ped))
return FALSE;
}
lpbbdi = g_pBitBucket[ped->iBitBucket];
lpbbde = (LPBBDATAENTRY)((LPBYTE)ped->lpbbde
+ (lpbbdi->cbDataEntrySize) * ped->iCurEntry);
if ( lpbbdi->cbDataEntrySize == SIZEOF(BBDATAENTRYW)) {
LPBBDATAENTRYW lpbbdew = (LPBBDATAENTRYW)lpbbde;
iIndex = lpbbdew->iIndex;
#ifdef UNICODE
lpszOriginal = lpbbdew->szOriginal;
#else
lpszOriginal = lpbbdew->szShortName;
#endif
} else {
LPBBDATAENTRYA lpbbdea = (LPBBDATAENTRYA)lpbbde;
iIndex = lpbbdea->iIndex;
#ifdef UNICODE
// Use szPath just to save stack
MultiByteToWideChar(CP_ACP, 0,
lpbbdea->szOriginal, -1,
szOriginal, ARRAYSIZE(szOriginal));
lpszOriginal = szOriginal;
#else
lpszOriginal = lpbbdea->szOriginal;
#endif
}
BBGetDeletedFileName(szFileName, iIndex, lpszOriginal);
// if the last found's index is greater than the current index, set the last found to 0
if (ped->iLastFound >= 0) {
pbbfd = DPA_GetPtr(ped->hdpaFD, ped->iLastFound);
if (pbbfd) {
Assert(ped->iLastFound < DPA_GetPtrCount(ped->hdpaFD));
if (pbbfd->iIndex > iIndex)
ped->iLastFound = -1;
}
}
// find the BBFINDDATA for this item
while (++ped->iLastFound < DPA_GetPtrCount(ped->hdpaFD)) {
pbbfd = DPA_GetPtr(ped->hdpaFD, ped->iLastFound);
if (!pbbfd) {
// this either means we've gone offt he deep end or we've
// been here before and looped around (see above LocalFree)
break;
}
// if this pbbfd's index is greater than lpbbde's index, then we've
// passed where the find data would have been and we didn't find it.
// step back one and break out
#ifdef BB_DEBUG
DebugMsg(DM_TRACE, TEXT("BB: comparing %d to %d"), pbbfd->iIndex, iIndex);
#endif
if (pbbfd->iIndex > iIndex) {
ped->iLastFound--;
break;
}
// if the indices match and the names are the same, take it!
// make sure the extensions are the same
if (pbbfd->iIndex == iIndex )
{
LPTSTR lpszType = PathFindExtension(szFileName);
TCHAR szType[ 5 ];
lstrcpyn(szType, lpszType, ARRAYSIZE(szType));
if (IntlStrEqNI(pbbfd->szType, szType, -1)) {
// DO ME BABY.
pfd->dwFileAttributes = pbbfd->dwFileAttributes;
pfd->ftCreationTime = pbbfd->ftCreationTime;
pfd->ftLastAccessTime = pbbfd->ftLastAccessTime;
pfd->ftLastWriteTime = pbbfd->ftLastWriteTime;
pfd->nFileSizeHigh = pbbfd->nFileSizeHigh;
pfd->nFileSizeLow = pbbfd->nFileSizeLow;
lstrcpy(pfd->cFileName, szFileName);
lstrcpy(pfd->cAlternateFileName, TEXT("") ); // Nobody needs this...
#ifdef BB_DEBUG
DebugMsg(DM_TRACE, TEXT("BBGetFindData: SURF'S UP!"));
#endif
return TRUE;
}
}
// we shouldn't have it where the the indices match but the names aren't the same
Assert(pbbfd->iIndex != iIndex);
}
#ifdef BB_DEBUG
DebugMsg(DM_TRACE, TEXT("BBGetFindData, Couldn't find data for %s"), szFileName);
#endif
return FALSE;
}
void BBNukeFindFirstCache(ENUMDELETED* ped)
{
int i;
LPBBFINDDATA pbbfd;
for (i = DPA_GetPtrCount(ped->hdpaFD) - 1; i >= 0; i--) {
pbbfd = DPA_FastGetPtr(ped->hdpaFD, i);
if (pbbfd)
LocalFree(pbbfd);
}
DPA_Destroy(ped->hdpaFD);
ped->hdpaFD = NULL;
}
//
// To be called back from within SHCreateEnumObjects
//
HRESULT CALLBACK CShellBitBucket_EnumCallBack(LPARAM lParam, LPVOID pvData,
UINT ecid, UINT index)
{
HRESULT hres = NOERROR;
ENUMDELETED * ped = (ENUMDELETED *)pvData;
switch (ecid) {
case ECID_SETNEXTID: {
LPITEMIDLIST pidl;
LPBBDATAENTRY lpbbde;
Assert(ped->iBitBucket < MAX_BITBUCKETS);
if (!(ped->grfFlags & SHCONTF_NONFOLDERS))
return S_FALSE; // "no more element"
do {
WIN32_FIND_DATA fd;
LPBBDRIVEINFO lpbbdi;
if (ped->iCurEntry >= ped->iLastEntry) {
if (ped->lpbbde) {
// did we find any missing files? if so, flush the cache
if (ped->lpbbdeWrite) {
BitBucketSyncInfoFile(ped->iBitBucket, ped->lpbbde, ped->dwReduceSize,
ped->lpbbdeWrite -ped->lpbbde);
ped->lpbbdeWrite = NULL;
ped->dwReduceSize = 0;
}
LocalFree((HLOCAL)ped->lpbbde);
ped->lpbbde = NULL;
}
if (ped->hdpaFD) {
BBNukeFindFirstCache(ped);
}
ped->iBitBucket++;
ped->iCurEntry = 0;
}
if (!ped->lpbbde) {
int iSize;
BOOL bFailed = FALSE;
ENTERCRITICAL;
do {
ped->iLastEntry = BBLoadFileData(ped->iBitBucket);
#ifdef BB_DEBUG
DebugMsg(DM_TRACE, TEXT("BitBucket Enum Callback: bucket %d returns %d"), ped->iBitBucket, ped->iLastEntry);
#endif
} while(!ped->iLastEntry && (++ped->iBitBucket < MAX_BITBUCKETS));
if (ped->iBitBucket < MAX_BITBUCKETS) {
lpbbdi = g_pBitBucket[ped->iBitBucket];
// found it. now copy it's data so we don't get stomped on
iSize = lpbbdi->cbDataEntrySize * ped->iLastEntry;
ped->lpbbde = (void*)LocalAlloc(LPTR, iSize);
if (ped->lpbbde)
hmemcpy(ped->lpbbde, g_pBitBucket[ped->iBitBucket]->lpbbdeRead, iSize);
else
bFailed = TRUE;
}
LEAVECRITICAL;
if ((ped->iBitBucket >= MAX_BITBUCKETS) || bFailed) {
return S_FALSE; // "no more element"
}
}
lpbbdi = g_pBitBucket[ped->iBitBucket];
lpbbde = (LPBBDATAENTRY)((LPBYTE)ped->lpbbde +
(lpbbdi->cbDataEntrySize*ped->iCurEntry));
pidl = NULL;
if (BBGetFindData(ped, &fd)) {
pidl = BBDataEntryToPidl(ped->iBitBucket, lpbbde, &fd);
}
if (pidl) {
// if we had found an entry that's now missing, this var would be != NULL.
// we need to copy out the current entry to this new location.
if (ped->lpbbdeWrite) {
hmemcpy(ped->lpbbdeWrite,lpbbde,lpbbdi->cbDataEntrySize);
ped->lpbbdeWrite = (LPBBDATAENTRY)((LPBYTE)ped->lpbbdeWrite
+ lpbbdi->cbDataEntrySize);
}
} else {
#ifdef BB_DEBUG
if ( lpbbdi->cbDataEntrySize == SIZEOF(BBDATAENTRYW)) {
LPBBDATAENTRYW lpbbdew = (LPBBDATAENTRYW)ped->lpbbde;
DebugMsg(DM_TRACE, TEXT("****************************BitBucket: Purging out of sync file: %s %d"), lpbbdew[ped->iCurEntry].szOriginal, lpbbdew[ped->iCurEntry].iIndex);
} else {
LPBBDATAENTRYA lpbbdea = (LPBBDATAENTRYA)ped->lpbbde;
DebugMsg(DM_TRACE, TEXT("****************************BitBucket: Purging out of sync file: %s %d"), lpbbdea[ped->iCurEntry].szOriginal, lpbbdea[ped->iCurEntry].iIndex);
}
#endif
// we have an entry in the info file that's no longer on disk. remove it by compacting the ped->lpbbde array
lpbbde = (LPBBDATAENTRY)((LPBYTE)ped->lpbbde +
(lpbbdi->cbDataEntrySize*ped->iCurEntry));
if (!ped->lpbbdeWrite) {
ped->lpbbdeWrite = lpbbde;
}
if (lpbbdi->cbDataEntrySize == SIZEOF(BBDATAENTRYW)) {
LPBBDATAENTRYW lpbbdew = (LPBBDATAENTRYW)lpbbde;
ped->dwReduceSize += lpbbdew->dwSize;
} else {
LPBBDATAENTRYA lpbbdea = (LPBBDATAENTRYA)lpbbde;
ped->dwReduceSize += lpbbdea->dwSize;
}
}
ped->iCurEntry++;
} while(!pidl);
CDefEnum_SetReturn(lParam, pidl);
#ifdef BBDEBUG
DebugMsg(DM_TRACE, TEXT("Enum returns %s"), PIDLTODATAENTRYID(pidl)->bbde.szOriginal);
#endif
hres = NOERROR; // in success
break;
}
case ECID_RELEASE:
if (ped->lpbbde)
LocalFree((HLOCAL)ped->lpbbde);
if (ped->hdpaFD)
BBNukeFindFirstCache(ped);
LocalFree((HLOCAL)ped);
break;
}
return hres;
}
void BBFlushCache()
{
DWORD idThread;
HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)BitBucketFlushCacheCheckPurge, (LPVOID)TRUE, 0, &idThread);
if (hThread) {
CloseHandle(hThread);
} else {
BitBucketFlushCacheCheckPurge(TRUE);
}
}
HRESULT CShellBitBucket_SF_EnumObjects(LPSHELLFOLDER psf, HWND hwndOwner,
DWORD grfFlags, LPENUMIDLIST * ppenumUnknown)
{
LPSHELLBITBUCKET this = IToClass(CShellBitBucket, isf, psf);
ENUMDELETED * ped = (void*)LocalAlloc(LPTR, SIZEOF(ENUMDELETED));
if (ped) {
BitBucketFlushCacheCheckPurge(TRUE);
//ped->iBitBucket = 0;
//ped->iCurEntry = 0;
//ped->lpbbde = 0;
ped->grfFlags = grfFlags;
ped->pbb = this;
return SHCreateEnumObjects(hwndOwner, ped, CShellBitBucket_EnumCallBack, ppenumUnknown);
}
return E_OUTOFMEMORY;
}
HRESULT CShellBitBucket_SF_GetDisplayNameOf(LPSHELLFOLDER psf,
LPCITEMIDLIST pidl, DWORD uFlags, LPSTRRET pStrRet)
{
LPBBDATAENTRYIDA pbbid;
#ifdef UNICODE
TCHAR szName[MAX_PATH];
TCHAR szTemp[MAX_PATH];
#endif
pbbid = PIDLTODATAENTRYID(pidl);
pStrRet->uType = STRRET_CSTR;
#ifdef UNICODE
if ( pbbid->cb == SIZEOF(BBDATAENTRYIDW)) {
LPBBDATAENTRYIDW pbbidlw = DATAENTRYIDATOW(pbbid);
ualstrcpyn(szTemp, pbbidlw->wszOriginal, ARRAYSIZE(szTemp));
} else {
MultiByteToWideChar( CP_ACP, 0,
pbbid->bbde.szOriginal, -1,
szTemp, ARRAYSIZE(szTemp));
}
lstrcpy(szName, PathFindFileName(szTemp));
//BUGBUG, this is wrong... see code in fstreex.c for this.
if (!FS_ShowExtension((LPIDFOLDER)pidl, FALSE)) {
PathRemoveExtension(szName);
}
pStrRet->pOleStr = (LPOLESTR)SHAlloc((lstrlen(szName)+1)*SIZEOF(TCHAR));
if ( pStrRet->pOleStr != NULL ) {
pStrRet->uType = STRRET_OLESTR;
lstrcpy(pStrRet->pOleStr, szName);
} else {
return E_OUTOFMEMORY;
}
#else
lstrcpyn(pStrRet->cStr, PathFindFileName(pbbid->bbde.szOriginal),
ARRAYSIZE(pStrRet->cStr));
//BUGBUG, this is wrong... see code in fstreex.c for this.
if (!FS_ShowExtension((LPIDFOLDER)pidl, FALSE)) {
PathRemoveExtension(pStrRet->cStr);
}
#endif
return NOERROR;
}
#pragma data_seg(".text", "CODE")
IShellFolderVtbl c_CShellBitBucketSFVtbl =
{
CShellBitBucket_SF_QueryInterface, // done
CShellBitBucket_SF_AddRef, // done
CShellBitBucket_SF_Release, // done
CShellBitBucket_SF_ParseDisplayName,
CShellBitBucket_SF_EnumObjects,
CDefShellFolder_BindToObject, // done
CDefShellFolder_BindToStorage, // done
CShellBitBucket_SF_CompareIDs, // done
CShellBitBucket_SF_CreateViewObject,
CShellBitBucket_SF_GetAttributesOf, // done
CShellBitBucket_SF_GetUIObjectOf,
CShellBitBucket_SF_GetDisplayNameOf,
CDefShellFolder_SetNameOf, // done
};
#pragma data_seg()
//========================================================================
// CShellBitBucket's PersistFile members
//========================================================================
HRESULT STDMETHODCALLTYPE CShellBitBucket_PF_QueryInterface(LPPERSISTFOLDER ppf, REFIID riid,
LPVOID * ppvObj)
{
LPSHELLBITBUCKET this = IToClass(CShellBitBucket, ipf, ppf);
return CShellBitBucket_SF_QueryInterface(&this->isf, riid, ppvObj);
}
ULONG STDMETHODCALLTYPE CShellBitBucket_PF_Release(LPPERSISTFOLDER ppf)
{
LPSHELLBITBUCKET this = IToClass(CShellBitBucket, ipf, ppf);
return CShellBitBucket_SF_Release(&this->isf);
}
ULONG STDMETHODCALLTYPE CShellBitBucket_PF_AddRef(LPPERSISTFOLDER ppf)
{
LPSHELLBITBUCKET this = IToClass(CShellBitBucket, ipf, ppf);
return CShellBitBucket_SF_AddRef(&this->isf);
}
HRESULT STDMETHODCALLTYPE CShellBitBucket_PF_GetClassID(LPPERSISTFOLDER ppf, LPCLSID lpClassID)
{
hmemcpy(lpClassID, &CLSID_ShellBitBucket, SIZEOF(CLSID_ShellBitBucket));
return NOERROR;
}
HRESULT STDMETHODCALLTYPE CShellBitBucket_PF_Initialize(LPPERSISTFOLDER ppf, LPCITEMIDLIST pidl)
{
LPSHELLBITBUCKET this = IToClass(CShellBitBucket, ipf, ppf);
this->pidl = ILClone(pidl);
return NOERROR;
}
#pragma data_seg(".text", "CODE")
IPersistFolderVtbl c_CShellBitBucketPFVtbl =
{
CShellBitBucket_PF_QueryInterface,
CShellBitBucket_PF_AddRef,
CShellBitBucket_PF_Release,
CShellBitBucket_PF_GetClassID,
CShellBitBucket_PF_Initialize
};
#pragma data_seg()
HRESULT STDMETHODCALLTYPE CShellBitBucket_SEI_QueryInterface(LPSHELLEXTINIT psei, REFIID riid,
LPVOID * ppvObj)
{
LPSHELLBITBUCKET this = IToClass(CShellBitBucket, isei, psei);
return CShellBitBucket_SF_QueryInterface(&this->isf, riid, ppvObj);
}
ULONG STDMETHODCALLTYPE CShellBitBucket_SEI_Release(LPSHELLEXTINIT psei)
{
LPSHELLBITBUCKET this = IToClass(CShellBitBucket, isei, psei);
return CShellBitBucket_SF_Release(&this->isf);
}
ULONG STDMETHODCALLTYPE CShellBitBucket_SEI_AddRef(LPSHELLEXTINIT psei)
{
LPSHELLBITBUCKET this = IToClass(CShellBitBucket, isei, psei);
return CShellBitBucket_SF_AddRef(&this->isf);
}
ULONG STDMETHODCALLTYPE CShellBitBucket_SEI_Initialize(LPSHELLEXTINIT psei,
LPCITEMIDLIST pidlFolder,
IDataObject * pdtobj,
HKEY hkeyProgID)
{
return NOERROR;
}
HRESULT STDMETHODCALLTYPE CShellBitBucket_CM_QueryInterface(LPCONTEXTMENU pcm, REFIID riid,
LPVOID * ppvObj)
{
LPSHELLBITBUCKET this = IToClass(CShellBitBucket, icm, pcm);
return CShellBitBucket_SF_QueryInterface(&this->isf, riid, ppvObj);
}
ULONG STDMETHODCALLTYPE CShellBitBucket_CM_Release(LPCONTEXTMENU pcm)
{
LPSHELLBITBUCKET this = IToClass(CShellBitBucket, icm, pcm);
return CShellBitBucket_SF_Release(&this->isf);
}
ULONG STDMETHODCALLTYPE CShellBitBucket_CM_AddRef(LPCONTEXTMENU pcm)
{
LPSHELLBITBUCKET this = IToClass(CShellBitBucket, icm, pcm);
return CShellBitBucket_SF_AddRef(&this->isf);
}
HMENU _LoadPopupMenu(UINT id);
STDMETHODIMP CShellBitBucket_QueryContextMenu(IContextMenu * pcm,
HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast,
UINT uFlags)
{
HMENU hmMerge = _LoadPopupMenu(POPUP_BITBUCKET_POPUPMERGE);
int iCommands = 0;
int idMax = idCmdFirst;
if (hmMerge) {
idMax = Shell_MergeMenus(hmenu, hmMerge, indexMenu,
idCmdFirst, idCmdLast,
0);
if (!BBTotalCount(NULL))
EnableMenuItem(hmenu, idCmdFirst + FSIDM_PURGEALL, MF_GRAYED|MF_BYCOMMAND);
DestroyMenu(hmMerge);
}
return ResultFromShort(idMax - idCmdFirst);
}
STDMETHODIMP CShellBitBucket_InvokeCommand(LPCONTEXTMENU pcm,
LPCMINVOKECOMMANDINFO pici)
{
LPSHELLBITBUCKET this = IToClass(CShellBitBucket, icm, pcm);
#ifdef BB_DEBUG
DebugMsg(DM_TRACE, TEXT("sh - tr - BitBucket_invokeCommand %d %d"), pici->lpVerb, FSIDM_PURGEALL);
#endif
switch ((UINT)pici->lpVerb) {
case FSIDM_PURGEALL:
BBPurgeAll(this, pici->hwnd);
break;
}
return NOERROR;
}
STDMETHODIMP CShellBitBucket_GetCommandString(IContextMenu *pcm,
UINT idCmd, UINT wFlags, UINT * pwReserved, LPSTR pszName, UINT cchMax)
{
#ifdef BB_DEBUG
DebugMsg(DM_TRACE, TEXT("sh - tr - idCmd = %d"), idCmd);
#endif
switch( wFlags )
{
case GCS_HELPTEXTA:
return LoadStringA(HINST_THISDLL,
idCmd + IDS_MH_FSIDM_FIRST,
pszName, cchMax) ? NOERROR : E_OUTOFMEMORY;
case GCS_HELPTEXTW:
return LoadStringW(HINST_THISDLL,
idCmd + IDS_MH_FSIDM_FIRST,
(LPWSTR)pszName, cchMax) ? NOERROR : E_OUTOFMEMORY;
default:
return E_NOTIMPL;
}
}
HRESULT CShellBitBucket_PS_QueryInterface(LPSHELLPROPSHEETEXT pps, REFIID riid,
LPVOID * ppvObj)
{
LPSHELLBITBUCKET this = IToClass(CShellBitBucket, ips, pps);
return CShellBitBucket_SF_QueryInterface(&this->isf, riid, ppvObj);
}
ULONG CShellBitBucket_PS_Release(LPSHELLPROPSHEETEXT pps)
{
LPSHELLBITBUCKET this = IToClass(CShellBitBucket, ips, pps);
return CShellBitBucket_SF_Release(&this->isf);
}
ULONG CShellBitBucket_PS_AddRef(LPSHELLPROPSHEETEXT pps)
{
LPSHELLBITBUCKET this = IToClass(CShellBitBucket, ips, pps);
return CShellBitBucket_SF_AddRef(&this->isf);
}
HRESULT CShellBitBucket_PS_AddPages(IShellPropSheetExt * pspx,
LPFNADDPROPSHEETPAGE lpfnAddPage,
LPARAM lParam)
{
HPROPSHEETPAGE hpage;
int idDrive;
int iPage;
BBPROPSHEETINFO bbpsp;
LPBBPROPSHEETINFO lppsiGlobal;
TCHAR szTitle[MAX_PATH];
bbpsp.psp.dwSize = SIZEOF(bbpsp);
bbpsp.psp.dwFlags = PSP_DEFAULT | PSP_SHPAGE;
bbpsp.psp.hInstance = HINST_THISDLL;
bbpsp.psp.pszTemplate = MAKEINTRESOURCE(DLG_BITBUCKET_GENCONFIG);
bbpsp.psp.pfnDlgProc = BBGenPropDlgProc;
// add the general page
hpage = CreatePropertySheetPage(&bbpsp.psp);
lppsiGlobal = (LPBBPROPSHEETINFO)hpage;
lppsiGlobal->pGlobal = lppsiGlobal;
lpfnAddPage(hpage, lParam);
bbpsp.psp.dwFlags = PSP_USETITLE | PSP_SHPAGE;
bbpsp.psp.pszTemplate = MAKEINTRESOURCE(DLG_BITBUCKET_CONFIG);
bbpsp.psp.pfnDlgProc = BBPropDlgProc;
bbpsp.psp.pszTitle = szTitle;
for (idDrive = 0, iPage = 1; (idDrive < MAX_BITBUCKETS) && (iPage < MAXPROPPAGES); idDrive++) {
if (MakeBitBucket(idDrive)) {
bbpsp.idDrive = idDrive;
bbpsp.pGlobal = lppsiGlobal;
BBGetDriveName(idDrive, szTitle, ARRAYSIZE(szTitle));
hpage = CreatePropertySheetPage(&bbpsp.psp);
lpfnAddPage(hpage, lParam);
}
}
return NOERROR;
}
#pragma data_seg(".text", "CODE")
STATIC IContextMenuVtbl c_CShellBitBucketCMVtbl =
{
CShellBitBucket_CM_QueryInterface,
CShellBitBucket_CM_AddRef,
CShellBitBucket_CM_Release,
CShellBitBucket_QueryContextMenu,
CShellBitBucket_InvokeCommand,
CShellBitBucket_GetCommandString
};
IShellPropSheetExtVtbl c_CShellBitBucketPSVtbl =
{
CShellBitBucket_PS_QueryInterface,
CShellBitBucket_PS_AddRef,
CShellBitBucket_PS_Release,
CShellBitBucket_PS_AddPages,
CCommonShellPropSheetExt_ReplacePage,
};
STATIC IShellExtInitVtbl c_CShellBitBucketSEIVtbl =
{
CShellBitBucket_SEI_QueryInterface,
CShellBitBucket_SEI_AddRef,
CShellBitBucket_SEI_Release,
CShellBitBucket_SEI_Initialize
};
#pragma data_seg()
HRESULT CALLBACK CShellBitBucket_CreateInstance(LPUNKNOWN punkOuter, REFIID riid, LPVOID * ppvOut)
{
LPSHELLBITBUCKET pbb;
HRESULT hres = E_OUTOFMEMORY; // assume error;
Assert(punkOuter == NULL);
pbb = (void*)LocalAlloc(LPTR, SIZEOF(CShellBitBucket));
if (pbb)
{
pbb->isf.lpVtbl = &c_CShellBitBucketSFVtbl;
pbb->ipf.lpVtbl = &c_CShellBitBucketPFVtbl;
pbb->icm.lpVtbl = &c_CShellBitBucketCMVtbl;
pbb->isei.lpVtbl = &c_CShellBitBucketSEIVtbl;
pbb->ips.lpVtbl = &c_CShellBitBucketPSVtbl;
pbb->cRef = 1;
hres = CShellBitBucket_SF_QueryInterface(&pbb->isf, riid, ppvOut);
CShellBitBucket_SF_Release(&pbb->isf);
}
return hres;
}
int CreateFileRetry(LPTSTR lpszPath, DWORD dwRWStyle, DWORD dwShareMode,
DWORD dwCreate, DWORD dwFlags)
{
int i = 0;
HFILE hfile = -1;
while ((i++ < OPENFILERETRYCOUNT) && (hfile == -1)) {
hfile = (HFILE)CreateFile(lpszPath, dwRWStyle, dwShareMode, 0L, dwCreate, dwFlags, NULL);
if ((hfile != HFILE_ERROR) || (GetLastError() != DE_ACCESSDENIED)) {
return hfile;
}
#ifdef BB_DEBUG
DebugMsg(DM_TRACE, TEXT("Bitbucket got access denied. sleeping then trying again"));
#endif
Sleep(OPENFILERETRYTIME);
}
}
// allocates a driveinfo structure and assigns it to the global array
LPBBDRIVEINFO BitBucketAllocDriveInfo(int idDrive, LPBBDATAHEADER lpbbdh)
{
LPBBDRIVEINFO lpbbdi = (void*)Alloc(SIZEOF(BBDRIVEINFO));
if (lpbbdi)
{
LPITEMIDLIST pidlLocal;
TCHAR szBitBucket[MAX_PATH];
DriveIDToBBPath(idDrive, szBitBucket);
pidlLocal = ILCreateFromPath(szBitBucket);
if (pidlLocal) {
lpbbdi->pidl = ILGlobalClone(pidlLocal);
ILFree(pidlLocal);
if (lpbbdi->pidl) {
lpbbdi->cFiles = lpbbdh->cCurrent;
lpbbdi->dwSize = lpbbdh->dwSize;
lpbbdi->cbDataEntrySize = lpbbdh->cbDataEntrySize;
g_pBitBucket[idDrive] = lpbbdi;
BitBucketGetDriveSettings(lpbbdi, idDrive, NULL);
}
}
if (!lpbbdi->pidl) {
Free(lpbbdi);
lpbbdi = NULL;
}
}
return lpbbdi;
}
HFILE BitBucketLoadHeader(LPTSTR lpszPath, LPBBDATAHEADER lpbbdh, BOOL fClose, BOOL fCreateDriveInfo, DWORD dwRWStyle)
{
OFSTRUCT of;
HFILE hfile;
int idDrive = BBPathGetDriveNumber(lpszPath);
PathAppend(lpszPath, c_szInfo);
of.cBytes = SIZEOF(OFSTRUCT);
if ((hfile = CreateFileRetry(lpszPath, dwRWStyle, FILE_SHARE_READ, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN))
!= HFILE_ERROR) {
if (_lread(hfile, lpbbdh, SIZEOF(BBDATAHEADER)) == -1) {
Assert(0); // Read failure
goto SetupDefaults;
}
if (lpbbdh->idVersion == BITBUCKET_DATAFILE_VERSION) {
if (lpbbdh->cbDataEntrySize != SIZEOF(BBDATAENTRYA)) {
Assert(0); // Version0 doesn't match entry size
goto SetupDefaults;
}
} else if (lpbbdh->idVersion == BITBUCKET_DATAFILE_VERSION2) {
if (lpbbdh->cbDataEntrySize != SIZEOF(BBDATAENTRYW)) {
Assert(0); // Version2 doesn't match entry size
goto SetupDefaults;
}
} else {
Assert(0); // Unknown version
goto SetupDefaults;
}
if (!g_pBitBucket[idDrive]) {
// there's a bitbucket here, add it to the g_pBitBucket
fCreateDriveInfo = TRUE;
}
} else {
SetupDefaults:
// else set up defaults
#ifdef UNICODE
lpbbdh->idVersion = BITBUCKET_DATAFILE_VERSION2;
#else
lpbbdh->idVersion = BITBUCKET_DATAFILE_VERSION;
#endif
lpbbdh->cNumFiles = 0;
lpbbdh->cCurrent = 0;
lpbbdh->cbDataEntrySize = SIZEOF(BBDATAENTRY);
lpbbdh->dwSize = 0;
}
if (fCreateDriveInfo) {
BitBucketAllocDriveInfo(idDrive, lpbbdh);
}
if (fClose && hfile != HFILE_ERROR)
_lclose(hfile);
return hfile;
}
// returns true or false if anything's been written
BOOL SaveDeletedFileInfo(LPBBDRIVEINFO lpbbdi, BOOL fForceCleanRead)
{
int i;
int iMax = 0;
LPBBDATAENTRY lpbbde;
HFILE hfile;
TCHAR szInfoFile[MAX_PATH];
BOOL fCacheDirty;
BOOL fReturn = FALSE;
ENTERCRITICAL;
fCacheDirty = BitBucketDeleteCacheIsDirty(lpbbdi);
LEAVECRITICAL;
// nuke the read cache
if (fCacheDirty || fForceCleanRead) {
if (lpbbdi->lpbbdeRead) {
LPBBDATAENTRY lpTemp;
if (lpbbdi->fReadDirty) {
fReturn = TRUE;
BitBucketSaveReadCache(lpbbdi);
}
ENTERCRITICAL;
lpTemp = lpbbdi->lpbbdeRead;
lpbbdi->lpbbdeRead = NULL;
lpbbdi->cReadCount = 0;
lpbbdi->fReadDirty = FALSE;
LEAVECRITICAL;
Free(lpTemp);
}
}
// don't cache the count because this one isn't and shouldn't be
// within a critical section
if (fCacheDirty) {
OFSTRUCT of;
BBDATAHEADER bbdh;
DWORD lPos = 0;
int idDrive;
SHGetPathFromIDList(lpbbdi->pidl, szInfoFile);
idDrive = BBPathGetDriveNumber(szInfoFile);
BitBucketLoadHeader(szInfoFile, &bbdh, TRUE, FALSE, GENERIC_READ); // this appends the c_szInfo for us
of.cBytes = SIZEOF(OFSTRUCT);
hfile = CreateFileRetry(szInfoFile, GENERIC_READ | GENERIC_WRITE, 0,
OPEN_ALWAYS, FILE_ATTRIBUTE_HIDDEN | FILE_FLAG_RANDOM_ACCESS);
if (hfile != HFILE_ERROR) {
// just to make sure it's here.
if (_lwrite(hfile, (LPSTR)&bbdh, SIZEOF(bbdh)) != SIZEOF(bbdh)) {
_lclose(hfile);
goto HandleDiskFull;
}
lPos = _llseek(hfile, 0, 2); // go to the end
if (lpbbdi->hdpaDeleteCache) {
ENTERCRITICAL;
if (lpbbdi->hdpaDeleteCache) {
iMax = DPA_GetPtrCount(lpbbdi->hdpaDeleteCache);
for (i = 0 ; i < iMax; i++) {
fReturn = TRUE;
lpbbde = DPA_FastGetPtr(lpbbdi->hdpaDeleteCache, i);
// write it!
if (_lwrite(hfile, (LPSTR)lpbbde, lpbbdi->cbDataEntrySize) != lpbbdi->cbDataEntrySize)
{
// out of disk space... go to recovery
_llseek(hfile, lPos, 0);
_lclose(hfile);
LEAVECRITICAL;
goto HandleDiskFull;
}
}
// do this in two separate loops because we could have
// had to bail on disk full
for (i = 0 ;i < iMax; i++) {
lpbbde = DPA_FastGetPtr(lpbbdi->hdpaDeleteCache, i);
Free(lpbbde);
}
DPA_DeleteAllPtrs(lpbbdi->hdpaDeleteCache);
}
LEAVECRITICAL;
}
BitBucketSaveHeader(hfile, bbdh.cNumFiles + iMax, lpbbdi);
_lclose(hfile);
} else {
/// DISK FULL recovery.
TCHAR szDrive[MAX_PATH];
DWORD dwFreeSpace;
DWORD dwSectorsPerCluster, dwBytesPerSector, dwFreeClusters, dwTotalClusters;
HandleDiskFull:
// check to see if we have enough disk space
BBPathBuildRoot(szDrive, idDrive);
PathStripToRoot(szDrive);
if (GetDiskFreeSpace(szDrive, &dwSectorsPerCluster, &dwBytesPerSector,
&dwFreeClusters, &dwTotalClusters)) {
dwFreeSpace = (dwFreeClusters * dwSectorsPerCluster * dwBytesPerSector);
if (MAX_WRITE_SIZE > dwFreeSpace) {
int iMax;
int i;
TCHAR szPath[MAX_PATH];
TCHAR szFile[MAX_PATH];
LPTSTR lpszOriginal;
int iIndex;
#ifdef BB_DEBUG
DebugMsg(DM_TRACE, TEXT("Unable to open info file because we're out of diskpace.. trying to recover"));
#endif
iMax = BBLoadFileData(idDrive);
for (i = 0 ; (i < iMax) && (dwFreeSpace < MAX_WRITE_SIZE); i++) {
#ifdef BB_DEBUG
DebugMsg(DM_TRACE, TEXT("**********Freespace = %d"), dwFreeSpace);
#endif
lpbbde = &lpbbdi->lpbbdeRead[0];
// get the file name
if ( lpbbdi->cbDataEntrySize == SIZEOF(BBDATAENTRYW)) {
LPBBDATAENTRYW lpbbdew = (LPBBDATAENTRYW)lpbbde;
#ifdef UNICODE
lpszOriginal = lpbbdew->szOriginal;
#else
lpszOriginal = lpbbdew->szShortName;
#endif
} else {
LPBBDATAENTRYA lpbbdea = (LPBBDATAENTRYA)lpbbde;
#ifdef UNICODE
// Use szPath just to save stack
MultiByteToWideChar(CP_ACP, 0,
lpbbdea->szOriginal, -1,
szPath, ARRAYSIZE(szPath));
lpszOriginal = szPath;
#else
lpszOriginal = lpbbdea->szOriginal;
#endif
}
BBGetDeletedFileName(szFile, lpbbde->iIndex, lpszOriginal);
DriveIDToBBPath(idDrive, szPath);
PathAppend(szPath, szFile);
// now do the deletion and update the info
#ifdef BB_DEBUG
DebugMsg(DM_TRACE, TEXT("********Deleteing %s**** fordisk full"), szPath);
#endif
BBDelete(szPath);
dwFreeSpace += lpbbde->dwSize;
iIndex = lpbbde->iIndex;
ENTERCRITICAL;
if (BBNukeFileInfoByFileIndex(lpbbdi, iIndex)) {
iMax--;
i--;
}
LEAVECRITICAL;
}
if ((dwFreeSpace < MAX_WRITE_SIZE) && (i == iMax) ) {
// we didn't delete enough, now look into our dpa cache
ENTERCRITICAL;
if (lpbbdi->hdpaDeleteCache) {
iMax = DPA_GetPtrCount( lpbbdi->hdpaDeleteCache);
for (i = 0; i < iMax; i++) {
#ifdef BB_DEBUG
DebugMsg(DM_TRACE, TEXT("**********Freespace = %d"), dwFreeSpace);
#endif
lpbbde = DPA_FastGetPtr(lpbbdi->hdpaDeleteCache, i);
// get the file name
if ( lpbbdi->cbDataEntrySize == SIZEOF(BBDATAENTRYW)) {
LPBBDATAENTRYW lpbbdew = (LPBBDATAENTRYW)lpbbde;
#ifdef UNICODE
lpszOriginal = lpbbdew->szOriginal;
#else
lpszOriginal = lpbbdew->szShortName;
#endif
} else {
LPBBDATAENTRYA lpbbdea = (LPBBDATAENTRYA)lpbbde;
#ifdef UNICODE
// Using szPath just to save stack space
MultiByteToWideChar(CP_ACP, 0,
lpbbdea->szOriginal, -1,
szPath, ARRAYSIZE(szPath));
lpszOriginal = szPath;
#else
lpszOriginal = lpbbdea->szOriginal;
#endif
}
BBGetDeletedFileName(szFile, lpbbde->iIndex, lpszOriginal);
DriveIDToBBPath(idDrive, szPath);
PathAppend(szPath, szFile);
// now do the deletion and update the info
#ifdef BB_DEBUG
DebugMsg(DM_TRACE, TEXT("********Deleteing %s**** fordisk full"), szPath);
#endif
BBDelete(szPath);
dwFreeSpace += lpbbde->dwSize;
if (lpbbdi->dwSize >= lpbbde->dwSize)
lpbbdi->dwSize -= lpbbde->dwSize;
DPA_DeletePtr(lpbbdi->hdpaDeleteCache, i);
iMax--;
i--;
}
}
LEAVECRITICAL;
}
if (dwFreeSpace >= MAX_WRITE_SIZE) {
/// YES!! we cleared up enough space.
// let's try again.
return SaveDeletedFileInfo(lpbbdi, fForceCleanRead);
} else {
// we're really bummed!
Assert(0);
}
} else {
#ifdef BB_DEBUG
DebugMsg(DM_ERROR, TEXT("I have no clue why we can't open the file"));
#endif
}
}
}
}
return fReturn;
}
void BBAddDeletedFileInfo(LPBBDRIVEINFO lpbbdi, LPTSTR lpszOriginal, int iIndex, int idDrive, DWORD dwSize)
{
LPBBDATAENTRY lpbbde;
SYSTEMTIME st;
#ifdef UNICODE
WCHAR szTemp[MAX_PATH];
#endif
lpbbde = (LPBBDATAENTRY)Alloc(lpbbdi->cbDataEntrySize);
if (lpbbde) {
if ( lpbbdi->cbDataEntrySize == SIZEOF(BBDATAENTRYW) )
{
LPBBDATAENTRYW lpbbdew = (LPBBDATAENTRYW)lpbbde;
#ifdef UNICODE
// Create a BBDATAENTRYW from a unicode name
lstrcpy(lpbbdew->szOriginal, lpszOriginal);
WideCharToMultiByte(CP_ACP, 0,
lpszOriginal, -1,
lpbbdew->szShortName, ARRAYSIZE(lpbbdew->szShortName),
NULL, NULL);
MultiByteToWideChar(CP_ACP, 0,
lpbbdew->szShortName, -1,
szTemp, ARRAYSIZE(szTemp));
if ( lstrcmp(lpszOriginal,szTemp) != 0 ) {
WCHAR szShort[MAX_PATH];
GetShortPathName(lpszOriginal,szShort,ARRAYSIZE(szShort));
WideCharToMultiByte(CP_ACP, 0,
szShort, -1,
lpbbdew->szShortName, ARRAYSIZE(lpbbdew->szShortName),
NULL, NULL);
}
#else
// Create a BBDATAENTRYW from an ansi name
lstrcpy(lpbbdew->szShortName, lpszOriginal);
MultiByteToWideChar(CP_ACP, 0,
lpszOriginal, -1,
lpbbdew->szOriginal, ARRAYSIZE(lpbbdew->szOriginal));
#endif
}
else
{
LPBBDATAENTRYA lpbbdea = (LPBBDATAENTRYA)lpbbde;
#ifdef UNICODE
// Create a BBDATAENTRYA from a unicode name
WideCharToMultiByte(CP_ACP, 0,
lpszOriginal, -1,
lpbbdea->szOriginal, ARRAYSIZE(lpbbdea->szOriginal),
NULL, NULL);
MultiByteToWideChar(CP_ACP, 0,
lpbbdea->szOriginal, -1,
szTemp, ARRAYSIZE(szTemp));
if ( lstrcmp(lpszOriginal,szTemp) != 0 ) {
WCHAR szShort[MAX_PATH];
GetShortPathName(lpszOriginal,szShort,ARRAYSIZE(szShort));
WideCharToMultiByte(CP_ACP, 0,
szShort, -1,
lpbbdea->szOriginal, ARRAYSIZE(lpbbdea->szOriginal),
NULL, NULL);
}
#else
// Create a BBDATAENTRYA from an ansi name
lstrcpy(lpbbdea->szOriginal, lpszOriginal);
#endif
}
lpbbde->iIndex = iIndex;
lpbbde->idDrive = idDrive;
// round up to nearest sector size
lpbbde->dwSize = (dwSize + lpbbdi->dwClusterSize)
- (dwSize % lpbbdi->dwClusterSize);
GetSystemTime(&st); // Get time of deletion
SystemTimeToFileTime(&st, &lpbbde->ft);
lpbbdi->dwSize += lpbbde->dwSize;
ENTERCRITICAL;
if (!lpbbdi->hdpaDeleteCache) {
lpbbdi->hdpaDeleteCache = DPA_Create(64);
}
if (lpbbdi->hdpaDeleteCache) {
DPA_InsertPtr(lpbbdi->hdpaDeleteCache, 0x7FFFFFFF, lpbbde);
}
LEAVECRITICAL;
}
}
void BBTerminate()
{
int i;
for (i = 0; i < MAX_BITBUCKETS ; i++) {
if (g_pBitBucket[i]) {
// commit any cached entries
SaveDeletedFileInfo(g_pBitBucket[i], TRUE);
// free it
if (g_pBitBucket[i]->hdpaDeleteCache)
DPA_Destroy(g_pBitBucket[i]->hdpaDeleteCache);
ILGlobalFree(g_pBitBucket[i]->pidl);
Free(g_pBitBucket[i]);
g_pBitBucket[i] = 0;
}
}
if (g_szNetHomeDir && (g_szNetHomeDir != NONETHOMEDIR))
Str_SetPtr(&g_szNetHomeDir, NULL); // this frees and zeros
}
BOOL InitializeRecycledDirectory(int idDrive)
{
TCHAR szPath[MAX_PATH];
PSECURITY_ATTRIBUTES psa = NULL;
BOOL bResult = FALSE;
BOOL bExists;
DriveIDToBBPath(idDrive, szPath);
bExists = PathIsDirectory(szPath);
if (!bExists)
{
#ifdef WINNT
if (DriveIsSecure(idDrive))
psa = GetUserSecurityAttributes(TRUE);
#endif
bExists = (SHCreateDirectoryEx(NULL, szPath, psa) == 0);
}
if (bExists) {
TCHAR szTemp[MAX_PATH];
// setup the desktop.ini
lstrcpy(szTemp, szPath);
PathAppend(szTemp, c_szDesktopIni);
WritePrivateProfileString(c_szClassInfo, c_szCLSID, c_szBITBUCKET_CLASSID, szTemp);
SetFileAttributes(szTemp, FILE_ATTRIBUTE_HIDDEN);
// Hide all of the directories along the way
do {
lstrcpy(szTemp, szPath);
SetFileAttributes(szTemp,FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
PathRemoveFileSpec(szPath);
} while (!PathIsRoot(szPath));
// everything's set. let's add it in
// try to load the and initalize g_pBitBuckets
bResult = TRUE;
}
#ifdef WINNT
if (psa)
LocalFree((HLOCAL)psa);
#endif
return bResult;
}
// this sets up the bitbucket directory and allocs the internal structure
// if it's a bitbucketable drive
LPBBDRIVEINFO MakeBitBucket(int idDrive)
{
if (idDrive != -1) {
LPBBDRIVEINFO lpbbdi;
lpbbdi = g_pBitBucket[idDrive];
if (!lpbbdi) {
if (BitBucketableDrive(idDrive) && InitializeRecycledDirectory(idDrive)) {
TCHAR szPath[MAX_PATH];
BBDATAHEADER bbdh;
DriveIDToBBPath(idDrive, szPath);
BitBucketLoadHeader(szPath, &bbdh, TRUE, TRUE, GENERIC_READ);
}
}
return g_pBitBucket[idDrive];
}
return NULL;
}
DWORD CALLBACK BitBucketFlushCacheCheckPurge(BOOL fCheckPurge)
{
int i;
DWORD dwTrashSize = (DWORD)-1;
#ifdef BB_DEBUG
DebugMsg(DM_TRACE, TEXT("Flushing bitbuckets"));
#endif
g_iCheckPurgeCount = 0;
for (i = 0; i < MAX_BITBUCKETS ; i++) {
if (g_pBitBucket[i]) {
// commit any cached entries
if (SaveDeletedFileInfo(g_pBitBucket[i], TRUE) && fCheckPurge) {
if (dwTrashSize == -1)
dwTrashSize = 0;
dwTrashSize += BBCheckPurgeFiles(i);
}
}
}
if (fCheckPurge) {
SHUpdateRecycleBinIcon();
SHChangeNotify(0, SHCNF_FLUSH | SHCNF_FLUSHNOWAIT, NULL, NULL);
}
return 0;
}
// tells if a file will *likely* be recycled...
// this could be wrong if
// * disk is full
// * file is really a folder
// * file greater than the allocated size for the recycle directory
LPBBDRIVEINFO IBitBucketWillRecycle(LPCTSTR lpszFile)
{
LPBBDRIVEINFO lpbbdi;
lpbbdi = MakeBitBucket(BBPathGetDriveNumber(lpszFile));
if ((lpbbdi && !lpbbdi->fNukeOnDelete && lpbbdi->iPercent))
return lpbbdi;
else
return NULL;
}
BOOL BitBucketWillRecycle(LPCTSTR lpszFile)
{
return (BOOL)IBitBucketWillRecycle(lpszFile);
}
BOOL BBDeleteFile(LPTSTR lpszFile, LPINT lpiReturn, LPUNDOATOM lpua, BOOL fIsDir, WIN32_FIND_DATA *pfd)
{
int iRet;
LPBBDRIVEINFO lpbbdi;
DWORD dwAttributes;
TCHAR szBitBucket[MAX_PATH];
TCHAR szFileName[MAX_PATH];
int cNumFiles = 0;
int cFiles;
int idDrive = BBPathGetDriveNumber(lpszFile);
if (g_iCheckPurgeCount++ > CHECK_PURGE_MAX) {
BitBucketFlushCacheCheckPurge(FALSE);
FOUndo_Release(lpua);
}
lpbbdi = IBitBucketWillRecycle(lpszFile);
if (lpbbdi) {
ULARGE_INTEGER ulSize;
// if we drag a bitbucket dir into the wastebasket, really delete it.
SHGetPathFromIDList(lpbbdi->pidl, szBitBucket);
if (fIsDir) {
// we need to calculate the size of this directory
FOLDERCONTENTSINFO fci;
fci.iSize = 0;
fci.cFiles = 0;
fci.bContinue = TRUE;
if (!CheckFolderSizeAndDeleteability(lpszFile, &fci)) {
return FALSE;
}
ulSize.QuadPart = fci.iSize;
} else {
if (!IsFileDeletable(lpszFile)) {
return FALSE;
}
ulSize.LowPart = pfd->nFileSizeLow;
ulSize.HighPart = pfd->nFileSizeHigh;
}
// check to make sure ti's not bigger than the allowed wastebasket..
// if it is nuke it now.
if (lpbbdi->cbMaxSize <= ulSize.QuadPart) {
#ifdef BB_DEBUG
DebugMsg(DM_TRACE, TEXT("Doing a true delete because the file is too big"));
#endif
return FALSE;
}
// get the target and move it
// put this in a critical section so that
// two different threads don't get the same number
ENTERCRITICAL;
cFiles = lpbbdi->cFiles++; // grab this and inc the count
lpbbdi->cFiles %= DELETEMAX;
LEAVECRITICAL;
BBGetDeletedFileName(szFileName, cFiles, lpszFile);
CharUpperBuff(szFileName, ARRAYSIZE(szFileName));
PathAppend(szBitBucket, szFileName);
TryMoveAgain:
iRet = Win32MoveFile(lpszFile, szBitBucket, fIsDir);
// do GetLastError here so that we don't get the last error from the PathFileExists
*lpiReturn = (iRet ? 0 : GetLastError());
if (!iRet) {
if (BBDelete(szBitBucket)) {
// there was already this file (maybe from an oldinstall
goto TryMoveAgain;
} else {
// is our recycled directory still there?
TCHAR szTemp[MAX_PATH];
SHGetPathFromIDList(lpbbdi->pidl, szTemp);
// if it already exists or there was some error in creating it, bail
// else try again
if (PathIsDirectory(szTemp) || !InitializeRecycledDirectory(idDrive))
SetLastError(*lpiReturn);
else
goto TryMoveAgain;
}
} else {
// success!
BBAddDeletedFileInfo(lpbbdi, lpszFile, cFiles, BBPathGetDriveNumber(szBitBucket), ulSize.LowPart);
if (lpua)
FOUndo_AddInfo(lpua, lpszFile, szBitBucket, 0);
}
return TRUE;
}
return FALSE;
}
BOOL IsFileInBitBucket(LPCTSTR pszPath)
{
// Basically it understands how we the trash is layed out which is fine
// as we are in the bitbucket code file... So we skip the first 3
// characters for the root of the name: c:\ and we truncate off the
// last part of the name and the rest should match our deathrow name...
TCHAR szPath[MAX_PATH];
int idDrive = BBPathGetDriveNumber(pszPath);
if (idDrive != -1) {
if (BitBucketableDrive(idDrive)) {
DriveIDToBBPath(idDrive, szPath);
return(PathCommonPrefix(szPath, pszPath, NULL) == lstrlen(szPath));
}
}
return FALSE;
}
// this takes two sparated/terminated lists of files
void BBUndeleteFiles(LPCTSTR lpszOriginal, LPCTSTR lpszDelFile)
{
SHFILEOPSTRUCT sFileOp =
{
NULL,
FO_MOVE,
lpszDelFile,
lpszOriginal,
FOF_NOCONFIRMATION | FOF_MULTIDESTFILES | FOF_SIMPLEPROGRESS,
} ;
// BUGBUG: we need to find out specific information about which
// file ops were aborted
if (SHFileOperation(&sFileOp) == 0 && !sFileOp.fAnyOperationsAborted) {
while (*lpszDelFile) {
BBNukeFileInfo(lpszDelFile);
lpszDelFile += lstrlen(lpszDelFile) + 1;
}
SHUpdateRecycleBinIcon();
}
}
void SHUpdateRecycleBinIcon()
{
UpdateIcon(BBTotalCount(NULL));
}
BOOL BBGetPathFromIDList(LPCITEMIDLIST pidl, LPTSTR lpszPath, UINT uOpts)
{
if (!ILIsEmpty(pidl)) {
TCHAR szName[MAX_PATH];
#ifdef UNICODE
TCHAR szOriginal[MAX_PATH];
#endif
UNALIGNED BBDATAENTRYIDA * pbbidl = PIDLTODATAENTRYID(pidl);
DriveIDToBBPath(pbbidl->bbde.idDrive, lpszPath);
#ifdef UNICODE
if (pbbidl->cb == SIZEOF(BBDATAENTRYIDW)) {
LPBBDATAENTRYIDW pbbidlw = DATAENTRYIDATOW(pbbidl);
ualstrcpyn(szOriginal, pbbidlw->wszOriginal, ARRAYSIZE(szOriginal));
} else {
MultiByteToWideChar(CP_ACP, 0,
pbbidl->bbde.szOriginal, -1,
szOriginal, ARRAYSIZE(szOriginal));
}
BBGetDeletedFileName(szName, pbbidl->bbde.iIndex, szOriginal);
#else
BBGetDeletedFileName(szName, pbbidl->bbde.iIndex, pbbidl->bbde.szOriginal);
#endif
PathAppend(lpszPath, szName);
return TRUE;
}
return FALSE;
}
int CALLBACK DiskFullDlgProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam)
{
switch (wMsg) {
case WM_INITDIALOG:
{
int idDrive = (int)lParam;
TCHAR szNewText[MAX_PATH];
TCHAR szText[MAX_PATH];
TCHAR szSize[80];
// sanity check
if (!g_pBitBucket[idDrive])
break;
GetDlgItemText(hDlg, IDD_TEXT, szText, ARRAYSIZE(szText));
wsprintf(szNewText, szText, TEXT('A') + idDrive);
SetDlgItemText(hDlg, IDD_TEXT, szNewText);
GetDlgItemText(hDlg, IDD_TEXT1, szText, ARRAYSIZE(szText));
ShortSizeFormat(g_pBitBucket[idDrive]->dwSize, szSize);
wsprintf(szNewText, szText, szSize);
SetDlgItemText(hDlg, IDD_TEXT1, szNewText);
break;
}
case WM_COMMAND:
switch (GET_WM_COMMAND_ID(wParam, lParam)) {
case IDD_OPEN:
case IDD_EMPTY:
case IDCANCEL:
EndDialog(hDlg, GET_WM_COMMAND_ID(wParam, lParam));
break;
}
break;
default:
return FALSE;
}
return TRUE;
}
void WINAPI SHHandleDiskFull(HWND hwnd, int idDrive)
{
if ((idDrive >= 0) && (idDrive < MAX_DRIVES)) {
if (BitBucketableDrive(idDrive)) {
LPBBDRIVEINFO lpbbdi;
lpbbdi = MakeBitBucket(idDrive);
if (lpbbdi && lpbbdi->dwSize) {
int ret;
ret = DialogBoxParam(HINST_THISDLL, MAKEINTRESOURCE(DLG_DISKFULL), hwnd, DiskFullDlgProc, (LPARAM)idDrive);
switch(ret) {
case IDD_OPEN: {
SHELLEXECUTEINFO ei;
LPITEMIDLIST pidlBitBuck = SHCloneSpecialIDList(hwnd, CSIDL_BITBUCKET, TRUE);
if (pidlBitBuck) {
FillExecInfo(ei, hwnd, c_szOpen, szNULL, NULL, szNULL, SW_NORMAL);
ei.fMask |= SEE_MASK_IDLIST | SEE_MASK_CLASSNAME;
ei.lpClass = c_szFolderClass;
ei.lpIDList = pidlBitBuck;
ShellExecuteEx(&ei);
ILFree(pidlBitBuck);
}
break;
}
case IDD_EMPTY: {
TCHAR szPath[MAX_PATH];
SHFILEOPSTRUCT sFileOp =
{
hwnd,
FO_DELETE,
szPath,
NULL,
FOF_NOCONFIRMATION | FOF_SIMPLEPROGRESS,
FALSE,
NULL,
(LPCTSTR)IDS_BB_EMPTYINGWASTEBASKET
} ;
DriveIDToBBPath(idDrive, szPath);
PathAppend(szPath, c_szStarDotStar);
szPath[lstrlen(szPath) + 1] = 0; // double null terminate
SHFileOperation(&sFileOp);
ResetDriveInfoStruct(g_pBitBucket[idDrive]);
SHUpdateRecycleBinIcon();
break;
}
default:
break;
}
}
}
}
}
// This function adds up the sizes of the files in pszDir, and
// also makes sure that all those files are "delete-able"
//
// return: TRUE all the files in the dir are deleteable
// FALSE the dir cant be deleted because a file is in use
//
// this function will also change the value of pszDir to be the file
// that is not deleteable so that the correct non-deleteable file will
// displayed in case there is an error
//
BOOL CheckFolderSizeAndDeleteability(LPCTSTR pszDir, FOLDERCONTENTSINFO *pfci)
{
TCHAR szPath[MAX_PATH];
TCHAR szFile[MAX_PATH];
if (!pfci->bContinue)
return TRUE;
pfci->cFolders++;
if (!IsDirectoryDeletable(pszDir))
{
// initial folder cant be deleted
return FALSE;
}
if (PathCombine(szPath, pszDir, c_szStarDotStar))
{
HANDLE hfind = FindFirstFile(szPath, &pfci->fd);
if (hfind != INVALID_HANDLE_VALUE)
{
do
{
if (!PathIsDotOrDotDot(pfci->fd.cFileName))
{
if (pfci->fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
// its a dir so recurse
PathCombine(szPath, pszDir, pfci->fd.cFileName);
if (!CheckFolderSizeAndDeleteability(szPath, pfci))
{
// pass the non-deletable file name up the recursion ladder, so we have the
// correct name in the popup dialog
lstrcpy((LPTSTR)pszDir, szPath);
FindClose(hfind);
return FALSE;
}
}
else {
ULARGE_INTEGER ulTemp;
INT iLen, iLen2;
BOOL bBadFileName = FALSE;
// its a file
lstrcpy(szFile, pszDir);
lstrcat(szFile, TEXT("\\"));
// Need to check to see if we can use the long name or
// or have to use the short name of the file
iLen = lstrlen(szFile);
iLen2 = lstrlen(pfci->fd.cFileName);
if (iLen + iLen2 + 1 > MAX_PATH) // +1 for NULL
{
// Make sure short name will fit, 12 == 8 + 1 + 3
if ( ((iLen + 1) <= (MAX_PATH-12)) &&
(pfci->fd.cAlternateFileName[0])
)
{
lstrcat(szFile, pfci->fd.cAlternateFileName);
}
else
{
// put as much of the path on there as possible...
PathAppend( szFile, pfci->fd.cFileName );
bBadFileName = TRUE;
}
}
else
{
lstrcat(szFile, pfci->fd.cFileName);
}
if (bBadFileName || !IsFileDeletable(szFile))
{
// copy the name of the file that cant be deleted over the dir name so that
// when we popup an error dialog, it identifies the suspect file, and not the
// entire directory
lstrcpy((LPTSTR)pszDir, szFile);
FindClose(hfind);
return FALSE;
}
ulTemp.LowPart = pfci->fd.nFileSizeLow;
ulTemp.HighPart = pfci->fd.nFileSizeHigh;
pfci->iSize += ulTemp.QuadPart;
pfci->cFiles++;
}
}
} while (FindNextFile(hfind, &pfci->fd) && pfci->bContinue);
FindClose(hfind);
}
else {
return FALSE;
} // FindFirstFile
return TRUE;
} // PathCombind
return FALSE; //default case
}
// This function checks to see if an local NT directory is delete-able
//
// returns:
// TRUE yes, the dir can be nuked
// FALSE for UNC dirs or dirs on network drives
// FALSE if no privlidges are present
//
// also sets the last error to explain why
//
BOOL IsDirectoryDeletable(LPCTSTR lpszDir)
{
#ifdef WINNT
NTSTATUS Status;
OBJECT_ATTRIBUTES Obja;
HANDLE Handle;
UNICODE_STRING FileName;
IO_STATUS_BLOCK IoStatusBlock;
BOOLEAN TranslationStatus;
RTL_RELATIVE_NAME RelativeName;
PVOID FreeBuffer;
DWORD dwAttributes;
BOOL fReadOnly = FALSE;
LPCWSTR lpUnicodeDir;
#ifndef UNICODE
WCHAR wszBuffer[MAX_PATH];
#endif
// return false for any network directories
if (PathIsUNC(lpszDir) || IsNetDrive(PathGetDriveNumber(lpszDir))) {
return FALSE;
}
// check to see if the dir is readonly
dwAttributes = GetFileAttributes(lpszDir);
if (dwAttributes & FILE_ATTRIBUTE_READONLY) {
fReadOnly = TRUE;
if (!SetFileAttributes(lpszDir, dwAttributes & ~FILE_ATTRIBUTE_READONLY)) {
return FALSE;
}
}
#ifdef UNICODE
lpUnicodeDir = lpszDir;
#else
lpUnicodeDir = wszBuffer;
MultiByteToWideChar(CP_ACP, 0, lpszDir, -1, wszBuffer, ARRAYSIZE(wszBuffer));
#endif
TranslationStatus = RtlDosPathNameToNtPathName_U(
lpUnicodeDir,
&FileName,
NULL,
&RelativeName
);
if ( !TranslationStatus ) {
SetLastError(ERROR_PATH_NOT_FOUND);
if (fReadOnly) {
// set the attribs back
SetFileAttributes(lpszDir, dwAttributes);
}
return FALSE;
}
FreeBuffer = FileName.Buffer;
if ( RelativeName.RelativeName.Length ) {
FileName = *(PUNICODE_STRING)&RelativeName.RelativeName;
}
else {
RelativeName.ContainingDirectory = NULL;
}
InitializeObjectAttributes(
&Obja,
&FileName,
OBJ_CASE_INSENSITIVE,
RelativeName.ContainingDirectory,
NULL
);
// Open the directory for delete access
Status = NtOpenFile(
&Handle,
DELETE | SYNCHRONIZE,
&Obja,
&IoStatusBlock,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT
);
RtlFreeHeap(RtlProcessHeap(), 0,FreeBuffer);
if ( !NT_SUCCESS(Status) ) {
// could not open it for delete access
if (fReadOnly) {
// set the attribs back
SetFileAttributes(lpszDir, dwAttributes);
}
SetLastError(RtlNtStatusToDosError(Status));
return FALSE;
}
// can be deleted
NtClose(Handle);
#endif
return TRUE;
}
// This function checks to see if an local NT file is delete-able
//
// returns:
// TRUE yes, the file can be nuked
// FALSE for UNC files or files on network drives
// FALSE if the file is in use
//
// also sets the last error to explain why
BOOL IsFileDeletable(LPCTSTR lpszFile)
{
#ifdef WINNT
NTSTATUS Status;
OBJECT_ATTRIBUTES Obja;
HANDLE Handle;
UNICODE_STRING FileName;
IO_STATUS_BLOCK IoStatusBlock;
FILE_DISPOSITION_INFORMATION Disposition;
BOOLEAN TranslationStatus;
RTL_RELATIVE_NAME RelativeName;
PVOID FreeBuffer;
DWORD dwAttributes;
BOOL fReadOnly = FALSE;
BOOL fSystem = FALSE;
LPCWSTR lpUnicodeFile;
#ifndef UNICODE
WCHAR wszBuffer[MAX_PATH];
#endif
// return false for any network drives
if (PathIsUNC(lpszFile) || IsNetDrive(PathGetDriveNumber(lpszFile))) {
return FALSE;
}
// check to see if the file is readonly or system
dwAttributes = GetFileAttributes(lpszFile);
if (dwAttributes & FILE_ATTRIBUTE_READONLY) {
fReadOnly = TRUE;
}
if (dwAttributes & FILE_ATTRIBUTE_SYSTEM) {
fSystem = TRUE;
}
if (fSystem || fReadOnly) {
if (!SetFileAttributes(lpszFile, dwAttributes & ~(FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM))) {
return FALSE;
}
}
#ifdef UNICODE
lpUnicodeFile = lpszFile;
#else
lpUnicodeFile = wszBuffer;
MultiByteToWideChar(CP_ACP, 0, lpszFile, -1, wszBuffer, ARRAYSIZE(wszBuffer));
#endif
TranslationStatus = RtlDosPathNameToNtPathName_U(
lpUnicodeFile,
&FileName,
NULL,
&RelativeName
);
if ( !TranslationStatus ) {
SetLastError(ERROR_PATH_NOT_FOUND);
if (fSystem || fReadOnly) {
// set the attribs back
SetFileAttributes(lpszFile, dwAttributes);
}
return FALSE;
}
FreeBuffer = FileName.Buffer;
if ( RelativeName.RelativeName.Length ) {
FileName = *(PUNICODE_STRING)&RelativeName.RelativeName;
}
else {
RelativeName.ContainingDirectory = NULL;
}
InitializeObjectAttributes(
&Obja,
&FileName,
OBJ_CASE_INSENSITIVE,
RelativeName.ContainingDirectory,
NULL
);
//
// Open the file for delete access
//
Status = NtOpenFile(
&Handle,
(ACCESS_MASK)DELETE,
&Obja,
&IoStatusBlock,
FILE_SHARE_DELETE |
FILE_SHARE_READ |
FILE_SHARE_WRITE,
FILE_NON_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT
);
RtlFreeHeap(RtlProcessHeap(), 0,FreeBuffer);
if ( !NT_SUCCESS(Status) ) {
SetLastError(RtlNtStatusToDosError(Status));
if (fSystem || fReadOnly) {
// set the attribs back
SetFileAttributes((LPCTSTR) lpszFile, dwAttributes);
}
return FALSE;
}
//
// Attempt to set the delete bit
//
#undef DeleteFile
Disposition.DeleteFile = TRUE;
Status = NtSetInformationFile(
Handle,
&IoStatusBlock,
&Disposition,
sizeof(Disposition),
FileDispositionInformation
);
if ( NT_SUCCESS(Status) ) {
// yep, we were able to set the bit, now unset it so its not delted!
Disposition.DeleteFile = FALSE;
Status = NtSetInformationFile(
Handle,
&IoStatusBlock,
&Disposition,
sizeof(Disposition),
FileDispositionInformation
);
NtClose(Handle);
if (fSystem || fReadOnly) {
// set the attribs back
SetFileAttributes(lpszFile, dwAttributes);
}
return TRUE;
}
else {
// nope couldnt set the del bit. can be deleted
NtClose(Handle);
if (fSystem || fReadOnly) {
// set the attribs back
SetFileAttributes(lpszFile, dwAttributes);
}
SetLastError(RtlNtStatusToDosError(Status));
return FALSE;
}
#else
return TRUE;
#endif
}