Source code of Windows XP (NT5)
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.

3142 lines
98 KiB

  1. #include "shellprv.h"
  2. #pragma hdrstop
  3. #include "bitbuck.h"
  4. #include "util.h"
  5. #include "copy.h"
  6. #include "prop.h" // for COLUMN_INFO
  7. #include "propsht.h"
  8. #include "datautil.h"
  9. #include "vdate.h" // for VDATEINPUTBUF
  10. #include "views.h"
  11. #include "defview.h" // for WM_DSV_FSNOTIFY
  12. #include "fsdata.h"
  13. #include "idldrop.h"
  14. #include "clsobj.h"
  15. #include "basefvcb.h"
  16. #include "idlcomm.h" // for HIDA
  17. #include "filefldr.h"
  18. #include <idhidden.h>
  19. #include "enumidlist.h"
  20. #include "contextmenu.h"
  21. class CBitBucket;
  22. class CBitBucketViewCB;
  23. class CBitBucketEnum;
  24. class CBitBucketDropTarget;
  25. class CBitBucketData;
  26. typedef struct {
  27. CBitBucket *pbb;
  28. HWND hwnd;
  29. IDataObject *pdtobj;
  30. IStream *pstmDataObj;
  31. ULONG_PTR idCmd;
  32. POINT ptDrop;
  33. BOOL fSameHwnd;
  34. BOOL fDragDrop;
  35. } BBTHREADDATA;
  36. class CBitBucket :
  37. public IPersistFolder2,
  38. public IShellFolder2,
  39. public IContextMenu,
  40. public IShellPropSheetExt,
  41. public IShellExtInit
  42. {
  43. friend CBitBucketEnum;
  44. friend CBitBucketViewCB;
  45. friend CBitBucketDropTarget;
  46. friend CBitBucketData;
  47. public:
  48. CBitBucket();
  49. // IUnknown
  50. STDMETHOD(QueryInterface)(REFIID riid, void **ppv);
  51. STDMETHOD_(ULONG,AddRef)();
  52. STDMETHOD_(ULONG,Release)();
  53. // IPersist
  54. STDMETHOD(GetClassID)(CLSID *pclsid);
  55. // IPersistFolder
  56. STDMETHOD(Initialize)(LPCITEMIDLIST pidl);
  57. // IPersistFolder2
  58. STDMETHOD(GetCurFolder)(LPITEMIDLIST *ppidl);
  59. // IShellFolder
  60. STDMETHOD(ParseDisplayName)(HWND hwnd, IBindCtx *pbc, LPOLESTR pszDisplayName, ULONG *pchEaten, LPITEMIDLIST *ppidl, ULONG *pdwAttributes);
  61. STDMETHOD(EnumObjects)(HWND hwnd, SHCONTF grfFlags, IEnumIDList **ppenumIDList);
  62. STDMETHOD(BindToObject)(LPCITEMIDLIST pidl, IBindCtx *pbc, REFIID riid, void **ppv);
  63. STDMETHOD(BindToStorage)(LPCITEMIDLIST pidl, IBindCtx *pbc, REFIID riid, void **ppv);
  64. STDMETHOD(CompareIDs)(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
  65. STDMETHOD(CreateViewObject)(HWND hwnd, REFIID riid, void **ppv);
  66. STDMETHOD(GetAttributesOf)(UINT cidl, LPCITEMIDLIST *apidl, SFGAOF *rgfInOut);
  67. STDMETHOD(GetUIObjectOf)(HWND hwnd, UINT cidl, LPCITEMIDLIST *apidl, REFIID riid, UINT *rgfReserved, void **ppv);
  68. STDMETHOD(GetDisplayNameOf)(LPCITEMIDLIST pidl, SHGDNF dwFlags, LPSTRRET lpName);
  69. STDMETHOD(SetNameOf)(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR pszName, SHGDNF dwFlags, LPITEMIDLIST *ppidlOut);
  70. // IShellFolder2
  71. STDMETHOD(GetDefaultSearchGUID)(GUID *pguid);
  72. STDMETHOD(EnumSearches)(IEnumExtraSearch **ppenum);
  73. STDMETHOD(GetDefaultColumn)(DWORD dwRes, ULONG *pSort, ULONG *pDisplay);
  74. STDMETHOD(GetDefaultColumnState)(UINT iColumn, SHCOLSTATEF *pdwFlags);
  75. STDMETHOD(GetDetailsEx)(LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv);
  76. STDMETHOD(GetDetailsOf)(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *psd);
  77. STDMETHOD(MapColumnToSCID)(UINT iColumn, SHCOLUMNID *pscid);
  78. // IContextMenu
  79. STDMETHOD(QueryContextMenu)(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags);
  80. STDMETHOD(InvokeCommand)(LPCMINVOKECOMMANDINFO lpici);
  81. STDMETHOD(GetCommandString)(UINT_PTR idCmd, UINT uType, UINT *pwReserved, LPSTR pszName, UINT cchMax);
  82. // IShellPropSheetExt
  83. STDMETHOD(AddPages)(LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam);
  84. STDMETHOD(ReplacePage)(UINT uPageID, LPFNADDPROPSHEETPAGE pfnReplaceWith, LPARAM lParam);
  85. // IShellExtInit
  86. STDMETHOD(Initialize)(LPCITEMIDLIST pidlFolder, IDataObject *pdobj, HKEY hkeyProgID);
  87. protected:
  88. LPITEMIDLIST DataEntryToIDList(BBDATAENTRYW *pbbde);
  89. LPITEMIDLIST PathToIDList(LPCTSTR pszPath);
  90. HGLOBAL BuildDestSpecs(LPIDA pida);
  91. private:
  92. ~CBitBucket();
  93. static HRESULT CALLBACK _ItemMenuCallBack(IShellFolder *psf, HWND hwnd,
  94. IDataObject * pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam);
  95. static HRESULT CALLBACK _BackgroundMenuCallBack(IShellFolder *psf, HWND hwnd,
  96. IDataObject * pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam);
  97. static UINT CALLBACK _GlobalSettingsCalback(HWND hwnd, UINT uMsg, LPPROPSHEETPAGE ppsp);
  98. static BOOL_PTR CALLBACK _FilePropDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
  99. static BOOL_PTR CALLBACK _DriveDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
  100. static BOOL_PTR CALLBACK _GlobalPropDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
  101. static DWORD CALLBACK _DispatchThreadProc(void *pv);
  102. static BOOL CALLBACK _AddPagesCallback(HPROPSHEETPAGE psp, LPARAM lParam);
  103. static DWORD WINAPI _DropThreadInit(BBTHREADDATA *pbbtd);
  104. static void _GlobalPropOnCommand(HWND hDlg, int id, HWND hwndCtl, UINT codeNotify);
  105. static CBitBucket *_FromFolder(IShellFolder *psf);
  106. HRESULT _LaunchThread(HWND hwnd, IDataObject *pdtobj, WPARAM idCmd);
  107. void _GetDeletedFileTime(LPCITEMIDLIST pidl, FILETIME *pft);
  108. DWORD _GetDeletedSize(LPCITEMIDLIST pidl);
  109. void _FileProperties(IDataObject *pdtobj);
  110. void _DefaultProperties();
  111. int _CompareOriginal(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
  112. int _DriveIDFromIDList(LPCITEMIDLIST pidl);
  113. HRESULT _FolderFromIDList(LPCITEMIDLIST pidl, REFIID riid, void **ppv);
  114. HRESULT _FolderFromDrive(int idDrive, REFIID riid, void **ppv);
  115. HRESULT _InitBindCtx();
  116. BOOL _MapColIndex(UINT *piColumn);
  117. PUBBDATAENTRYA _IsValid(LPCITEMIDLIST pidl);
  118. HRESULT _OriginalPath(LPCITEMIDLIST pidl, TCHAR *pszOrig, UINT cch);
  119. HRESULT _OriginalDirectory(LPCITEMIDLIST pidl, TCHAR *pszOrig, UINT cch);
  120. void _RestoreFileList(HWND hwnd, IDataObject * pdtobj);
  121. void _NukeFileList(HWND hwnd, IDataObject * pdtobj);
  122. int _DataObjToFileOpString(IDataObject *pdtobj, LPTSTR *ppszSrc, LPTSTR *ppszDest);
  123. void _GetDriveDisplayName(int idDrive, LPTSTR pszName, UINT cchSize);
  124. HRESULT _Compare(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
  125. LPITEMIDLIST _DriveInfoToIDList(int idDrive, int iIndex);
  126. DWORD _IsFolder(LPCITEMIDLIST pidl);
  127. UINT _SizeColumn();
  128. LONG _cRef;
  129. LPITEMIDLIST _pidl;
  130. UINT _uiColumnSize;
  131. IUnknown *_rgFolders[MAX_BITBUCKETS];
  132. };
  133. class CBitBucketViewCB : public CBaseShellFolderViewCB
  134. {
  135. public:
  136. STDMETHODIMP RealMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
  137. private:
  138. friend HRESULT Create_CBitBucketViewCB(CBitBucket* psf, IShellFolderViewCB **ppsfvcb);
  139. CBitBucketViewCB(CBitBucket *pbbf) : CBaseShellFolderViewCB(pbbf->_pidl, 0), _pbbf(pbbf)
  140. {
  141. ZeroMemory(&_fssci, sizeof(_fssci));
  142. _pbbf->AddRef();
  143. }
  144. ~CBitBucketViewCB()
  145. {
  146. _pbbf->Release();
  147. }
  148. HRESULT _HandleFSNotify(LONG lEvent, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
  149. HRESULT OnBACKGROUNDENUM(DWORD pv)
  150. {
  151. return S_OK;
  152. }
  153. HRESULT OnGetCCHMax(DWORD pv, LPCITEMIDLIST wP, UINT *lP)
  154. {
  155. return S_OK;
  156. }
  157. HRESULT OnSelChange(DWORD pv, UINT wPl, UINT wPh, SFVM_SELCHANGE_DATA*lP)
  158. {
  159. ViewSelChange(_pbbf, lP, &_fssci);
  160. return S_OK;
  161. }
  162. HRESULT OnFSNotify(DWORD pv, LPCITEMIDLIST *ppidl, LPARAM lP)
  163. {
  164. return _HandleFSNotify((LONG)lP, ppidl[0], ppidl[1]);
  165. }
  166. HRESULT OnUpdateStatusBar(DWORD pv, BOOL wP)
  167. {
  168. return ViewUpdateStatusBar(_punkSite, _pidl, &_fssci);
  169. }
  170. HRESULT OnWindowCreated(DWORD pv, HWND hwnd)
  171. {
  172. for (int i = 0; i < MAX_BITBUCKETS; i++)
  173. {
  174. SHChangeNotifyEntry fsne = {0};
  175. ASSERT(FALSE == fsne.fRecursive);
  176. // make it if it's there so that we'll get any events
  177. if (MakeBitBucket(i))
  178. {
  179. fsne.pidl = g_pBitBucket[i]->pidl;
  180. UINT u = SHChangeNotifyRegister(hwnd, SHCNRF_NewDelivery | SHCNRF_ShellLevel | SHCNRF_InterruptLevel,
  181. SHCNE_DISKEVENTS, WM_DSV_FSNOTIFY, 1, &fsne);
  182. }
  183. }
  184. // _fssci.szDrive[0] == '\0' // no drive specific stuff
  185. InitializeStatus(_punkSite);
  186. return S_OK;
  187. }
  188. HRESULT OnInsertDeleteItem(int iMul, LPCITEMIDLIST pidl)
  189. {
  190. ViewInsertDeleteItem(_pbbf, &_fssci, pidl, iMul);
  191. //since recycle bin doesn't receive shcne_xxx
  192. //defview doesn't update status bar
  193. OnUpdateStatusBar(0, FALSE);
  194. return S_OK;
  195. }
  196. HRESULT OnWindowDestroy(DWORD pv, HWND hwnd)
  197. {
  198. SHChangeNotifyDeregisterWindow(hwnd); // deregister all
  199. return S_OK;
  200. }
  201. HRESULT OnSize(DWORD pv, UINT cx, UINT cy)
  202. {
  203. ResizeStatus(_punkSite, cx);
  204. return S_OK;
  205. }
  206. HRESULT OnEnumeratedItems(DWORD pv, UINT celt, LPCITEMIDLIST *rgpidl)
  207. {
  208. _cItems = celt;
  209. return S_OK;
  210. }
  211. HRESULT OnDefViewMode(DWORD pv, FOLDERVIEWMODE*lP)
  212. {
  213. if (_cItems < DEFVIEW_FVM_MANY_CUTOFF)
  214. *lP = FVM_TILE;
  215. else
  216. *lP = FVM_ICON;
  217. return S_OK;
  218. }
  219. HRESULT OnGetHelpTopic(DWORD pv, SFVM_HELPTOPIC_DATA *phtd)
  220. {
  221. if (IsOS(OS_ANYSERVER))
  222. {
  223. StrCpyW(phtd->wszHelpFile, L"recycle.chm > windefault");
  224. }
  225. else
  226. {
  227. StrCpyW(phtd->wszHelpTopic, L"hcp://services/subsite?node=Unmapped/Recycle_Bin");
  228. }
  229. return S_OK;
  230. }
  231. FSSELCHANGEINFO _fssci;
  232. CBitBucket *_pbbf;
  233. UINT _cItems;
  234. // Web View implementation
  235. HRESULT OnGetWebViewLayout(DWORD pv, UINT uViewMode, SFVM_WEBVIEW_LAYOUT_DATA* pData);
  236. HRESULT OnGetWebViewContent(DWORD pv, SFVM_WEBVIEW_CONTENT_DATA* pData);
  237. HRESULT OnGetWebViewTasks(DWORD pv, SFVM_WEBVIEW_TASKSECTION_DATA* pTasks);
  238. public:
  239. static HRESULT _OnEmptyRecycleBin(IUnknown* pv,IShellItemArray *psiItemArray, IBindCtx *pbc);
  240. static HRESULT _OnRestore(IUnknown* pv,IShellItemArray *psiItemArray, IBindCtx *pbc);
  241. static HRESULT _HaveDeletedItems(IUnknown* pv,IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState);
  242. };
  243. HRESULT Create_CBitBucketViewCB(CBitBucket* psf, IShellFolderViewCB **ppsfvcb)
  244. {
  245. HRESULT hr;
  246. CBitBucketViewCB* psfvcb = new CBitBucketViewCB(psf);
  247. if (psfvcb)
  248. {
  249. *ppsfvcb = SAFECAST(psfvcb, IShellFolderViewCB*);
  250. hr = S_OK;
  251. }
  252. else
  253. {
  254. *ppsfvcb = NULL;
  255. hr = E_OUTOFMEMORY;
  256. }
  257. return hr;
  258. }
  259. HRESULT CBitBucketViewCB::OnGetWebViewLayout(DWORD pv, UINT uViewMode, SFVM_WEBVIEW_LAYOUT_DATA* pData)
  260. {
  261. ZeroMemory(pData, sizeof(*pData));
  262. pData->dwLayout = SFVMWVL_NORMAL;
  263. return S_OK;
  264. }
  265. HRESULT CBitBucketViewCB::_OnEmptyRecycleBin(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc)
  266. {
  267. CBitBucketViewCB* pThis = (CBitBucketViewCB*)(void*)pv;
  268. HRESULT hr = SHInvokeCommandOnPidl(pThis->_hwndMain, NULL, pThis->_pidl, 0, "empty");
  269. if (S_FALSE == hr)
  270. MessageBeep(0); // let the user know the click was processed, but nothing was there to delete
  271. return hr;
  272. }
  273. HRESULT CBitBucketViewCB::_OnRestore(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc)
  274. {
  275. IDataObject *pdo;
  276. CBitBucketViewCB* pThis = (CBitBucketViewCB*)(void*)pv;
  277. HRESULT hr = S_OK;
  278. if (!psiItemArray)
  279. {
  280. hr = E_FAIL;
  281. IFolderView* pfv;
  282. if (pThis->_punkSite && SUCCEEDED(pThis->_punkSite->QueryInterface(IID_PPV_ARG(IFolderView, &pfv))))
  283. {
  284. hr = pfv->Items(SVGIO_ALLVIEW, IID_PPV_ARG(IDataObject, &pdo));
  285. pfv->Release();
  286. }
  287. }
  288. else
  289. {
  290. hr = psiItemArray->BindToHandler(NULL,BHID_DataObject,IID_PPV_ARG(IDataObject,&pdo));
  291. }
  292. if (SUCCEEDED(hr))
  293. {
  294. hr = SHInvokeCommandOnDataObject(pThis->_hwndMain, NULL, pdo, 0, "undelete");
  295. ATOMICRELEASE(pdo);
  296. }
  297. return hr;
  298. }
  299. HRESULT CBitBucketViewCB::_HaveDeletedItems(IUnknown* /*pv*/,IShellItemArray * /* psiItemArray */, BOOL /*fOkToBeSlow*/, UISTATE* puisState)
  300. {
  301. *puisState = IsRecycleBinEmpty() ? UIS_DISABLED : UIS_ENABLED;
  302. return S_OK;
  303. }
  304. const WVTASKITEM c_BitBucketTaskHeader = WVTI_HEADER(L"shell32.dll", IDS_HEADER_BITBUCKET, IDS_HEADER_BITBUCKET_TT);
  305. const WVTASKITEM c_BitBucketTaskList[] =
  306. {
  307. WVTI_ENTRY_ALL(CLSID_NULL, L"shell32.dll", IDS_TASK_EMPTYRECYCLEBIN, IDS_TASK_EMPTYRECYCLEBIN_TT, IDI_TASK_EMPTYRECYCLEBIN, CBitBucketViewCB::_HaveDeletedItems, CBitBucketViewCB::_OnEmptyRecycleBin),
  308. WVTI_ENTRY_ALL_TITLE(CLSID_NULL, L"shell32.dll", IDS_TASK_RESTORE_ALL, IDS_TASK_RESTORE_ITEM, IDS_TASK_RESTORE_ITEM, IDS_TASK_RESTORE_ITEMS, IDS_TASK_RESTORE_TT, IDI_TASK_RESTOREITEMS, CBitBucketViewCB::_HaveDeletedItems, CBitBucketViewCB::_OnRestore),
  309. };
  310. HRESULT CBitBucketViewCB::OnGetWebViewContent(DWORD pv, SFVM_WEBVIEW_CONTENT_DATA* pData)
  311. {
  312. ZeroMemory(pData, sizeof(*pData));
  313. Create_IUIElement(&c_BitBucketTaskHeader, &(pData->pFolderTaskHeader));
  314. LPCTSTR rgcsidl[] = { MAKEINTRESOURCE(CSIDL_DESKTOP), MAKEINTRESOURCE(CSIDL_PERSONAL), MAKEINTRESOURCE(CSIDL_DRIVES), MAKEINTRESOURCE(CSIDL_NETWORK) };
  315. CreateIEnumIDListOnCSIDLs(NULL, rgcsidl, ARRAYSIZE(rgcsidl), &(pData->penumOtherPlaces));
  316. return S_OK;
  317. }
  318. HRESULT CBitBucketViewCB::OnGetWebViewTasks(DWORD pv, SFVM_WEBVIEW_TASKSECTION_DATA* pTasks)
  319. {
  320. ZeroMemory(pTasks, sizeof(*pTasks));
  321. pTasks->dwUpdateFlags = SFVMWVTSDF_CONTENTSCHANGE;
  322. Create_IEnumUICommand((IUnknown*)(void*)this, c_BitBucketTaskList, ARRAYSIZE(c_BitBucketTaskList), &pTasks->penumFolderTasks);
  323. return S_OK;
  324. }
  325. HRESULT CBitBucketViewCB::_HandleFSNotify(LONG lEvent, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  326. {
  327. HRESULT hr = S_OK;
  328. TCHAR szPath[MAX_PATH];
  329. // pidls must be child of drives or network
  330. // (actually only drives work for right now)
  331. // that way we won't get duplicate events
  332. if ((!ILIsParent((LPCITEMIDLIST)&c_idlDrives, pidl1, FALSE) && !ILIsParent((LPCITEMIDLIST)&c_idlNet, pidl1, FALSE)) ||
  333. (pidl2 && !ILIsParent((LPCITEMIDLIST)&c_idlDrives, pidl2, FALSE) && !ILIsParent((LPCITEMIDLIST)&c_idlNet, pidl2, FALSE)))
  334. {
  335. return S_FALSE;
  336. }
  337. SHGetPathFromIDList(pidl1, szPath);
  338. LPTSTR pszFileName = PathFindFileName(szPath);
  339. if (!lstrcmpi(pszFileName, c_szInfo2) ||
  340. !lstrcmpi(pszFileName, c_szInfo) ||
  341. !lstrcmpi(pszFileName, c_szDesktopIni))
  342. {
  343. // we ignore changes to these files because they mean we were simply doing bookeeping
  344. // (eg updating the info file, re-creating the desktop.ini, etc)
  345. return S_FALSE;
  346. }
  347. switch (lEvent)
  348. {
  349. case SHCNE_RENAMEFOLDER:
  350. case SHCNE_RENAMEITEM:
  351. {
  352. // if the rename's target is in a bitbucket, then do a create.
  353. // otherwise, return S_OK..
  354. int idDrive = DriveIDFromBBPath(szPath);
  355. if (MakeBitBucket(idDrive) && ILIsParent(g_pBitBucket[idDrive]->pidl, pidl1, TRUE))
  356. {
  357. hr = _HandleFSNotify((lEvent == SHCNE_RENAMEITEM) ? SHCNE_DELETE : SHCNE_RMDIR, pidl1, NULL);
  358. }
  359. }
  360. break;
  361. case SHCNE_CREATE:
  362. case SHCNE_MKDIR:
  363. {
  364. LPITEMIDLIST pidl = _pbbf->PathToIDList(szPath);
  365. if (pidl)
  366. {
  367. ShellFolderView_AddObject(_hwndMain, pidl);
  368. hr = S_FALSE;
  369. }
  370. }
  371. break;
  372. case SHCNE_DELETE:
  373. case SHCNE_RMDIR:
  374. // if this was a delete into the recycle bin, pidl2 will exist
  375. if (pidl2)
  376. {
  377. hr = _HandleFSNotify((lEvent == SHCNE_DELETE) ? SHCNE_CREATE : SHCNE_MKDIR, pidl2, NULL);
  378. }
  379. else
  380. {
  381. ShellFolderView_RemoveObject(_hwndMain, ILFindLastID(pidl1));
  382. hr = S_FALSE;
  383. }
  384. break;
  385. case SHCNE_UPDATEDIR:
  386. // we recieved an updatedir, which means we probably had more than 10 fsnotify events come in,
  387. // so we just refresh our brains out.
  388. ShellFolderView_RefreshAll(_hwndMain);
  389. break;
  390. default:
  391. hr = S_FALSE; // didn't handle this message
  392. break;
  393. }
  394. return hr;
  395. }
  396. STDMETHODIMP CBitBucketViewCB::RealMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
  397. {
  398. switch (uMsg)
  399. {
  400. HANDLE_MSG(0, SFVM_GETHELPTOPIC, OnGetHelpTopic);
  401. HANDLE_MSG(0, SFVM_GETCCHMAX, OnGetCCHMax);
  402. HANDLE_MSG(0, SFVM_SELCHANGE, OnSelChange);
  403. HANDLE_MSG(0, SFVM_FSNOTIFY, OnFSNotify);
  404. HANDLE_MSG(0, SFVM_UPDATESTATUSBAR, OnUpdateStatusBar);
  405. HANDLE_MSG(0, SFVM_WINDOWCREATED, OnWindowCreated);
  406. HANDLE_MSG(1 , SFVM_INSERTITEM, OnInsertDeleteItem);
  407. HANDLE_MSG(-1, SFVM_DELETEITEM, OnInsertDeleteItem);
  408. HANDLE_MSG(0, SFVM_WINDOWDESTROY, OnWindowDestroy);
  409. HANDLE_MSG(0, SFVM_ENUMERATEDITEMS, OnEnumeratedItems);
  410. HANDLE_MSG(0, SFVM_DEFVIEWMODE, OnDefViewMode);
  411. HANDLE_MSG(0, SFVM_SIZE, OnSize);
  412. HANDLE_MSG(0, SFVM_BACKGROUNDENUM, OnBACKGROUNDENUM);
  413. HANDLE_MSG(0, SFVM_GETWEBVIEWLAYOUT, OnGetWebViewLayout);
  414. HANDLE_MSG(0, SFVM_GETWEBVIEWCONTENT, OnGetWebViewContent);
  415. HANDLE_MSG(0, SFVM_GETWEBVIEWTASKS, OnGetWebViewTasks);
  416. default:
  417. return E_FAIL;
  418. }
  419. return S_OK;
  420. }
  421. typedef struct _bbpropsheetinfo
  422. {
  423. PROPSHEETPAGE psp;
  424. int idDrive;
  425. BOOL fNukeOnDelete;
  426. BOOL fOriginalNukeOnDelete;
  427. int iPercent;
  428. int iOriginalPercent;
  429. // the following two fields are valid only for the "global" tab, where they represent the state
  430. // of the "Configure drives independently" / "Use one setting for all drives" checkbox
  431. BOOL fUseGlobalSettings;
  432. BOOL fOriginalUseGlobalSettings;
  433. // the following fields are for policy overrides
  434. BOOL fPolicyNukeOnDelete;
  435. BOOL fPolicyPercent;
  436. // this is a pointer to the global property sheet page after it has been copied somewhere by the
  437. // CreatePropertySheetPage(), we use this to get to the global state of the % slider and fNukeOnDelete
  438. // from the other tabs
  439. struct _bbpropsheetinfo* pGlobal;
  440. } BBPROPSHEETINFO;
  441. const static DWORD aBitBucketPropHelpIDs[] = { // Context Help IDs
  442. IDD_ATTR_GROUPBOX, IDH_COMM_GROUPBOX,
  443. IDC_INDEPENDENT, IDH_RECYCLE_CONFIG_INDEP,
  444. IDC_GLOBAL, IDH_RECYCLE_CONFIG_ALL,
  445. IDC_DISKSIZE, IDH_RECYCLE_DRIVE_SIZE,
  446. IDC_DISKSIZEDATA, IDH_RECYCLE_DRIVE_SIZE,
  447. IDC_BYTESIZE, IDH_RECYCLE_BIN_SIZE,
  448. IDC_BYTESIZEDATA, IDH_RECYCLE_BIN_SIZE,
  449. IDC_NUKEONDELETE, IDH_RECYCLE_PURGE_ON_DEL,
  450. IDC_BBSIZE, IDH_RECYCLE_MAX_SIZE,
  451. IDC_BBSIZETEXT, IDH_RECYCLE_MAX_SIZE,
  452. IDC_CONFIRMDELETE, IDH_DELETE_CONFIRM_DLG,
  453. IDC_TEXT, NO_HELP,
  454. 0, 0
  455. };
  456. const static DWORD aBitBucketHelpIDs[] = { // Context Help IDs
  457. IDD_LINE_1, NO_HELP,
  458. IDD_LINE_2, NO_HELP,
  459. IDD_ITEMICON, IDH_FPROP_GEN_ICON,
  460. IDD_NAME, IDH_FPROP_GEN_NAME,
  461. IDD_FILETYPE_TXT, IDH_FPROP_GEN_TYPE,
  462. IDD_FILETYPE, IDH_FPROP_GEN_TYPE,
  463. IDD_FILESIZE_TXT, IDH_FPROP_GEN_SIZE,
  464. IDD_FILESIZE, IDH_FPROP_GEN_SIZE,
  465. IDD_LOCATION_TXT, IDH_FCAB_DELFILEPROP_LOCATION,
  466. IDD_LOCATION, IDH_FCAB_DELFILEPROP_LOCATION,
  467. IDD_DELETED_TXT, IDH_FCAB_DELFILEPROP_DELETED,
  468. IDD_DELETED, IDH_FCAB_DELFILEPROP_DELETED,
  469. IDD_CREATED_TXT, IDH_FPROP_GEN_DATE_CREATED,
  470. IDD_CREATED, IDH_FPROP_GEN_DATE_CREATED,
  471. IDD_READONLY, IDH_FCAB_DELFILEPROP_READONLY,
  472. IDD_HIDDEN, IDH_FCAB_DELFILEPROP_HIDDEN,
  473. IDD_ARCHIVE, IDH_FCAB_DELFILEPROP_ARCHIVE,
  474. IDD_ATTR_GROUPBOX, IDH_COMM_GROUPBOX,
  475. 0, 0
  476. };
  477. CBitBucket::CBitBucket() : _cRef(1), _pidl(NULL), _uiColumnSize(-1)
  478. {
  479. }
  480. CBitBucket::~CBitBucket()
  481. {
  482. for (int i = 0; i < ARRAYSIZE(_rgFolders); i++)
  483. {
  484. if (_rgFolders[i])
  485. _rgFolders[i]->Release();
  486. }
  487. ILFree(_pidl);
  488. }
  489. STDMETHODIMP CBitBucket::QueryInterface(REFIID riid, void **ppv)
  490. {
  491. static const QITAB qit[] = {
  492. QITABENT(CBitBucket, IPersistFolder2), // IID_IPersistFolder2
  493. QITABENTMULTI(CBitBucket, IPersistFolder, IPersistFolder2), // IID_IPersistFolder
  494. QITABENT(CBitBucket, IShellFolder2), // IID_IShellFolder2
  495. QITABENTMULTI(CBitBucket, IShellFolder, IShellFolder2), // IID_IShellFolder
  496. QITABENT(CBitBucket, IContextMenu), // IID_IContextMenu
  497. QITABENT(CBitBucket, IShellPropSheetExt), // IID_IShellPropSheetExt
  498. QITABENT(CBitBucket, IShellExtInit), // IID_IShellExtInit
  499. { 0 },
  500. };
  501. HRESULT hr = QISearch(this, qit, riid, ppv);
  502. if (FAILED(hr) && riid == CLSID_RecycleBin)
  503. {
  504. *ppv = this; // not ref counted
  505. hr = S_OK;
  506. }
  507. return hr;
  508. }
  509. STDMETHODIMP_(ULONG) CBitBucket::AddRef()
  510. {
  511. return InterlockedIncrement(&_cRef);
  512. }
  513. STDMETHODIMP_(ULONG) CBitBucket::Release()
  514. {
  515. if (InterlockedDecrement(&_cRef))
  516. return _cRef;
  517. delete this;
  518. return 0;
  519. }
  520. #pragma pack(1)
  521. typedef struct {
  522. HIDDENITEMID hid;
  523. BBDATAENTRYA bbde;
  524. } HIDDENRECYCLEBINDATA;
  525. #pragma pack()
  526. typedef HIDDENRECYCLEBINDATA UNALIGNED *PUHIDDENRECYCLEBINDATA;
  527. #define HRBD_CURRENTVERSION 0
  528. PUBBDATAENTRYA CBitBucket::_IsValid(LPCITEMIDLIST pidl)
  529. {
  530. if (pidl)
  531. {
  532. PUHIDDENRECYCLEBINDATA phrbd = (PUHIDDENRECYCLEBINDATA)ILFindHiddenID(pidl, IDLHID_RECYCLEBINDATA);
  533. if (phrbd && phrbd->hid.wVersion >= HRBD_CURRENTVERSION)
  534. return &phrbd->bbde;
  535. }
  536. return NULL;
  537. }
  538. HRESULT CBitBucket::_OriginalPath(LPCITEMIDLIST pidl, TCHAR *pszOrig, UINT cch)
  539. {
  540. ASSERT(pidl == ILFindLastID(pidl));
  541. *pszOrig = 0;
  542. HRESULT hr;
  543. PUBBDATAENTRYA pbbde = _IsValid(pidl);
  544. if (pbbde)
  545. {
  546. if (!ILGetHiddenString(pidl, IDLHID_RECYCLEBINORIGINAL, pszOrig, cch))
  547. {
  548. SHAnsiToTChar(pbbde->szOriginal, pszOrig, cch);
  549. }
  550. hr = *pszOrig ? S_OK : S_FALSE;
  551. }
  552. else
  553. {
  554. ASSERTMSG(pbbde != NULL, "_OriginalPath: caller needs to call _IsValid on the pidl passed to us!");
  555. hr = E_FAIL;
  556. }
  557. return hr;
  558. }
  559. HRESULT CBitBucket::_OriginalDirectory(LPCITEMIDLIST pidl, TCHAR *pszOrig, UINT cch)
  560. {
  561. HRESULT hr = _OriginalPath(pidl, pszOrig, cch);
  562. if (SUCCEEDED(hr))
  563. PathRemoveFileSpec(pszOrig);
  564. return hr;
  565. }
  566. // subclass member function to support CF_HDROP and CF_NETRESOURCE
  567. // in:
  568. // hida bitbucket id array
  569. //
  570. // out:
  571. // HGLOBAL with double NULL terminated string list of destination names
  572. //
  573. HGLOBAL CBitBucket::BuildDestSpecs(LPIDA pida)
  574. {
  575. LPCITEMIDLIST pidl;
  576. TCHAR szTemp[MAX_PATH];
  577. UINT cbAlloc = sizeof(TCHAR); // for double NULL termination
  578. for (UINT i = 0; pidl = IDA_GetIDListPtr(pida, i); i++)
  579. {
  580. _OriginalPath(pidl, szTemp, ARRAYSIZE(szTemp));
  581. cbAlloc += lstrlen(PathFindFileName(szTemp)) * sizeof(TCHAR) + sizeof(TCHAR);
  582. }
  583. LPTSTR pszRet = (LPTSTR) LocalAlloc(LPTR, cbAlloc);
  584. if (pszRet)
  585. {
  586. LPTSTR pszDest = pszRet;
  587. for (i = 0; pidl = IDA_GetIDListPtr(pida, i); i++)
  588. {
  589. _OriginalPath(pidl, szTemp, ARRAYSIZE(szTemp));
  590. lstrcpy(pszDest, PathFindFileName(szTemp));
  591. pszDest += lstrlen(pszDest) + 1;
  592. ASSERT((ULONG_PTR)((LPBYTE)pszDest - (LPBYTE)pszRet) < cbAlloc);
  593. ASSERT(*(pszDest) == 0); // zero init alloc
  594. }
  595. ASSERT((LPTSTR)((LPBYTE)pszRet + cbAlloc - sizeof(TCHAR)) == pszDest);
  596. ASSERT(*pszDest == 0); // zero init alloc
  597. }
  598. return pszRet;
  599. }
  600. class CBitBucketData : public CFSIDLData
  601. {
  602. public:
  603. CBitBucketData(CBitBucket *pbbf, UINT cidl, LPCITEMIDLIST apidl[]): CFSIDLData(pbbf->_pidl, cidl, apidl, NULL), _pbbf(pbbf)
  604. {
  605. _pbbf->AddRef();
  606. }
  607. // IDataObject methods overwrite
  608. STDMETHODIMP GetData(FORMATETC *pFmtEtc, STGMEDIUM *pstm);
  609. STDMETHODIMP QueryGetData(FORMATETC *pFmtEtc);
  610. private:
  611. ~CBitBucketData()
  612. {
  613. _pbbf->Release();
  614. }
  615. CBitBucket *_pbbf;
  616. };
  617. STDMETHODIMP CBitBucketData::QueryGetData(FORMATETC * pformatetc)
  618. {
  619. ASSERT(g_cfFileNameMap);
  620. if (pformatetc->cfFormat == g_cfFileNameMap && (pformatetc->tymed & TYMED_HGLOBAL))
  621. {
  622. return S_OK; // same as S_OK
  623. }
  624. return CFSIDLData::QueryGetData(pformatetc);
  625. }
  626. STDMETHODIMP CBitBucketData::GetData(FORMATETC * pformatetcIn, STGMEDIUM * pmedium)
  627. {
  628. HRESULT hr = E_INVALIDARG;
  629. ASSERT(g_cfFileNameMap);
  630. if (pformatetcIn->cfFormat == g_cfFileNameMap && (pformatetcIn->tymed & TYMED_HGLOBAL))
  631. {
  632. STGMEDIUM medium;
  633. LPIDA pida = DataObj_GetHIDA(this, &medium);
  634. if (medium.hGlobal)
  635. {
  636. pmedium->hGlobal = _pbbf->BuildDestSpecs(pida);
  637. pmedium->tymed = TYMED_HGLOBAL;
  638. pmedium->pUnkForRelease = NULL;
  639. HIDA_ReleaseStgMedium(pida, &medium);
  640. hr = pmedium->hGlobal ? S_OK : E_OUTOFMEMORY;
  641. }
  642. }
  643. else
  644. {
  645. hr = CFSIDLData::GetData(pformatetcIn, pmedium);
  646. }
  647. return hr;
  648. }
  649. //
  650. // We need to be able to compare the names of two bbpidls. Since either of
  651. // them could be a unicode name, we might have to convert both to unicode.
  652. //
  653. int CBitBucket::_CompareOriginal(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  654. {
  655. TCHAR szOrig1[MAX_PATH], szOrig2[MAX_PATH];
  656. if (SUCCEEDED(_OriginalPath(pidl1, szOrig1, ARRAYSIZE(szOrig1))) &&
  657. SUCCEEDED(_OriginalPath(pidl2, szOrig2, ARRAYSIZE(szOrig2))))
  658. {
  659. PathRemoveFileSpec(szOrig1);
  660. PathRemoveFileSpec(szOrig2);
  661. return lstrcmpi(szOrig1,szOrig2);
  662. }
  663. return -1; // failure, say 2 > 1
  664. }
  665. // we are cheating here passing pidl1 and pidl2 to one folder when
  666. // the could have come from different folders. but since these are
  667. // file system we can get away with this, see findfldr.cpp for the
  668. // code to deal with this in the general case
  669. HRESULT CBitBucket::_Compare(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  670. {
  671. IShellFolder *psf;
  672. HRESULT hr = _FolderFromIDList(pidl1, IID_PPV_ARG(IShellFolder, &psf));
  673. if (SUCCEEDED(hr))
  674. {
  675. hr = psf->CompareIDs(lParam, pidl1, pidl2);
  676. psf->Release();
  677. }
  678. return hr;
  679. }
  680. enum
  681. {
  682. ICOL_NAME = 0,
  683. ICOL_ORIGINAL = 1,
  684. ICOL_DATEDELETED = 2,
  685. };
  686. const COLUMN_INFO c_bb_cols[] =
  687. {
  688. DEFINE_COL_STR_ENTRY(SCID_NAME, 30, IDS_NAME_COL),
  689. DEFINE_COL_STR_ENTRY(SCID_DELETEDFROM, 30, IDS_DELETEDFROM_COL),
  690. DEFINE_COL_DATE_ENTRY(SCID_DATEDELETED, IDS_DATEDELETED_COL),
  691. };
  692. STDMETHODIMP CBitBucket::CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  693. {
  694. HRESULT hr = E_INVALIDARG;
  695. ASSERT(pidl1 == ILFindLastID(pidl1));
  696. UINT iColumn = ((DWORD)lParam & SHCIDS_COLUMNMASK);
  697. PUBBDATAENTRYA pbbde1 = _IsValid(pidl1); // both may be NULL for pure FS pidl
  698. PUBBDATAENTRYA pbbde2 = _IsValid(pidl2); // generated by change notify
  699. if (_MapColIndex(&iColumn))
  700. {
  701. switch (iColumn)
  702. {
  703. case ICOL_NAME:
  704. // compare the real filenames first, if they are different,
  705. // try comparing the display name
  706. hr = _Compare(lParam, pidl1, pidl2);
  707. if (0 == hr)
  708. return hr; // fs pidl comapre says they are the same
  709. else
  710. {
  711. TCHAR sz1[MAX_PATH], sz2[MAX_PATH];
  712. DisplayNameOf(this, pidl1, SHGDN_INFOLDER, sz1, ARRAYSIZE(sz1));
  713. DisplayNameOf(this, pidl2, SHGDN_INFOLDER, sz2, ARRAYSIZE(sz2));
  714. int iRes = StrCmpLogicalRestricted(sz1, sz2);
  715. if (iRes)
  716. return ResultFromShort(iRes);
  717. if (pbbde1 && pbbde2)
  718. return ResultFromShort(pbbde1->idDrive - pbbde2->idDrive);
  719. }
  720. break;
  721. case ICOL_ORIGINAL:
  722. {
  723. int iRes = _CompareOriginal(pidl1, pidl2);
  724. if (iRes)
  725. return ResultFromShort(iRes);
  726. }
  727. break;
  728. case ICOL_DATEDELETED:
  729. {
  730. FILETIME ft1, ft2;
  731. _GetDeletedFileTime(pidl1, &ft1);
  732. _GetDeletedFileTime(pidl2, &ft2);
  733. int iRes = CompareFileTime(&ft1, &ft2);
  734. if (iRes)
  735. return ResultFromShort(iRes);
  736. }
  737. break;
  738. }
  739. lParam &= ~SHCIDS_COLUMNMASK; // fall thorugh to sort on name...
  740. }
  741. else if (pbbde1 && pbbde2 && (_SizeColumn() == iColumn))
  742. {
  743. if (pbbde1->dwSize < pbbde2->dwSize)
  744. return ResultFromShort(-1);
  745. if (pbbde1->dwSize > pbbde2->dwSize)
  746. return ResultFromShort(1);
  747. lParam &= ~SHCIDS_COLUMNMASK; // fall thorugh to sort on name...
  748. }
  749. else
  750. {
  751. lParam = (lParam & ~SHCIDS_COLUMNMASK) | iColumn;
  752. }
  753. return _Compare(lParam, pidl1, pidl2);
  754. }
  755. STDMETHODIMP CBitBucket::GetAttributesOf(UINT cidl, LPCITEMIDLIST *apidl, ULONG *rgfOut)
  756. {
  757. HRESULT hr;
  758. if (IsSelf(cidl, apidl))
  759. {
  760. // asking about folder as a whole
  761. *rgfOut = SFGAO_FOLDER | SFGAO_DROPTARGET | SFGAO_HASPROPSHEET;
  762. if (SHRestricted(REST_BITBUCKNOPROP))
  763. {
  764. *rgfOut &= ~SFGAO_HASPROPSHEET;
  765. }
  766. hr = S_OK;
  767. }
  768. else
  769. {
  770. IShellFolder *psf;
  771. hr = _FolderFromIDList(apidl[0], IID_PPV_ARG(IShellFolder, &psf));
  772. if (SUCCEEDED(hr))
  773. {
  774. hr = psf->GetAttributesOf(cidl, apidl, rgfOut);
  775. psf->Release();
  776. // only allow these attributes to be returned
  777. *rgfOut &= (SFGAO_CANMOVE | SFGAO_CANDELETE | SFGAO_HASPROPSHEET | SFGAO_FILESYSANCESTOR | SFGAO_FILESYSTEM | SFGAO_LINK);
  778. }
  779. }
  780. return hr;
  781. }
  782. int CBitBucket::_DataObjToFileOpString(IDataObject *pdtobj, LPTSTR *ppszSrc, LPTSTR *ppszDest)
  783. {
  784. int cItems = 0;
  785. STGMEDIUM medium;
  786. LPIDA pida = DataObj_GetHIDA(pdtobj, &medium);
  787. *ppszSrc = NULL;
  788. *ppszDest = NULL;
  789. if (pida)
  790. {
  791. cItems = pida->cidl;
  792. // start with null terminated strings
  793. int cchSrc = 1, cchDest = 1;
  794. LPTSTR pszSrc = (LPTSTR)LocalAlloc(LPTR, cchSrc * sizeof(TCHAR));
  795. LPTSTR pszDest = (LPTSTR)LocalAlloc(LPTR, cchDest * sizeof(TCHAR));
  796. if (!pszSrc || !pszDest)
  797. {
  798. // skip the loop and fail
  799. cItems = 0;
  800. }
  801. for (int i = 0 ; i < cItems; i++)
  802. {
  803. LPITEMIDLIST pidl = IDA_FullIDList(pida, i);
  804. if (pidl)
  805. {
  806. TCHAR szTemp[MAX_PATH];
  807. // src
  808. SHGetPathFromIDList(pidl, szTemp);
  809. // Done with this already. Free now in case we exit early.
  810. ILFree(pidl);
  811. int cchSrcFile = lstrlen(szTemp) + 1;
  812. LPTSTR psz = (LPTSTR)LocalReAlloc((HLOCAL)pszSrc, (cchSrc + cchSrcFile) * sizeof(TCHAR), LMEM_MOVEABLE | LMEM_ZEROINIT);
  813. if (psz)
  814. {
  815. pszSrc = psz;
  816. lstrcpy(pszSrc + cchSrc - 1, szTemp);
  817. cchSrc += cchSrcFile;
  818. }
  819. else
  820. {
  821. cItems = 0;
  822. break;
  823. }
  824. // dest
  825. _OriginalPath(IDA_GetIDListPtr(pida, i), szTemp, ARRAYSIZE(szTemp));
  826. int cchDestFile = lstrlen(szTemp) + 1;
  827. psz = (LPTSTR)LocalReAlloc((HLOCAL)pszDest, (cchDest + cchDestFile) * sizeof(TCHAR), LMEM_MOVEABLE | LMEM_ZEROINIT);
  828. if (psz)
  829. {
  830. pszDest = psz;
  831. lstrcpy(pszDest + cchDest - 1, szTemp);
  832. cchDest += cchDestFile;
  833. }
  834. else
  835. {
  836. // out of memory!
  837. cItems = 0;
  838. break;
  839. }
  840. }
  841. }
  842. if (0 == cItems)
  843. {
  844. // ok to pass NULL here
  845. LocalFree((HLOCAL)pszSrc);
  846. LocalFree((HLOCAL)pszDest);
  847. }
  848. else
  849. {
  850. *ppszSrc = pszSrc;
  851. *ppszDest = pszDest;
  852. }
  853. HIDA_ReleaseStgMedium(pida, &medium);
  854. }
  855. return cItems;
  856. }
  857. //
  858. // restores the list of files in the IDataObject
  859. //
  860. void CBitBucket::_RestoreFileList(HWND hwnd, IDataObject * pdtobj)
  861. {
  862. LPTSTR pszSrc, pszDest;
  863. if (_DataObjToFileOpString(pdtobj, &pszSrc, &pszDest))
  864. {
  865. // now do the actual restore.
  866. SHFILEOPSTRUCT sFileOp = { hwnd, FO_MOVE, pszSrc, pszDest,
  867. FOF_MULTIDESTFILES | FOF_SIMPLEPROGRESS | FOF_NOCONFIRMMKDIR,
  868. FALSE, NULL, MAKEINTRESOURCE(IDS_BB_RESTORINGFILES)};
  869. DECLAREWAITCURSOR;
  870. SetWaitCursor();
  871. if (SHFileOperation(&sFileOp) == ERROR_SUCCESS)
  872. {
  873. SHChangeNotifyHandleEvents();
  874. BBCheckRestoredFiles(pszSrc);
  875. }
  876. LocalFree((HLOCAL)pszSrc);
  877. LocalFree((HLOCAL)pszDest);
  878. ResetWaitCursor();
  879. }
  880. }
  881. //
  882. // nukes the list of files in the IDataObject
  883. //
  884. void CBitBucket::_NukeFileList(HWND hwnd, IDataObject * pdtobj)
  885. {
  886. LPTSTR pszSrc, pszDest;
  887. int nFiles = _DataObjToFileOpString(pdtobj, &pszSrc, &pszDest);
  888. if (nFiles)
  889. {
  890. // now do the actual nuke.
  891. WIN32_FIND_DATA fd;
  892. CONFIRM_DATA cd = {CONFIRM_DELETE_FILE | CONFIRM_DELETE_FOLDER | CONFIRM_PROGRAM_FILE | CONFIRM_MULTIPLE, 0};
  893. SHFILEOPSTRUCT sFileOp = { hwnd, FO_DELETE, pszSrc, NULL, FOF_NOCONFIRMATION | FOF_SIMPLEPROGRESS,
  894. FALSE, NULL, MAKEINTRESOURCE(IDS_BB_DELETINGWASTEBASKETFILES)};
  895. DECLAREWAITCURSOR;
  896. SetWaitCursor();
  897. fd.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
  898. if (ConfirmFileOp(hwnd, NULL, &cd, nFiles, 0, CONFIRM_DELETE_FILE | CONFIRM_WASTEBASKET_PURGE, pszDest, &fd, NULL, &fd, NULL) == IDYES)
  899. {
  900. SHFileOperation(&sFileOp);
  901. SHChangeNotifyHandleEvents();
  902. // update the icon if there are objects left in the list
  903. int iItems = (int) ShellFolderView_GetObjectCount(hwnd);
  904. UpdateIcon(iItems);
  905. }
  906. LocalFree((HLOCAL)pszSrc);
  907. LocalFree((HLOCAL)pszDest);
  908. ResetWaitCursor();
  909. }
  910. }
  911. void EnableTrackbarAndFamily(HWND hDlg, BOOL f)
  912. {
  913. EnableWindow(GetDlgItem(hDlg, IDC_BBSIZE), f);
  914. EnableWindow(GetDlgItem(hDlg, IDC_BBSIZETEXT), f);
  915. EnableWindow(GetDlgItem(hDlg, IDC_TEXT), f);
  916. }
  917. void CBitBucket::_GlobalPropOnCommand(HWND hDlg, int id, HWND hwndCtl, UINT codeNotify)
  918. {
  919. BBPROPSHEETINFO *ppsi = (BBPROPSHEETINFO *)GetWindowLongPtr(hDlg, DWLP_USER);
  920. BOOL fNukeOnDelete;
  921. switch (id)
  922. {
  923. case IDC_GLOBAL:
  924. case IDC_INDEPENDENT:
  925. fNukeOnDelete = IsDlgButtonChecked(hDlg, IDC_NUKEONDELETE);
  926. ppsi->fUseGlobalSettings = (IsDlgButtonChecked(hDlg, IDC_GLOBAL) == BST_CHECKED) ? TRUE : FALSE;
  927. EnableWindow(GetDlgItem(hDlg, IDC_NUKEONDELETE), ppsi->fUseGlobalSettings && !ppsi->fPolicyNukeOnDelete);
  928. EnableTrackbarAndFamily(hDlg, ppsi->fUseGlobalSettings && !fNukeOnDelete && !ppsi->fPolicyPercent);
  929. SendMessage(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0);
  930. break;
  931. case IDC_NUKEONDELETE:
  932. fNukeOnDelete = IsDlgButtonChecked(hDlg, IDC_NUKEONDELETE);
  933. if (fNukeOnDelete)
  934. {
  935. // In order to help protect users, when they turn on "Remove files immedately" we also
  936. // check the "show delete confimation" box automatically for them. Thus, they will have
  937. // to explicitly uncheck it if they do not want confimation that their files will be nuked.
  938. CheckDlgButton(hDlg, IDC_CONFIRMDELETE, BST_CHECKED);
  939. }
  940. ppsi->fNukeOnDelete = fNukeOnDelete;
  941. EnableTrackbarAndFamily(hDlg, !fNukeOnDelete && !ppsi->fPolicyPercent);
  942. // fall through
  943. case IDC_CONFIRMDELETE:
  944. SendMessage(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0L);
  945. break;
  946. }
  947. }
  948. void RelayMessageToChildren(HWND hwnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
  949. {
  950. for (HWND hwndChild = GetWindow(hwnd, GW_CHILD); hwndChild != NULL; hwndChild = GetWindow(hwndChild, GW_HWNDNEXT))
  951. {
  952. SendMessage(hwndChild, uMessage, wParam, lParam);
  953. }
  954. }
  955. //
  956. // This is the dlg proc for the "Global" tab on the recycle bin
  957. //
  958. BOOL_PTR CALLBACK CBitBucket::_GlobalPropDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  959. {
  960. BBPROPSHEETINFO *ppsi = (BBPROPSHEETINFO *)GetWindowLongPtr(hDlg, DWLP_USER);
  961. switch (uMsg)
  962. {
  963. HANDLE_MSG(hDlg, WM_COMMAND, _GlobalPropOnCommand);
  964. case WM_INITDIALOG:
  965. {
  966. HWND hwndTrack = GetDlgItem(hDlg, IDC_BBSIZE);
  967. SHELLSTATE ss;
  968. // make sure the info we have is current
  969. RefreshAllBBDriveSettings();
  970. ppsi = (BBPROPSHEETINFO *)lParam;
  971. SetWindowLongPtr(hDlg, DWLP_USER, lParam);
  972. SendMessage(hwndTrack, TBM_SETTICFREQ, 10, 0);
  973. SendMessage(hwndTrack, TBM_SETRANGE, FALSE, MAKELONG(0, 100));
  974. SendMessage(hwndTrack, TBM_SETPOS, TRUE, ppsi->iOriginalPercent);
  975. EnableWindow(GetDlgItem(hDlg, IDC_NUKEONDELETE), ppsi->fUseGlobalSettings && !ppsi->fPolicyNukeOnDelete);
  976. EnableTrackbarAndFamily(hDlg, ppsi->fUseGlobalSettings && !ppsi->fNukeOnDelete && !ppsi->fPolicyPercent);
  977. CheckDlgButton(hDlg, IDC_NUKEONDELETE, ppsi->fNukeOnDelete);
  978. CheckRadioButton(hDlg, IDC_INDEPENDENT, IDC_GLOBAL, ppsi->fUseGlobalSettings ? IDC_GLOBAL : IDC_INDEPENDENT);
  979. EnableWindow(GetDlgItem(hDlg, IDC_INDEPENDENT), !ppsi->fPolicyNukeOnDelete);
  980. SHGetSetSettings(&ss, SSF_NOCONFIRMRECYCLE, FALSE);
  981. CheckDlgButton(hDlg, IDC_CONFIRMDELETE, !ss.fNoConfirmRecycle);
  982. EnableWindow(GetDlgItem(hDlg, IDC_CONFIRMDELETE), !SHRestricted(REST_BITBUCKCONFIRMDELETE));
  983. }
  984. // fall through to set iGlobalPercent
  985. case WM_HSCROLL:
  986. {
  987. TCHAR szPercent[20];
  988. HWND hwndTrack = GetDlgItem(hDlg, IDC_BBSIZE);
  989. ppsi->iPercent = (int) SendMessage(hwndTrack, TBM_GETPOS, 0, 0);
  990. wsprintf(szPercent, TEXT("%d%%"), ppsi->iPercent);
  991. SetDlgItemText(hDlg, IDC_BBSIZETEXT, szPercent);
  992. if (ppsi->iPercent != ppsi->iOriginalPercent)
  993. {
  994. SendMessage(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0L);
  995. if (ppsi->iPercent == 0)
  996. {
  997. // In order to help protect users, when they set the % slider to zero we also
  998. // check the "show delete confimation" box automatically for them. Thus, they will have
  999. // to explicitly uncheck it if they do not want confimation that their files will be nuked.
  1000. CheckDlgButton(hDlg, IDC_CONFIRMDELETE, BST_CHECKED);
  1001. }
  1002. }
  1003. return TRUE;
  1004. }
  1005. case WM_HELP:
  1006. WinHelp((HWND)((LPHELPINFO) lParam)->hItemHandle, NULL,
  1007. HELP_WM_HELP, (ULONG_PTR)(LPTSTR) aBitBucketPropHelpIDs);
  1008. return TRUE;
  1009. case WM_CONTEXTMENU:
  1010. WinHelp((HWND) wParam, NULL, HELP_CONTEXTMENU,
  1011. (ULONG_PTR)(void *) aBitBucketPropHelpIDs);
  1012. return TRUE;
  1013. case WM_WININICHANGE:
  1014. case WM_SYSCOLORCHANGE:
  1015. case WM_DISPLAYCHANGE:
  1016. RelayMessageToChildren(hDlg, uMsg, wParam, lParam);
  1017. break;
  1018. case WM_DESTROY:
  1019. CheckCompactAndPurge();
  1020. SHUpdateRecycleBinIcon();
  1021. break;
  1022. case WM_NOTIFY:
  1023. switch (((NMHDR *)lParam)->code)
  1024. {
  1025. case PSN_APPLY:
  1026. {
  1027. SHELLSTATE ss;
  1028. ss.fNoConfirmRecycle = !IsDlgButtonChecked(hDlg, IDC_CONFIRMDELETE);
  1029. SHGetSetSettings(&ss, SSF_NOCONFIRMRECYCLE, TRUE);
  1030. ppsi->fNukeOnDelete = (IsDlgButtonChecked(hDlg, IDC_NUKEONDELETE) == BST_CHECKED) ? TRUE : FALSE;
  1031. ppsi->fUseGlobalSettings = (IsDlgButtonChecked(hDlg, IDC_INDEPENDENT) == BST_CHECKED) ? FALSE : TRUE;
  1032. // if anything on the global tab changed, update all the drives
  1033. if (ppsi->fUseGlobalSettings != ppsi->fOriginalUseGlobalSettings ||
  1034. ppsi->fNukeOnDelete != ppsi->fOriginalNukeOnDelete ||
  1035. ppsi->iPercent != ppsi->iOriginalPercent)
  1036. {
  1037. // NOTE: We get a PSN_APPLY after all the drive tabs. This has to be this way so that
  1038. // if global settings change, then the global tab will re-apply all the most current settings
  1039. // bassed on the global variables that get set above.
  1040. // this sets the new global settings in the registry
  1041. if (!PersistGlobalSettings(ppsi->fUseGlobalSettings, ppsi->fNukeOnDelete, ppsi->iPercent))
  1042. {
  1043. // we failed, so show the error dialog and bail
  1044. ShellMessageBox(HINST_THISDLL,
  1045. hDlg,
  1046. MAKEINTRESOURCE(IDS_BB_CANNOTCHANGESETTINGS),
  1047. MAKEINTRESOURCE(IDS_WASTEBASKET),
  1048. MB_OK | MB_ICONEXCLAMATION);
  1049. SetDlgMsgResult(hDlg, WM_NOTIFY, PSNRET_INVALID_NOCHANGEPAGE);
  1050. return TRUE;
  1051. }
  1052. for (int i = 0; i < MAX_BITBUCKETS; i++)
  1053. {
  1054. if (MakeBitBucket(i))
  1055. {
  1056. BOOL bPurge = TRUE;
  1057. // we need to purge all the drives in this case
  1058. RegSetValueEx(g_pBitBucket[i]->hkeyPerUser, TEXT("NeedToPurge"), 0, REG_DWORD, (LPBYTE)&bPurge, sizeof(bPurge));
  1059. RefreshBBDriveSettings(i);
  1060. }
  1061. }
  1062. ppsi->fOriginalUseGlobalSettings = ppsi->fUseGlobalSettings;
  1063. ppsi->fOriginalNukeOnDelete = ppsi->fNukeOnDelete;
  1064. ppsi->iOriginalPercent = ppsi->iPercent;
  1065. }
  1066. }
  1067. }
  1068. break;
  1069. SetDlgMsgResult(hDlg, WM_NOTIFY, 0);
  1070. return TRUE;
  1071. }
  1072. return FALSE;
  1073. }
  1074. BOOL_PTR CALLBACK CBitBucket::_DriveDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1075. {
  1076. BBPROPSHEETINFO *ppsi = (BBPROPSHEETINFO *)GetWindowLongPtr(hDlg, DWLP_USER);
  1077. TCHAR szDiskSpace[40];
  1078. switch (uMsg)
  1079. {
  1080. case WM_INITDIALOG:
  1081. {
  1082. HWND hwndTrack = GetDlgItem(hDlg, IDC_BBSIZE);
  1083. ppsi = (BBPROPSHEETINFO *)lParam;
  1084. SetWindowLongPtr(hDlg, DWLP_USER, lParam);
  1085. SendMessage(hwndTrack, TBM_SETTICFREQ, 10, 0);
  1086. SendMessage(hwndTrack, TBM_SETRANGE, FALSE, MAKELONG(0, 100));
  1087. SendMessage(hwndTrack, TBM_SETPOS, TRUE, ppsi->iPercent);
  1088. CheckDlgButton(hDlg, IDC_NUKEONDELETE, ppsi->fNukeOnDelete);
  1089. // set the disk space info
  1090. StrFormatByteSize64(g_pBitBucket[ppsi->idDrive]->qwDiskSize, szDiskSpace, ARRAYSIZE(szDiskSpace));
  1091. SetDlgItemText(hDlg, IDC_DISKSIZEDATA, szDiskSpace);
  1092. wParam = 0;
  1093. }
  1094. // fall through
  1095. case WM_HSCROLL:
  1096. {
  1097. ULARGE_INTEGER ulBucketSize;
  1098. HWND hwndTrack = GetDlgItem(hDlg, IDC_BBSIZE);
  1099. ppsi->iPercent = (int)SendMessage(hwndTrack, TBM_GETPOS, 0, 0);
  1100. wsprintf(szDiskSpace, TEXT("%d%%"), ppsi->iPercent);
  1101. SetDlgItemText(hDlg, IDC_BBSIZETEXT, szDiskSpace);
  1102. if (ppsi->iPercent != ppsi->iOriginalPercent)
  1103. {
  1104. SendMessage(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0);
  1105. }
  1106. // we peg the max size of the recycle bin to 4 gig
  1107. ulBucketSize.QuadPart = (ppsi->pGlobal->fUseGlobalSettings ? ppsi->pGlobal->iPercent : ppsi->iPercent) * (g_pBitBucket[ppsi->idDrive]->qwDiskSize / 100);
  1108. StrFormatByteSize64(ulBucketSize.HighPart ? (DWORD)-1 : ulBucketSize.LowPart, szDiskSpace, ARRAYSIZE(szDiskSpace));
  1109. SetDlgItemText(hDlg, IDC_BYTESIZEDATA, szDiskSpace);
  1110. return TRUE;
  1111. }
  1112. case WM_HELP:
  1113. WinHelp((HWND)((LPHELPINFO) lParam)->hItemHandle, NULL,
  1114. HELP_WM_HELP, (ULONG_PTR)(LPTSTR) aBitBucketPropHelpIDs);
  1115. return TRUE;
  1116. case WM_CONTEXTMENU:
  1117. WinHelp((HWND) wParam, NULL, HELP_CONTEXTMENU,
  1118. (ULONG_PTR)(void *) aBitBucketPropHelpIDs);
  1119. return TRUE;
  1120. case WM_COMMAND:
  1121. {
  1122. WORD wCommandID = GET_WM_COMMAND_ID(wParam, lParam);
  1123. if (wCommandID == IDC_NUKEONDELETE)
  1124. {
  1125. SendMessage(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0L);
  1126. EnableTrackbarAndFamily(hDlg, !IsDlgButtonChecked(hDlg, IDC_NUKEONDELETE) && !ppsi->fPolicyPercent);
  1127. EnableWindow(GetDlgItem(hDlg, IDC_BYTESIZE), !IsDlgButtonChecked(hDlg, IDC_NUKEONDELETE));
  1128. EnableWindow(GetDlgItem(hDlg, IDC_BYTESIZEDATA), !IsDlgButtonChecked(hDlg, IDC_NUKEONDELETE));
  1129. }
  1130. }
  1131. break;
  1132. case WM_NOTIFY:
  1133. switch (((NMHDR *)lParam)->code)
  1134. {
  1135. case PSN_APPLY:
  1136. {
  1137. ppsi->fNukeOnDelete = (IsDlgButtonChecked(hDlg, IDC_NUKEONDELETE) == BST_CHECKED) ? TRUE : FALSE;
  1138. // update the info in the registry
  1139. if (!PersistBBDriveSettings(ppsi->idDrive, ppsi->iPercent, ppsi->fNukeOnDelete))
  1140. {
  1141. // we failed, so show the error dialog and bail
  1142. ShellMessageBox(HINST_THISDLL, hDlg,
  1143. MAKEINTRESOURCE(IDS_BB_CANNOTCHANGESETTINGS),
  1144. MAKEINTRESOURCE(IDS_WASTEBASKET),
  1145. MB_OK | MB_ICONEXCLAMATION);
  1146. SetDlgMsgResult(hDlg, WM_NOTIFY, PSNRET_INVALID_NOCHANGEPAGE);
  1147. return TRUE;
  1148. }
  1149. // only purge this drive if the user set the slider to a smaller value
  1150. if (ppsi->iPercent < ppsi->iOriginalPercent)
  1151. {
  1152. BOOL bPurge = TRUE;
  1153. // since this drive just shrunk, we need to purge the files in it
  1154. RegSetValueEx(g_pBitBucket[ppsi->idDrive]->hkeyPerUser, TEXT("NeedToPurge"), 0, REG_DWORD, (LPBYTE)&bPurge, sizeof(bPurge));
  1155. }
  1156. ppsi->iOriginalPercent = ppsi->iPercent;
  1157. ppsi->fOriginalNukeOnDelete = ppsi->fNukeOnDelete;
  1158. // update the g_pBitBucket[] for this drive
  1159. // NOTE: We get a PSN_APPLY before the global tab does. This has to be this way so that
  1160. // if global settings change, then the global tab will re-apply all the most current settings
  1161. // bassed on the global variables that get set in his tab.
  1162. RefreshBBDriveSettings(ppsi->idDrive);
  1163. }
  1164. break;
  1165. case PSN_SETACTIVE:
  1166. {
  1167. BOOL fNukeOnDelete;
  1168. fNukeOnDelete = ppsi->pGlobal->fUseGlobalSettings ? ppsi->pGlobal->fNukeOnDelete :
  1169. IsDlgButtonChecked(hDlg, IDC_NUKEONDELETE);
  1170. EnableWindow(GetDlgItem(hDlg, IDC_NUKEONDELETE), !ppsi->pGlobal->fUseGlobalSettings && !ppsi->fPolicyNukeOnDelete);
  1171. EnableTrackbarAndFamily(hDlg, !ppsi->pGlobal->fUseGlobalSettings && !fNukeOnDelete && !ppsi->fPolicyPercent);
  1172. EnableWindow(GetDlgItem(hDlg, IDC_BYTESIZE), !fNukeOnDelete);
  1173. EnableWindow(GetDlgItem(hDlg, IDC_BYTESIZEDATA), !fNukeOnDelete);
  1174. // send this to make sure that the "space reserved" field is accurate when using global settings
  1175. SendMessage(hDlg, WM_HSCROLL, 0, 0);
  1176. }
  1177. break;
  1178. }
  1179. SetDlgMsgResult(hDlg, WM_NOTIFY, 0);
  1180. return TRUE;
  1181. }
  1182. return FALSE;
  1183. }
  1184. typedef struct {
  1185. PROPSHEETPAGE psp;
  1186. LPITEMIDLIST pidl;
  1187. FILETIME ftDeleted;
  1188. DWORD dwSize;
  1189. TCHAR szOriginal[MAX_PATH];
  1190. } BBFILEPROPINFO;
  1191. // property sheet page for a file/folder in the bitbucket
  1192. BOOL_PTR CALLBACK CBitBucket::_FilePropDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1193. {
  1194. BBFILEPROPINFO * pbbfpi = (BBFILEPROPINFO *)GetWindowLongPtr(hDlg, DWLP_USER);
  1195. switch (uMsg)
  1196. {
  1197. case WM_INITDIALOG:
  1198. {
  1199. pbbfpi = (BBFILEPROPINFO *)lParam;
  1200. SetWindowLongPtr(hDlg, DWLP_USER, lParam);
  1201. SHFILEINFO sfi = {0};
  1202. SHGetFileInfo((LPTSTR)pbbfpi->pidl, 0, &sfi, sizeof(sfi),
  1203. SHGFI_PIDL | SHGFI_TYPENAME | SHGFI_ICON | SHGFI_LARGEICON | SHGFI_ADDOVERLAYS | SHGFI_DISPLAYNAME);
  1204. // icon
  1205. ReplaceDlgIcon(hDlg, IDD_ITEMICON, sfi.hIcon);
  1206. // Type
  1207. SetDlgItemText(hDlg, IDD_FILETYPE, sfi.szTypeName);
  1208. TCHAR szTemp[MAX_PATH];
  1209. StrCpyN(szTemp, pbbfpi->szOriginal, ARRAYSIZE(szTemp));
  1210. PathRemoveExtension(szTemp);
  1211. SetDlgItemText(hDlg, IDD_NAME, PathFindFileName(szTemp));
  1212. // origin
  1213. PathRemoveFileSpec(szTemp);
  1214. SetDlgItemText(hDlg, IDD_LOCATION, PathFindFileName(szTemp));
  1215. // deleted time
  1216. SetDateTimeText(hDlg, IDD_DELETED, &pbbfpi->ftDeleted);
  1217. // Size
  1218. StrFormatByteSize64(pbbfpi->dwSize, szTemp, ARRAYSIZE(szTemp));
  1219. SetDlgItemText(hDlg, IDD_FILESIZE, szTemp);
  1220. if (SHGetPathFromIDList(pbbfpi->pidl, szTemp))
  1221. {
  1222. WIN32_FIND_DATA fd;
  1223. HANDLE hfind = FindFirstFile(szTemp, &fd);
  1224. if (hfind != INVALID_HANDLE_VALUE)
  1225. {
  1226. SetDateTimeText(hDlg, IDD_CREATED, &fd.ftCreationTime);
  1227. FindClose(hfind);
  1228. // We don't allow user to change compression attribute on a deleted file
  1229. // but we do show the current compressed state
  1230. TCHAR szRoot[MAX_PATH], szFSName[12];
  1231. // If file's volume doesn't support compression, don't show
  1232. // "Compressed" checkbox.
  1233. // If compression is supported, show the checkbox and check/uncheck
  1234. // it to indicate compression state of the file.
  1235. lstrcpy(szRoot, szTemp);
  1236. PathStripToRoot(szRoot);
  1237. PathAddBackslash(szRoot); // for UNC (MyDocs) case
  1238. DWORD dwVolumeFlags;
  1239. if (GetVolumeInformation(szRoot, NULL, 0, NULL, NULL, &dwVolumeFlags, szFSName, ARRAYSIZE(szFSName)))
  1240. {
  1241. if (dwVolumeFlags & FS_FILE_COMPRESSION)
  1242. {
  1243. if (fd.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED)
  1244. CheckDlgButton(hDlg, IDD_COMPRESS, 1);
  1245. ShowWindow(GetDlgItem(hDlg, IDD_COMPRESS), SW_SHOW);
  1246. }
  1247. if (dwVolumeFlags & FS_FILE_ENCRYPTION)
  1248. {
  1249. if (fd.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED)
  1250. CheckDlgButton(hDlg, IDD_ENCRYPT, 1);
  1251. ShowWindow(GetDlgItem(hDlg, IDD_ENCRYPT), SW_SHOW);
  1252. }
  1253. }
  1254. // file attributes
  1255. if (fd.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
  1256. CheckDlgButton(hDlg, IDD_READONLY, 1);
  1257. if (fd.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)
  1258. CheckDlgButton(hDlg, IDD_ARCHIVE, 1);
  1259. if (fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
  1260. CheckDlgButton(hDlg, IDD_HIDDEN, 1);
  1261. }
  1262. }
  1263. }
  1264. break;
  1265. case WM_WININICHANGE:
  1266. case WM_SYSCOLORCHANGE:
  1267. case WM_DISPLAYCHANGE:
  1268. RelayMessageToChildren(hDlg, uMsg, wParam, lParam);
  1269. break;
  1270. case WM_DESTROY:
  1271. ReplaceDlgIcon(hDlg, IDD_ITEMICON, NULL);
  1272. break;
  1273. case WM_COMMAND:
  1274. {
  1275. UINT id = GET_WM_COMMAND_ID(wParam, lParam);
  1276. switch (id)
  1277. {
  1278. case IDD_RESTORE:
  1279. if (S_OK == SHInvokeCommandOnPidl(hDlg, NULL, pbbfpi->pidl, 0, "undelete"))
  1280. {
  1281. // We succeeded, so disable the button (invoking again will fail)
  1282. EnableWindow(GetDlgItem(hDlg, IDD_RESTORE), FALSE);
  1283. }
  1284. break;
  1285. }
  1286. }
  1287. break;
  1288. case WM_NOTIFY:
  1289. switch (((NMHDR *)lParam)->code)
  1290. {
  1291. case PSN_APPLY:
  1292. case PSN_SETACTIVE:
  1293. case PSN_KILLACTIVE:
  1294. return TRUE;
  1295. }
  1296. break;
  1297. case WM_HELP:
  1298. WinHelp((HWND)((LPHELPINFO) lParam)->hItemHandle, NULL,
  1299. HELP_WM_HELP, (ULONG_PTR)(LPTSTR) aBitBucketHelpIDs);
  1300. return TRUE;
  1301. case WM_CONTEXTMENU:
  1302. WinHelp((HWND) wParam, NULL, HELP_CONTEXTMENU,
  1303. (ULONG_PTR)(void *) aBitBucketHelpIDs);
  1304. return TRUE;
  1305. }
  1306. return FALSE;
  1307. }
  1308. void CBitBucket::_GetDriveDisplayName(int idDrive, LPTSTR pszName, UINT cchSize)
  1309. {
  1310. TCHAR szDrive[MAX_PATH];
  1311. DriveIDToBBRoot(idDrive, szDrive);
  1312. SHFILEINFO sfi;
  1313. if (SHGetFileInfo(szDrive, FILE_ATTRIBUTE_DIRECTORY, &sfi, sizeof(sfi), SHGFI_USEFILEATTRIBUTES | SHGFI_DISPLAYNAME))
  1314. {
  1315. lstrcpyn(pszName, sfi.szDisplayName, cchSize);
  1316. }
  1317. // If SERVERDRIVE, attempt to overwrite the default display name with the display
  1318. // name for the mydocs folder on the desktop, since SERVERDRIVE==mydocs for now
  1319. if (idDrive == SERVERDRIVE)
  1320. {
  1321. GetMyDocumentsDisplayName(pszName, cchSize);
  1322. }
  1323. }
  1324. BOOL CALLBACK CBitBucket::_AddPagesCallback(HPROPSHEETPAGE psp, LPARAM lParam)
  1325. {
  1326. LPPROPSHEETHEADER ppsh = (LPPROPSHEETHEADER)lParam;
  1327. ppsh->phpage[ppsh->nPages++] = psp;
  1328. return TRUE;
  1329. }
  1330. // properties for recycle bin
  1331. void CBitBucket::_DefaultProperties()
  1332. {
  1333. UNIQUESTUBINFO usi;
  1334. if (EnsureUniqueStub(_pidl, STUBCLASS_PROPSHEET, NULL, &usi))
  1335. {
  1336. HPROPSHEETPAGE ahpage[MAXPROPPAGES];
  1337. PROPSHEETHEADER psh = {0};
  1338. psh.dwSize = sizeof(psh);
  1339. psh.dwFlags = PSH_PROPTITLE;
  1340. psh.hInstance = HINST_THISDLL;
  1341. psh.phpage = ahpage;
  1342. AddPages(_AddPagesCallback, (LPARAM)&psh);
  1343. psh.pszCaption = MAKEINTRESOURCE(IDS_WASTEBASKET);
  1344. psh.hwndParent = usi.hwndStub;
  1345. PropertySheet(&psh);
  1346. FreeUniqueStub(&usi);
  1347. }
  1348. }
  1349. // deals with alignment and pidl validation for you
  1350. void CBitBucket::_GetDeletedFileTime(LPCITEMIDLIST pidl, FILETIME *pft)
  1351. {
  1352. ZeroMemory(pft, sizeof(*pft));
  1353. PUBBDATAENTRYA pbbde = _IsValid(pidl);
  1354. if (pbbde)
  1355. *pft = pbbde->ft;
  1356. }
  1357. DWORD CBitBucket::_GetDeletedSize(LPCITEMIDLIST pidl)
  1358. {
  1359. PUBBDATAENTRYA pbbde = _IsValid(pidl);
  1360. return pbbde ? pbbde->dwSize : 0;
  1361. }
  1362. // recycled items properties
  1363. // note: we only show the proeprties for the first file if there is a multiple selection
  1364. void CBitBucket::_FileProperties(IDataObject *pdtobj)
  1365. {
  1366. STGMEDIUM medium;
  1367. LPIDA pida = DataObj_GetHIDA(pdtobj, &medium);
  1368. if (pida)
  1369. {
  1370. BBFILEPROPINFO bbfpi = {0};
  1371. bbfpi.pidl = IDA_FullIDList(pida, 0);
  1372. if (bbfpi.pidl)
  1373. {
  1374. UNIQUESTUBINFO usi;
  1375. if (EnsureUniqueStub(bbfpi.pidl, STUBCLASS_PROPSHEET, NULL, &usi))
  1376. {
  1377. HPROPSHEETPAGE ahpage[MAXPROPPAGES];
  1378. TCHAR szTitle[80];
  1379. bbfpi.psp.dwSize = sizeof(bbfpi);
  1380. bbfpi.psp.hInstance = HINST_THISDLL;
  1381. bbfpi.psp.pszTemplate = MAKEINTRESOURCE(DLG_DELETEDFILEPROP);
  1382. bbfpi.psp.pfnDlgProc = _FilePropDlgProc;
  1383. bbfpi.psp.pszTitle = szTitle;
  1384. _OriginalPath(IDA_GetIDListPtr(pida, 0), bbfpi.szOriginal, ARRAYSIZE(bbfpi.szOriginal));
  1385. bbfpi.dwSize = _GetDeletedSize(IDA_GetIDListPtr(pida, 0));
  1386. _GetDeletedFileTime(IDA_GetIDListPtr(pida, 0), &bbfpi.ftDeleted);
  1387. lstrcpyn(szTitle, PathFindFileName(bbfpi.szOriginal), ARRAYSIZE(szTitle));
  1388. PathRemoveExtension(szTitle);
  1389. PROPSHEETHEADER psh = {0};
  1390. psh.dwSize = sizeof(psh);
  1391. psh.dwFlags = PSH_PROPTITLE;
  1392. psh.hInstance = HINST_THISDLL;
  1393. psh.phpage = ahpage;
  1394. psh.phpage[0] = CreatePropertySheetPage(&bbfpi.psp);
  1395. if (psh.phpage[0])
  1396. {
  1397. psh.nPages = 1;
  1398. psh.pszCaption = szTitle;
  1399. psh.hwndParent = usi.hwndStub;
  1400. PropertySheet(&psh);
  1401. }
  1402. FreeUniqueStub(&usi);
  1403. }
  1404. ILFree(bbfpi.pidl);
  1405. }
  1406. HIDA_ReleaseStgMedium(pida, &medium);
  1407. }
  1408. return;
  1409. }
  1410. DWORD WINAPI CBitBucket::_DropThreadInit(BBTHREADDATA *pbbtd)
  1411. {
  1412. STGMEDIUM medium;
  1413. FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  1414. if (SUCCEEDED(pbbtd->pdtobj->GetData(&fmte, &medium)))
  1415. {
  1416. // call delete here so that files will be moved in
  1417. // their respective bins, not necessarily this one.
  1418. DRAGINFO di;
  1419. di.uSize = sizeof(DRAGINFO);
  1420. if (DragQueryInfo((HDROP) medium.hGlobal, &di))
  1421. {
  1422. // Since BBWillRecycle() can return true even when the file will NOT be
  1423. // recycled (eg the file will be nuked), we want to warn the user when we
  1424. // are going to nuke something that they initiall thought that it would
  1425. // be recycled
  1426. UINT fOptions = SD_WARNONNUKE;
  1427. if (!BBWillRecycle(di.lpFileList, NULL) ||
  1428. (di.lpFileList && (di.lpFileList[lstrlen(di.lpFileList)+1] == 0)
  1429. && PathIsShortcutToProgram(di.lpFileList)))
  1430. fOptions = SD_USERCONFIRMATION;
  1431. if (IsFileInBitBucket(di.lpFileList))
  1432. {
  1433. LPITEMIDLIST *ppidl = NULL;
  1434. int cidl = CreateMoveCopyList((HDROP)medium.hGlobal, NULL, &ppidl);
  1435. if (ppidl)
  1436. {
  1437. // Bug#163533 (edwardp 8/15/00) Change this to use PositionItems.
  1438. PositionItems_DontUse(pbbtd->hwnd, cidl, ppidl, pbbtd->pdtobj, &pbbtd->ptDrop, pbbtd->fDragDrop, FALSE);
  1439. FreeIDListArray(ppidl, cidl);
  1440. }
  1441. }
  1442. else
  1443. {
  1444. TransferDelete(pbbtd->hwnd, (HDROP) medium.hGlobal, fOptions);
  1445. }
  1446. SHChangeNotifyHandleEvents();
  1447. SHFree(di.lpFileList);
  1448. }
  1449. ReleaseStgMedium(&medium);
  1450. }
  1451. return 0;
  1452. }
  1453. DWORD CALLBACK CBitBucket::_DispatchThreadProc(void *pv)
  1454. {
  1455. BBTHREADDATA *pbbtd = (BBTHREADDATA *)pv;
  1456. if (pbbtd->pstmDataObj)
  1457. {
  1458. CoGetInterfaceAndReleaseStream(pbbtd->pstmDataObj, IID_PPV_ARG(IDataObject, &pbbtd->pdtobj));
  1459. pbbtd->pstmDataObj = NULL; // this is dead
  1460. }
  1461. switch (pbbtd->idCmd)
  1462. {
  1463. case DFM_CMD_MOVE:
  1464. if (pbbtd->pdtobj)
  1465. _DropThreadInit(pbbtd);
  1466. break;
  1467. case DFM_CMD_PROPERTIES:
  1468. case FSIDM_PROPERTIESBG:
  1469. if (pbbtd->pdtobj)
  1470. pbbtd->pbb->_FileProperties(pbbtd->pdtobj);
  1471. else
  1472. pbbtd->pbb->_DefaultProperties(); // no data object for the background
  1473. break;
  1474. case DFM_CMD_DELETE:
  1475. if (pbbtd->pdtobj)
  1476. pbbtd->pbb->_NukeFileList(pbbtd->hwnd, pbbtd->pdtobj);
  1477. break;
  1478. case FSIDM_RESTORE:
  1479. if (pbbtd->pdtobj)
  1480. pbbtd->pbb->_RestoreFileList(pbbtd->hwnd, pbbtd->pdtobj);
  1481. break;
  1482. }
  1483. if (pbbtd->pdtobj)
  1484. pbbtd->pdtobj->Release();
  1485. pbbtd->pbb->Release();
  1486. LocalFree((HLOCAL)pbbtd);
  1487. return 0;
  1488. }
  1489. HRESULT CBitBucket::_LaunchThread(HWND hwnd, IDataObject *pdtobj, WPARAM idCmd)
  1490. {
  1491. HRESULT hr = E_OUTOFMEMORY;
  1492. BBTHREADDATA *pbbtd = (BBTHREADDATA *)LocalAlloc(LPTR, sizeof(*pbbtd));
  1493. if (pbbtd)
  1494. {
  1495. pbbtd->hwnd = hwnd;
  1496. pbbtd->idCmd = idCmd;
  1497. pbbtd->pbb = this;
  1498. pbbtd->pbb->AddRef();
  1499. if (idCmd == DFM_CMD_MOVE)
  1500. pbbtd->fDragDrop = (BOOL)ShellFolderView_GetDropPoint(hwnd, &pbbtd->ptDrop);
  1501. if (pdtobj)
  1502. CoMarshalInterThreadInterfaceInStream(IID_IDataObject, (IUnknown *)pdtobj, &pbbtd->pstmDataObj);
  1503. if (SHCreateThread(_DispatchThreadProc, pbbtd, CTF_COINIT, NULL))
  1504. {
  1505. hr = S_OK;
  1506. }
  1507. else
  1508. {
  1509. if (pbbtd->pstmDataObj)
  1510. pbbtd->pstmDataObj->Release();
  1511. pbbtd->pbb->Release();
  1512. LocalFree((HLOCAL)pbbtd);
  1513. }
  1514. }
  1515. return hr;
  1516. }
  1517. HRESULT GetVerb(UINT_PTR idCmd, LPSTR pszName, UINT cchMax, BOOL bUnicode)
  1518. {
  1519. HRESULT hr;
  1520. LPCTSTR psz;
  1521. switch (idCmd)
  1522. {
  1523. case FSIDM_RESTORE:
  1524. psz = TEXT("undelete");
  1525. break;
  1526. case FSIDM_PURGEALL:
  1527. psz = TEXT("empty");
  1528. break;
  1529. default:
  1530. return E_NOTIMPL;
  1531. }
  1532. if (bUnicode)
  1533. hr = SHTCharToUnicode(psz, (LPWSTR)pszName, cchMax);
  1534. else
  1535. hr = SHTCharToAnsi(psz, (LPSTR)pszName, cchMax);
  1536. return hr;
  1537. }
  1538. CBitBucket *CBitBucket::_FromFolder(IShellFolder *psf)
  1539. {
  1540. CBitBucket *pbbf = NULL;
  1541. if (psf)
  1542. psf->QueryInterface(CLSID_RecycleBin, (void **)&pbbf);
  1543. return pbbf;
  1544. }
  1545. // item context menu callback
  1546. HRESULT CALLBACK CBitBucket::_ItemMenuCallBack(IShellFolder *psf, HWND hwnd, IDataObject *pdtobj,
  1547. UINT uMsg, WPARAM wParam, LPARAM lParam)
  1548. {
  1549. CBitBucket *pbbf = _FromFolder(psf);
  1550. HRESULT hr = S_OK; // assume no error
  1551. switch (uMsg)
  1552. {
  1553. case DFM_MERGECONTEXTMENU:
  1554. CDefFolderMenu_MergeMenu(HINST_THISDLL, POPUP_BITBUCKET_ITEM, 0, (QCMINFO *)lParam);
  1555. hr = S_OK;
  1556. break;
  1557. case DFM_GETDEFSTATICID:
  1558. *(WPARAM *)lParam = DFM_CMD_PROPERTIES;
  1559. hr = S_OK;
  1560. break;
  1561. case DFM_MAPCOMMANDNAME:
  1562. if (lstrcmpi((LPCTSTR)lParam, TEXT("undelete")) == 0)
  1563. {
  1564. *(UINT_PTR *)wParam = FSIDM_RESTORE;
  1565. }
  1566. else
  1567. {
  1568. hr = E_FAIL; // command not found
  1569. }
  1570. break;
  1571. case DFM_INVOKECOMMAND:
  1572. switch (wParam)
  1573. {
  1574. case FSIDM_RESTORE:
  1575. case DFM_CMD_DELETE:
  1576. case DFM_CMD_PROPERTIES:
  1577. hr = pbbf->_LaunchThread(hwnd, pdtobj, wParam);
  1578. break;
  1579. default:
  1580. hr = S_FALSE;
  1581. break;
  1582. }
  1583. break;
  1584. case DFM_GETHELPTEXT:
  1585. LoadStringA(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPSTR)lParam, HIWORD(wParam));;
  1586. break;
  1587. case DFM_GETHELPTEXTW:
  1588. LoadStringW(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPWSTR)lParam, HIWORD(wParam));;
  1589. break;
  1590. case DFM_GETVERBA:
  1591. case DFM_GETVERBW:
  1592. hr = GetVerb((UINT_PTR)(LOWORD(wParam)), (LPSTR)lParam, (UINT)(HIWORD(wParam)), uMsg == DFM_GETVERBW);
  1593. break;
  1594. default:
  1595. hr = E_NOTIMPL;
  1596. break;
  1597. }
  1598. return hr;
  1599. }
  1600. class CBitBucketDropTarget : public CIDLDropTarget
  1601. {
  1602. public:
  1603. CBitBucketDropTarget(HWND hwnd, CBitBucket *pbbf) : CIDLDropTarget(hwnd), _pbbf(pbbf)
  1604. {
  1605. _pbbf->AddRef();
  1606. }
  1607. // IDropTarget (override base class)
  1608. STDMETHODIMP DragEnter(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
  1609. STDMETHODIMP Drop(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
  1610. private:
  1611. ~CBitBucketDropTarget()
  1612. {
  1613. _pbbf->Release();
  1614. }
  1615. CBitBucket *_pbbf;
  1616. };
  1617. //
  1618. // This function puts DROPEFFECT_LINK in *pdwEffect, only if the data object
  1619. // contains one or more net resource.
  1620. //
  1621. STDMETHODIMP CBitBucketDropTarget::DragEnter(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
  1622. {
  1623. TraceMsg(TF_BITBUCKET, "Bitbucket: CBitBucketDropTarget::DragEnter");
  1624. // Call the base class first
  1625. CIDLDropTarget::DragEnter(pDataObj, grfKeyState, pt, pdwEffect);
  1626. // we don't really care what is in the data object, as long as move
  1627. // is supported by the source we say you can move it to the wastbasket
  1628. // in the case of files we will do the regular recycle bin stuff, if
  1629. // it is not files we will just say it is moved and let the source delete it
  1630. *pdwEffect &= DROPEFFECT_MOVE;
  1631. m_dwEffectLastReturned = *pdwEffect;
  1632. return S_OK;
  1633. }
  1634. // This function creates a connection to a dropped net resource object.
  1635. STDMETHODIMP CBitBucketDropTarget::Drop(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
  1636. {
  1637. BOOL fWebFoldersHack = FALSE;
  1638. HRESULT hr;
  1639. // only move operation is allowed
  1640. *pdwEffect &= DROPEFFECT_MOVE;
  1641. if (*pdwEffect)
  1642. {
  1643. hr = CIDLDropTarget::DragDropMenu(DROPEFFECT_MOVE, pDataObj,
  1644. pt, pdwEffect, NULL, NULL, POPUP_NONDEFAULTDD, grfKeyState);
  1645. if (hr == S_FALSE)
  1646. {
  1647. // let callers know where this is about to go
  1648. // Defview cares where it went so it can handle non-filesys items
  1649. // SHScrap cares because it needs to close the file so we can delete it
  1650. DataObj_SetDropTarget(pDataObj, &CLSID_RecycleBin);
  1651. if (DataObj_GetDWORD(pDataObj, g_cfNotRecyclable, 0))
  1652. {
  1653. if (ShellMessageBox(HINST_THISDLL, NULL,
  1654. MAKEINTRESOURCE(IDS_CONFIRMNOTRECYCLABLE),
  1655. MAKEINTRESOURCE(IDS_RECCLEAN_NAMETEXT),
  1656. MB_SETFOREGROUND | MB_ICONQUESTION | MB_YESNO) == IDNO)
  1657. {
  1658. *pdwEffect = DROPEFFECT_NONE;
  1659. goto lCancel;
  1660. }
  1661. }
  1662. if (m_dwData & DTID_HDROP) // CF_HDROP
  1663. {
  1664. _pbbf->_LaunchThread(_GetWindow(), pDataObj, DFM_CMD_MOVE);
  1665. // since we will move the file ourself, known as an optimised move,
  1666. // we return zero here. this is per the OLE spec
  1667. *pdwEffect = DROPEFFECT_NONE;
  1668. }
  1669. else
  1670. {
  1671. // if it was not files, we just say we moved the data, letting the
  1672. // source deleted it. lets hope they support undo...
  1673. *pdwEffect = DROPEFFECT_MOVE;
  1674. // HACK: Put up a "you can't undo this" warning for web folders.
  1675. {
  1676. STGMEDIUM stgmed;
  1677. LPIDA pida = DataObj_GetHIDA(pDataObj, &stgmed);
  1678. if (pida)
  1679. {
  1680. LPCITEMIDLIST pidl = IDA_GetIDListPtr(pida, -1);
  1681. if (pidl)
  1682. {
  1683. IPersist *pPers;
  1684. hr = SHBindToIDListParent(pidl, IID_PPV_ARG(IPersist, &pPers), NULL);
  1685. if (SUCCEEDED(hr))
  1686. {
  1687. CLSID clsidSource;
  1688. hr = pPers->GetClassID(&clsidSource);
  1689. if (SUCCEEDED(hr) &&
  1690. IsEqualGUID(clsidSource, CLSID_WebFolders))
  1691. {
  1692. if (ShellMessageBox(HINST_THISDLL, NULL,
  1693. MAKEINTRESOURCE(IDS_CONFIRMNOTRECYCLABLE),
  1694. MAKEINTRESOURCE(IDS_RECCLEAN_NAMETEXT),
  1695. MB_SETFOREGROUND | MB_ICONQUESTION | MB_YESNO) == IDNO)
  1696. {
  1697. *pdwEffect = DROPEFFECT_NONE;
  1698. pPers->Release();
  1699. HIDA_ReleaseStgMedium (pida, &stgmed);
  1700. goto lCancel;
  1701. }
  1702. else
  1703. {
  1704. fWebFoldersHack = TRUE;
  1705. }
  1706. }
  1707. pPers->Release();
  1708. }
  1709. }
  1710. HIDA_ReleaseStgMedium(pida, &stgmed);
  1711. }
  1712. }
  1713. }
  1714. lCancel:
  1715. if (!fWebFoldersHack)
  1716. {
  1717. DataObj_SetDWORD(pDataObj, g_cfPerformedDropEffect, *pdwEffect);
  1718. DataObj_SetDWORD(pDataObj, g_cfLogicalPerformedDropEffect, DROPEFFECT_MOVE);
  1719. }
  1720. else
  1721. {
  1722. // Make web folders really delete its source file.
  1723. DataObj_SetDWORD (pDataObj, g_cfPerformedDropEffect, 0);
  1724. }
  1725. }
  1726. }
  1727. CIDLDropTarget::DragLeave();
  1728. return S_OK;
  1729. }
  1730. HRESULT CALLBACK CBitBucket::_BackgroundMenuCallBack(IShellFolder *psf, HWND hwnd,
  1731. IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1732. {
  1733. CBitBucket *pbbf = _FromFolder(psf);
  1734. HRESULT hr = S_OK;
  1735. switch (uMsg)
  1736. {
  1737. case DFM_MERGECONTEXTMENU_BOTTOM:
  1738. if (!(wParam & (CMF_VERBSONLY | CMF_DVFILE)))
  1739. {
  1740. QCMINFO *pqcm = (QCMINFO*)lParam;
  1741. UINT idFirst = pqcm->idCmdFirst;
  1742. CDefFolderMenu_MergeMenu(HINST_THISDLL, POPUP_PROPERTIES_BG, 0, (QCMINFO *)lParam);
  1743. if (SHRestricted(REST_BITBUCKNOPROP))
  1744. {
  1745. // Disable the Properties menu item
  1746. EnableMenuItem(pqcm->hmenu, idFirst + FSIDM_PROPERTIESBG, MF_GRAYED | MF_BYCOMMAND);
  1747. }
  1748. }
  1749. break;
  1750. case DFM_GETHELPTEXT:
  1751. LoadStringA(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPSTR)lParam, HIWORD(wParam));
  1752. break;
  1753. case DFM_GETHELPTEXTW:
  1754. LoadStringW(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPWSTR)lParam, HIWORD(wParam));
  1755. break;
  1756. case DFM_INVOKECOMMAND:
  1757. switch (wParam)
  1758. {
  1759. case FSIDM_PROPERTIESBG:
  1760. hr = pbbf->_LaunchThread(hwnd, NULL, FSIDM_PROPERTIESBG);
  1761. break;
  1762. case DFM_CMD_PASTE:
  1763. case DFM_CMD_PROPERTIES:
  1764. hr = S_FALSE; // do this for me
  1765. break;
  1766. default:
  1767. hr = E_FAIL;
  1768. break;
  1769. }
  1770. break;
  1771. default:
  1772. hr = E_NOTIMPL;
  1773. break;
  1774. }
  1775. return hr;
  1776. }
  1777. STDMETHODIMP CBitBucket::CreateViewObject(HWND hwnd, REFIID riid, void **ppv)
  1778. {
  1779. HRESULT hr;
  1780. *ppv = NULL;
  1781. if (IsEqualIID(riid, IID_IShellView))
  1782. {
  1783. IShellFolderViewCB* psfvcb;
  1784. hr = Create_CBitBucketViewCB(this, &psfvcb);
  1785. if (SUCCEEDED(hr))
  1786. {
  1787. SFV_CREATE sSFV = {0};
  1788. sSFV.cbSize = sizeof(sSFV);
  1789. sSFV.pshf = SAFECAST(this, IShellFolder *);
  1790. sSFV.psfvcb = psfvcb;
  1791. hr = SHCreateShellFolderView(&sSFV, (IShellView**)ppv);
  1792. psfvcb->Release();
  1793. }
  1794. }
  1795. else if (IsEqualIID(riid, IID_IDropTarget))
  1796. {
  1797. CBitBucketDropTarget *pbbdt = new CBitBucketDropTarget(hwnd, this);
  1798. if (pbbdt)
  1799. {
  1800. hr = pbbdt->_Init(_pidl);
  1801. if (SUCCEEDED(hr))
  1802. hr = pbbdt->QueryInterface(riid, ppv);
  1803. pbbdt->Release();
  1804. }
  1805. else
  1806. hr = E_OUTOFMEMORY;
  1807. }
  1808. else if (IsEqualIID(riid, IID_IContextMenu))
  1809. {
  1810. IContextMenu* pcmBase;
  1811. hr = CDefFolderMenu_Create(NULL, hwnd, 0, NULL, SAFECAST(this, IShellFolder *), _BackgroundMenuCallBack,
  1812. NULL, NULL, &pcmBase);
  1813. if (SUCCEEDED(hr))
  1814. {
  1815. IContextMenu* pcmFolder = SAFECAST(this, IContextMenu*);
  1816. IContextMenu* rgpcm[] = { pcmFolder, pcmBase };
  1817. hr = Create_ContextMenuOnContextMenuArray(rgpcm, ARRAYSIZE(rgpcm), riid, ppv);
  1818. pcmBase->Release();
  1819. }
  1820. }
  1821. else
  1822. {
  1823. *ppv = NULL;
  1824. hr = E_NOINTERFACE;
  1825. }
  1826. return hr;
  1827. }
  1828. // search the database on idDrive for file with index iIndex
  1829. LPITEMIDLIST CBitBucket::_DriveInfoToIDList(int idDrive, int iIndex)
  1830. {
  1831. LPITEMIDLIST pidl = NULL;
  1832. HANDLE hFile = OpenBBInfoFile(idDrive, OPENBBINFO_WRITE, 0);
  1833. if (hFile != INVALID_HANDLE_VALUE)
  1834. {
  1835. // read records until we find an index match
  1836. BBDATAENTRYW bbdew;
  1837. while (ReadNextDataEntry(hFile, &bbdew, TRUE, idDrive))
  1838. {
  1839. if (bbdew.iIndex == iIndex)
  1840. {
  1841. ASSERT(idDrive == bbdew.idDrive);
  1842. pidl = DataEntryToIDList(&bbdew);
  1843. break;
  1844. }
  1845. }
  1846. CloseBBInfoFile(hFile, idDrive);
  1847. }
  1848. return pidl;
  1849. }
  1850. // we implement this supporting D<drive_id><index>.ext
  1851. STDMETHODIMP CBitBucket::ParseDisplayName(HWND hwnd, IBindCtx *pbc, LPOLESTR pwszDisplayName,
  1852. ULONG *pchEaten, LPITEMIDLIST *ppidl, ULONG *pdwAttributes)
  1853. {
  1854. if (!ppidl)
  1855. return E_INVALIDARG;
  1856. *ppidl = NULL;
  1857. if (!pwszDisplayName)
  1858. return E_INVALIDARG;
  1859. int idDrive, iIndex;
  1860. HRESULT hr = BBFileNameToInfo(pwszDisplayName, &idDrive, &iIndex);
  1861. if (SUCCEEDED(hr))
  1862. {
  1863. // since anyone can call us with a path that is under the recycled directory,
  1864. // we need to check to make sure that we have inited this drive:
  1865. if (MakeBitBucket(idDrive))
  1866. {
  1867. *ppidl = _DriveInfoToIDList(idDrive, iIndex);
  1868. hr = *ppidl ? S_OK : E_FAIL;
  1869. }
  1870. else
  1871. {
  1872. hr = E_FAIL;
  1873. }
  1874. }
  1875. return hr;
  1876. }
  1877. // takes a full path to a file in a recycle bin storage folder and creates a
  1878. // single level bitbucket pidl
  1879. LPITEMIDLIST CBitBucket::PathToIDList(LPCTSTR pszPath)
  1880. {
  1881. LPITEMIDLIST pidl = NULL;
  1882. int idDrive = DriveIDFromBBPath(pszPath);
  1883. ASSERT(idDrive >= 0); // general UNC case will generate -1
  1884. int iIndex = BBPathToIndex(pszPath);
  1885. if (iIndex != -1)
  1886. {
  1887. pidl = _DriveInfoToIDList(idDrive, iIndex);
  1888. }
  1889. return pidl;
  1890. }
  1891. STDMETHODIMP CBitBucket::GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST *apidl,
  1892. REFIID riid, UINT *prgfInOut, void **ppv)
  1893. {
  1894. HRESULT hr;
  1895. *ppv = NULL;
  1896. if (cidl && IsEqualIID(riid, IID_IDataObject))
  1897. {
  1898. CBitBucketData *pbbd = new CBitBucketData(this, cidl, apidl);
  1899. if (pbbd)
  1900. {
  1901. hr = pbbd->QueryInterface(riid, ppv);
  1902. pbbd->Release();
  1903. }
  1904. else
  1905. hr = E_OUTOFMEMORY;
  1906. }
  1907. else if (IsEqualIID(riid, IID_IContextMenu))
  1908. {
  1909. hr = CDefFolderMenu_Create(_pidl, hwnd, cidl, apidl,
  1910. SAFECAST(this, IShellFolder *), _ItemMenuCallBack, NULL, NULL, (IContextMenu**)ppv);
  1911. }
  1912. else if (IsEqualIID(riid, IID_IDropTarget))
  1913. {
  1914. hr = E_FAIL; // You can't drop on internal items of the bitbucket!
  1915. }
  1916. else if (cidl == 1)
  1917. {
  1918. // blindly delegate unknown riid's to folder!
  1919. IShellFolder *psf;
  1920. hr = _FolderFromIDList(apidl[0], IID_PPV_ARG(IShellFolder, &psf));
  1921. if (SUCCEEDED(hr))
  1922. {
  1923. hr = psf->GetUIObjectOf(hwnd, cidl, apidl, riid, prgfInOut, ppv);
  1924. if (SUCCEEDED(hr) && IsEqualIID(riid, IID_IQueryInfo))
  1925. {
  1926. WrapInfotip(SAFECAST(this, IShellFolder2 *), apidl[0], &SCID_DELETEDFROM, (IUnknown *)*ppv);
  1927. }
  1928. psf->Release();
  1929. }
  1930. }
  1931. else
  1932. {
  1933. hr = E_NOTIMPL;
  1934. }
  1935. return hr;
  1936. }
  1937. HRESULT CBitBucket::_FolderFromDrive(int idDrive, REFIID riid, void **ppv)
  1938. {
  1939. *ppv = NULL;
  1940. ASSERT(idDrive < ARRAYSIZE(_rgFolders));
  1941. if (NULL == _rgFolders[idDrive])
  1942. {
  1943. PERSIST_FOLDER_TARGET_INFO pfti = {0};
  1944. DriveIDToBBPath(idDrive, pfti.szTargetParsingName);
  1945. pfti.dwAttributes = FILE_ATTRIBUTE_DIRECTORY;
  1946. pfti.csidl = -1;
  1947. CFSFolder_CreateFolder(NULL, NULL, _pidl, &pfti, IID_PPV_ARG(IUnknown, &_rgFolders[idDrive]));
  1948. }
  1949. return _rgFolders[idDrive] ? _rgFolders[idDrive]->QueryInterface(riid, ppv) : E_FAIL;
  1950. }
  1951. // accepts NULL, or undecorated recycle bin pidl (raw file system pidl).
  1952. // in these cases computes the default recycle bin file system folder
  1953. // index so we will defer to that.
  1954. int CBitBucket::_DriveIDFromIDList(LPCITEMIDLIST pidl)
  1955. {
  1956. int iDrive = 0;
  1957. PUBBDATAENTRYA pbbde = _IsValid(pidl);
  1958. if (pbbde)
  1959. {
  1960. iDrive = pbbde->idDrive;
  1961. }
  1962. else
  1963. {
  1964. // unknown, compute the default recycle bin folder index
  1965. TCHAR szPath[MAX_PATH];
  1966. if (GetWindowsDirectory(szPath, ARRAYSIZE(szPath)))
  1967. {
  1968. iDrive = PathGetDriveNumber(szPath);
  1969. if (iDrive < 0)
  1970. iDrive = 0;
  1971. }
  1972. }
  1973. ASSERT(iDrive >= 0 && iDrive < ARRAYSIZE(_rgFolders));
  1974. return iDrive;
  1975. }
  1976. // in:
  1977. // pidl of item, or NULL for default folder (base recycle bin)
  1978. HRESULT CBitBucket::_FolderFromIDList(LPCITEMIDLIST pidl, REFIID riid, void **ppv)
  1979. {
  1980. return _FolderFromDrive(_DriveIDFromIDList(pidl), riid, ppv);
  1981. }
  1982. // create a bitbucket pidl, start with the file system pidl, then add the extra data sections as needed
  1983. LPITEMIDLIST CBitBucket::DataEntryToIDList(BBDATAENTRYW *pbbde)
  1984. {
  1985. LPITEMIDLIST pidl = NULL;
  1986. WCHAR szFile[MAX_PATH];
  1987. GetDeletedFileName(szFile, pbbde);
  1988. IShellFolder *psf;
  1989. if (SUCCEEDED(_FolderFromDrive(pbbde->idDrive, IID_PPV_ARG(IShellFolder, &psf))))
  1990. {
  1991. if (SUCCEEDED(psf->ParseDisplayName(NULL, NULL, szFile, NULL, &pidl, NULL)))
  1992. {
  1993. HIDDENRECYCLEBINDATA hrbd = { {sizeof(hrbd), HRBD_CURRENTVERSION, IDLHID_RECYCLEBINDATA}};
  1994. hrbd.bbde = *((LPBBDATAENTRYA)pbbde);
  1995. pidl = ILAppendHiddenID(pidl, &hrbd.hid);
  1996. if (pidl)
  1997. {
  1998. if (g_pBitBucket[pbbde->idDrive]->fIsUnicode &&
  1999. !DoesStringRoundTrip(pbbde->szOriginal, NULL, 0))
  2000. {
  2001. pidl = ILAppendHiddenStringW(pidl, IDLHID_RECYCLEBINORIGINAL, pbbde->szOriginal);
  2002. }
  2003. }
  2004. }
  2005. psf->Release();
  2006. }
  2007. return pidl;
  2008. }
  2009. class CBitBucketEnum : public IEnumIDList
  2010. {
  2011. public:
  2012. // IUnknown
  2013. STDMETHOD(QueryInterface)(REFIID riid, void **ppv);
  2014. STDMETHOD_(ULONG,AddRef)();
  2015. STDMETHOD_(ULONG,Release)();
  2016. // IEnumIDList
  2017. STDMETHOD(Next)(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched);
  2018. STDMETHOD(Skip)(ULONG celt);
  2019. STDMETHOD(Reset)();
  2020. STDMETHOD(Clone)(IEnumIDList **ppenum);
  2021. CBitBucketEnum(CBitBucket *pbbf, DWORD grfFlags);
  2022. private:
  2023. HRESULT _BuildEnumDPA();
  2024. ~CBitBucketEnum();
  2025. LONG _cRef;
  2026. CBitBucket *_pbbf;
  2027. HDPA _hdpa;
  2028. int _nItem;
  2029. DWORD _grfFlags;
  2030. };
  2031. CBitBucketEnum::CBitBucketEnum(CBitBucket *pbbf, DWORD grfFlags) :
  2032. _cRef(1), _pbbf(pbbf), _grfFlags(grfFlags)
  2033. {
  2034. _pbbf->AddRef();
  2035. }
  2036. CBitBucketEnum::~CBitBucketEnum()
  2037. {
  2038. Reset();
  2039. _pbbf->Release();
  2040. }
  2041. STDMETHODIMP CBitBucketEnum::QueryInterface(REFIID riid, void **ppv)
  2042. {
  2043. static const QITAB qit[] = {
  2044. QITABENT(CBitBucketEnum, IEnumIDList),
  2045. { 0 },
  2046. };
  2047. return QISearch(this, qit, riid, ppv);
  2048. }
  2049. STDMETHODIMP_(ULONG) CBitBucketEnum::AddRef()
  2050. {
  2051. return InterlockedIncrement(&_cRef);
  2052. }
  2053. STDMETHODIMP_(ULONG) CBitBucketEnum::Release()
  2054. {
  2055. if (InterlockedDecrement(&_cRef))
  2056. return _cRef;
  2057. delete this;
  2058. return 0;
  2059. }
  2060. #ifdef DEBUG
  2061. #define BB_DELETED_ENTRY_MAX 10 // smaller to force compaction more often
  2062. #else
  2063. #define BB_DELETED_ENTRY_MAX 100
  2064. #endif
  2065. // on the first ::Next() call snapshot the data needed to do the enum
  2066. HRESULT CBitBucketEnum::_BuildEnumDPA()
  2067. {
  2068. HRESULT hr = S_OK;
  2069. if (NULL == _hdpa)
  2070. {
  2071. _hdpa = DPA_CreateEx(0, NULL);
  2072. if (_hdpa)
  2073. {
  2074. if (_grfFlags & SHCONTF_NONFOLDERS) //if they asked for folders we have none so leave DPA empty
  2075. {
  2076. // loop through the bitbucket drives to find an info file
  2077. for (int iBitBucket = 0; iBitBucket < MAX_BITBUCKETS; iBitBucket++)
  2078. {
  2079. if (MakeBitBucket(iBitBucket))
  2080. {
  2081. int cDeleted = 0;
  2082. HANDLE hFile = OpenBBInfoFile(iBitBucket, OPENBBINFO_WRITE, 0);
  2083. if (INVALID_HANDLE_VALUE != hFile)
  2084. {
  2085. BBDATAENTRYW bbdew;
  2086. while (ReadNextDataEntry(hFile, &bbdew, FALSE, iBitBucket))
  2087. {
  2088. if (IsDeletedEntry(&bbdew))
  2089. cDeleted++;
  2090. else
  2091. {
  2092. ASSERT(iBitBucket == bbdew.idDrive);
  2093. LPITEMIDLIST pidl = _pbbf->DataEntryToIDList(&bbdew);
  2094. if (pidl)
  2095. {
  2096. if (-1 == DPA_AppendPtr(_hdpa, pidl))
  2097. ILFree(pidl);
  2098. }
  2099. }
  2100. }
  2101. if (cDeleted > BB_DELETED_ENTRY_MAX)
  2102. {
  2103. BOOL bTrue = TRUE;
  2104. // set the registry key so that we will compact the info file after the next delete operation
  2105. RegSetValueEx(g_pBitBucket[iBitBucket]->hkeyPerUser, TEXT("NeedToCompact"), 0, REG_DWORD, (LPBYTE)&bTrue, sizeof(bTrue));
  2106. }
  2107. CloseBBInfoFile(hFile, iBitBucket);
  2108. }
  2109. }
  2110. }
  2111. }
  2112. }
  2113. else
  2114. {
  2115. hr = E_OUTOFMEMORY;
  2116. }
  2117. }
  2118. return hr;
  2119. }
  2120. STDMETHODIMP CBitBucketEnum::Next(ULONG celt, LPITEMIDLIST *ppidl, ULONG *pceltFetched)
  2121. {
  2122. *ppidl = NULL;
  2123. HRESULT hr = _BuildEnumDPA();
  2124. if (SUCCEEDED(hr))
  2125. {
  2126. LPITEMIDLIST pidl = (LPITEMIDLIST)DPA_GetPtr(_hdpa, _nItem);
  2127. if (pidl)
  2128. {
  2129. hr = SHILClone(pidl, ppidl);
  2130. _nItem++;
  2131. }
  2132. else
  2133. {
  2134. hr = S_FALSE; // no more items
  2135. }
  2136. }
  2137. if (pceltFetched)
  2138. *pceltFetched = (hr == S_OK) ? 1 : 0;
  2139. return hr;
  2140. }
  2141. STDMETHODIMP CBitBucketEnum::Skip(ULONG celt)
  2142. {
  2143. HRESULT hr = E_FAIL;
  2144. if (_hdpa)
  2145. {
  2146. _nItem += celt;
  2147. if (_nItem >= DPA_GetPtrCount(_hdpa))
  2148. {
  2149. _nItem = DPA_GetPtrCount(_hdpa);
  2150. hr = S_FALSE;
  2151. }
  2152. else
  2153. {
  2154. hr = S_OK;
  2155. }
  2156. }
  2157. return hr;
  2158. }
  2159. STDMETHODIMP CBitBucketEnum::Reset()
  2160. {
  2161. DPA_FreeIDArray(_hdpa);
  2162. _hdpa = NULL;
  2163. _nItem = 0;
  2164. return S_OK;
  2165. }
  2166. STDMETHODIMP CBitBucketEnum::Clone(IEnumIDList **ppenum)
  2167. {
  2168. *ppenum = NULL;
  2169. return E_NOTIMPL;
  2170. }
  2171. STDMETHODIMP CBitBucket::EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList **ppenum)
  2172. {
  2173. *ppenum = NULL;
  2174. HRESULT hr;
  2175. CBitBucketEnum *penum = new CBitBucketEnum(this, grfFlags);
  2176. if (penum)
  2177. {
  2178. hr = penum->QueryInterface(IID_PPV_ARG(IEnumIDList, ppenum));
  2179. penum->Release();
  2180. }
  2181. else
  2182. {
  2183. hr = E_OUTOFMEMORY;
  2184. }
  2185. return hr;
  2186. }
  2187. STDMETHODIMP CBitBucket::BindToObject(LPCITEMIDLIST pidl, IBindCtx *pbc, REFIID riid, void **ppv)
  2188. {
  2189. *ppv = NULL;
  2190. HRESULT hr = E_NOTIMPL;
  2191. if (riid != IID_IShellFolder && riid != IID_IShellFolder2)
  2192. {
  2193. // let IPropertySetStorage/IStream/etc binds go through
  2194. IShellFolder *psf;
  2195. hr = _FolderFromIDList(pidl, IID_PPV_ARG(IShellFolder, &psf));
  2196. if (SUCCEEDED(hr))
  2197. {
  2198. hr = psf->BindToObject(pidl, pbc, riid, ppv);
  2199. psf->Release();
  2200. }
  2201. }
  2202. return hr;
  2203. }
  2204. STDMETHODIMP CBitBucket::BindToStorage(LPCITEMIDLIST pidl, IBindCtx *pbc, REFIID riid, void **ppv)
  2205. {
  2206. return BindToObject(pidl, pbc, riid, ppv);
  2207. }
  2208. DWORD CBitBucket::_IsFolder(LPCITEMIDLIST pidl)
  2209. {
  2210. DWORD dwAttributes = SFGAO_FOLDER;
  2211. HRESULT hr = GetAttributesOf(1, &pidl, &dwAttributes);
  2212. return (SUCCEEDED(hr) && (SFGAO_FOLDER & dwAttributes)) ? FILE_ATTRIBUTE_DIRECTORY : 0;
  2213. }
  2214. STDMETHODIMP CBitBucket::GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD dwFlags, STRRET *psr)
  2215. {
  2216. HRESULT hr;
  2217. // on change notifications we can get file system pidls that don't have our
  2218. // extra data in them. in this case we need to delegate to the file system
  2219. // folder
  2220. if ((0 == (dwFlags & SHGDN_FORPARSING)) && _IsValid(pidl))
  2221. {
  2222. TCHAR szTemp[MAX_PATH];
  2223. hr = _OriginalPath(pidl, szTemp, ARRAYSIZE(szTemp));
  2224. if (SUCCEEDED(hr))
  2225. {
  2226. if (dwFlags & SHGDN_INFOLDER)
  2227. {
  2228. SHFILEINFO sfi;
  2229. if (SHGetFileInfo(szTemp, _IsFolder(pidl), &sfi, sizeof(sfi), SHGFI_USEFILEATTRIBUTES | SHGFI_DISPLAYNAME))
  2230. {
  2231. hr = StringToStrRet(sfi.szDisplayName, psr);
  2232. }
  2233. else
  2234. {
  2235. hr = E_FAIL;
  2236. }
  2237. }
  2238. else
  2239. {
  2240. hr = StringToStrRet(szTemp, psr);
  2241. }
  2242. }
  2243. }
  2244. else
  2245. {
  2246. IShellFolder *psf;
  2247. hr = _FolderFromIDList(pidl, IID_PPV_ARG(IShellFolder, &psf));
  2248. if (SUCCEEDED(hr))
  2249. {
  2250. hr = psf->GetDisplayNameOf(pidl, dwFlags, psr);
  2251. psf->Release();
  2252. }
  2253. }
  2254. return hr;
  2255. }
  2256. STDMETHODIMP CBitBucket::SetNameOf(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR pszName, DWORD dwRes, LPITEMIDLIST *ppidlOut)
  2257. {
  2258. return E_FAIL;
  2259. }
  2260. STDMETHODIMP CBitBucket::GetDefaultSearchGUID(GUID *pguid)
  2261. {
  2262. return DefaultSearchGUID(pguid);
  2263. }
  2264. STDMETHODIMP CBitBucket::EnumSearches(IEnumExtraSearch **ppenum)
  2265. {
  2266. *ppenum = NULL;
  2267. return E_NOTIMPL;
  2268. }
  2269. STDMETHODIMP CBitBucket::GetDefaultColumn(DWORD dwRes, ULONG *pSort, ULONG *pDisplay)
  2270. {
  2271. return E_NOTIMPL;
  2272. }
  2273. STDMETHODIMP CBitBucket::GetDefaultColumnState(UINT iColumn, DWORD *pdwState)
  2274. {
  2275. HRESULT hr;
  2276. if (_MapColIndex(&iColumn))
  2277. {
  2278. *pdwState = c_bb_cols[iColumn].csFlags | SHCOLSTATE_PREFER_VARCMP;
  2279. hr = S_OK;
  2280. }
  2281. else
  2282. {
  2283. IShellFolder2 *psf;
  2284. hr = _FolderFromIDList(NULL, IID_PPV_ARG(IShellFolder2, &psf));
  2285. if (SUCCEEDED(hr))
  2286. {
  2287. hr = psf->GetDefaultColumnState(iColumn, pdwState);
  2288. psf->Release();
  2289. }
  2290. }
  2291. return hr;
  2292. }
  2293. STDMETHODIMP CBitBucket::GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv)
  2294. {
  2295. HRESULT hr;
  2296. if (IsEqualSCID(*pscid, SCID_DELETEDFROM))
  2297. {
  2298. TCHAR szTemp[MAX_PATH];
  2299. hr = _OriginalDirectory(pidl, szTemp, ARRAYSIZE(szTemp));
  2300. if (SUCCEEDED(hr))
  2301. hr = InitVariantFromStr(pv, szTemp);
  2302. }
  2303. else if (IsEqualSCID(*pscid, SCID_DATEDELETED))
  2304. {
  2305. FILETIME ft;
  2306. _GetDeletedFileTime(pidl, &ft);
  2307. hr = InitVariantFromFileTime(&ft, pv);
  2308. }
  2309. else if (IsEqualSCID(*pscid, SCID_DIRECTORY))
  2310. {
  2311. // don't let this get through to file folder as we want to hide
  2312. // the real file system folder from callers
  2313. VariantInit(pv);
  2314. hr = E_FAIL;
  2315. }
  2316. else if (IsEqualSCID(*pscid, SCID_SIZE))
  2317. {
  2318. pv->ullVal = _GetDeletedSize(pidl);
  2319. pv->vt = VT_UI8;
  2320. hr = S_OK;
  2321. }
  2322. else
  2323. {
  2324. IShellFolder2 *psf;
  2325. hr = _FolderFromIDList(pidl, IID_PPV_ARG(IShellFolder2, &psf));
  2326. if (SUCCEEDED(hr))
  2327. {
  2328. hr = psf->GetDetailsEx(pidl, pscid, pv);
  2329. psf->Release();
  2330. }
  2331. }
  2332. return hr;
  2333. }
  2334. BOOL CBitBucket::_MapColIndex(UINT *piColumn)
  2335. {
  2336. switch (*piColumn)
  2337. {
  2338. case ICOL_NAME: // 0
  2339. case ICOL_ORIGINAL: // 1
  2340. case ICOL_DATEDELETED: // 2
  2341. return TRUE;
  2342. default: // >= 3
  2343. *piColumn -= ICOL_DATEDELETED;
  2344. return FALSE;
  2345. }
  2346. }
  2347. STDMETHODIMP CBitBucket::GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pdi)
  2348. {
  2349. HRESULT hr;
  2350. if (_MapColIndex(&iColumn))
  2351. {
  2352. if (pidl)
  2353. {
  2354. TCHAR szTemp[MAX_PATH];
  2355. szTemp[0] = 0;
  2356. switch (iColumn)
  2357. {
  2358. case ICOL_NAME:
  2359. DisplayNameOf(this, pidl, SHGDN_INFOLDER, szTemp, ARRAYSIZE(szTemp));
  2360. break;
  2361. case ICOL_ORIGINAL:
  2362. _OriginalDirectory(pidl, szTemp, ARRAYSIZE(szTemp));
  2363. break;
  2364. case ICOL_DATEDELETED:
  2365. {
  2366. FILETIME ft;
  2367. _GetDeletedFileTime(pidl, &ft);
  2368. DWORD dwFlags = FDTF_DEFAULT;
  2369. switch (pdi->fmt)
  2370. {
  2371. case LVCFMT_LEFT_TO_RIGHT:
  2372. dwFlags |= FDTF_LTRDATE;
  2373. break;
  2374. case LVCFMT_RIGHT_TO_LEFT:
  2375. dwFlags |= FDTF_RTLDATE;
  2376. break;
  2377. }
  2378. SHFormatDateTime(&ft, &dwFlags, szTemp, ARRAYSIZE(szTemp));
  2379. }
  2380. break;
  2381. }
  2382. hr = StringToStrRet(szTemp, &pdi->str);
  2383. }
  2384. else
  2385. {
  2386. hr = GetDetailsOfInfo(c_bb_cols, ARRAYSIZE(c_bb_cols), iColumn, pdi);
  2387. }
  2388. }
  2389. else
  2390. {
  2391. if (pidl && (_SizeColumn() == iColumn))
  2392. {
  2393. TCHAR szTemp[64];
  2394. StrFormatKBSize(_GetDeletedSize(pidl), szTemp, ARRAYSIZE(szTemp));
  2395. hr = StringToStrRet(szTemp, &pdi->str);
  2396. }
  2397. else
  2398. {
  2399. IShellFolder2 *psf;
  2400. hr = _FolderFromIDList(pidl, IID_PPV_ARG(IShellFolder2, &psf));
  2401. if (SUCCEEDED(hr))
  2402. {
  2403. hr = psf->GetDetailsOf(pidl, iColumn, pdi);
  2404. psf->Release();
  2405. }
  2406. }
  2407. }
  2408. return hr;
  2409. }
  2410. UINT CBitBucket::_SizeColumn()
  2411. {
  2412. if (-1 == _uiColumnSize)
  2413. {
  2414. _uiColumnSize = MapSCIDToColumn(SAFECAST(this, IShellFolder2 *), &SCID_SIZE);
  2415. _MapColIndex(&_uiColumnSize); // map to other folder index space
  2416. }
  2417. return _uiColumnSize;
  2418. }
  2419. STDMETHODIMP CBitBucket::MapColumnToSCID(UINT iColumn, SHCOLUMNID *pscid)
  2420. {
  2421. HRESULT hr;
  2422. if (_MapColIndex(&iColumn))
  2423. {
  2424. hr = MapColumnToSCIDImpl(c_bb_cols, ARRAYSIZE(c_bb_cols), iColumn, pscid);
  2425. }
  2426. else
  2427. {
  2428. IShellFolder2 *psf;
  2429. hr = _FolderFromIDList(NULL, IID_PPV_ARG(IShellFolder2, &psf));
  2430. if (SUCCEEDED(hr))
  2431. {
  2432. hr = psf->MapColumnToSCID(iColumn, pscid);
  2433. psf->Release();
  2434. }
  2435. }
  2436. return hr;
  2437. }
  2438. // IPersist
  2439. STDMETHODIMP CBitBucket::GetClassID(CLSID *pclsid)
  2440. {
  2441. *pclsid = CLSID_RecycleBin;
  2442. return S_OK;
  2443. }
  2444. // IPersistFolder
  2445. STDMETHODIMP CBitBucket::Initialize(LPCITEMIDLIST pidl)
  2446. {
  2447. return Pidl_Set(&_pidl, pidl) ? S_OK : E_OUTOFMEMORY;
  2448. }
  2449. // IPersistFolder2
  2450. STDMETHODIMP CBitBucket::GetCurFolder(LPITEMIDLIST *ppidl)
  2451. {
  2452. return GetCurFolderImpl(_pidl, ppidl);
  2453. }
  2454. // IShellExtInit
  2455. STDMETHODIMP CBitBucket::Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID)
  2456. {
  2457. return S_OK;
  2458. }
  2459. // IContextMenu
  2460. STDMETHODIMP CBitBucket::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
  2461. {
  2462. int idMax = idCmdFirst;
  2463. HMENU hmMerge = SHLoadPopupMenu(HINST_THISDLL, POPUP_BITBUCKET_POPUPMERGE);
  2464. if (hmMerge)
  2465. {
  2466. if (IsRecycleBinEmpty())
  2467. {
  2468. EnableMenuItem(hmMerge, FSIDM_PURGEALL, MF_GRAYED | MF_BYCOMMAND);
  2469. }
  2470. idMax = Shell_MergeMenus(hmenu, hmMerge, indexMenu, idCmdFirst, idCmdLast, 0);
  2471. DestroyMenu(hmMerge);
  2472. }
  2473. return ResultFromShort(idMax - idCmdFirst);
  2474. }
  2475. const ICIVERBTOIDMAP c_sBBCmdInfo[] = {
  2476. { L"empty", "empty", FSIDM_PURGEALL, FSIDM_PURGEALL, },
  2477. };
  2478. STDMETHODIMP CBitBucket::InvokeCommand(LPCMINVOKECOMMANDINFO pici)
  2479. {
  2480. UINT idCmd;
  2481. HRESULT hr = SHMapICIVerbToCmdID(pici, c_sBBCmdInfo, ARRAYSIZE(c_sBBCmdInfo), &idCmd);
  2482. if (SUCCEEDED(hr))
  2483. {
  2484. switch (idCmd)
  2485. {
  2486. case FSIDM_PURGEALL:
  2487. hr = BBPurgeAll(pici->hwnd, 0);
  2488. // command is ours, let caller know we processed it (but we want the right success code)
  2489. if (FAILED(hr))
  2490. hr = S_FALSE;
  2491. break;
  2492. default:
  2493. hr = E_FAIL;
  2494. break;
  2495. }
  2496. }
  2497. return hr;
  2498. }
  2499. STDMETHODIMP CBitBucket::GetCommandString(UINT_PTR idCmd, UINT wFlags, UINT * pwReserved, LPSTR pszName, UINT cchMax)
  2500. {
  2501. switch (wFlags)
  2502. {
  2503. case GCS_VERBA:
  2504. case GCS_VERBW:
  2505. return SHMapCmdIDToVerb(idCmd, c_sBBCmdInfo, ARRAYSIZE(c_sBBCmdInfo), pszName, cchMax, wFlags == GCS_VERBW);
  2506. case GCS_HELPTEXTA:
  2507. return LoadStringA(HINST_THISDLL,
  2508. (UINT)(idCmd + IDS_MH_FSIDM_FIRST),
  2509. pszName, cchMax) ? S_OK : E_OUTOFMEMORY;
  2510. case GCS_HELPTEXTW:
  2511. return LoadStringW(HINST_THISDLL,
  2512. (UINT)(idCmd + IDS_MH_FSIDM_FIRST),
  2513. (LPWSTR)pszName, cchMax) ? S_OK : E_OUTOFMEMORY;
  2514. default:
  2515. return E_NOTIMPL;
  2516. }
  2517. }
  2518. //
  2519. // Callback function that saves the location of the HPROPSHEETPAGE's
  2520. // LPPROPSHEETPAGE so we can pass it to other propsheet pages.
  2521. //
  2522. UINT CALLBACK CBitBucket::_GlobalSettingsCalback(HWND hwnd, UINT uMsg, LPPROPSHEETPAGE ppsp)
  2523. {
  2524. switch (uMsg)
  2525. {
  2526. case PSPCB_ADDREF:
  2527. {
  2528. // we save off the address of the "real" ppsi in the pGlobal param of the
  2529. // the template, so that the other drives can get to the global page information
  2530. BBPROPSHEETINFO *ppsiGlobal = (BBPROPSHEETINFO *)ppsp;
  2531. BBPROPSHEETINFO *ppsiTemplate = (BBPROPSHEETINFO *)ppsp->lParam;
  2532. ppsiTemplate->pGlobal = ppsiGlobal;
  2533. ppsiGlobal->pGlobal = ppsiGlobal;
  2534. }
  2535. break;
  2536. case PSPCB_CREATE:
  2537. return TRUE; // Yes, please create me
  2538. }
  2539. return 0;
  2540. }
  2541. STDMETHODIMP CBitBucket::AddPages(LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam)
  2542. {
  2543. BBPROPSHEETINFO bbpsp;
  2544. TCHAR szTitle[MAX_PATH];
  2545. if (SHRestricted(REST_BITBUCKNOPROP))
  2546. {
  2547. return E_ACCESSDENIED;
  2548. }
  2549. // read in the global settings
  2550. DWORD dwSize1 = sizeof(bbpsp.fUseGlobalSettings);
  2551. DWORD dwSize2 = sizeof(bbpsp.iOriginalPercent);
  2552. DWORD dwSize3 = sizeof(bbpsp.fOriginalNukeOnDelete);
  2553. if (RegQueryValueEx(g_hkBitBucket, TEXT("UseGlobalSettings"), NULL, NULL, (LPBYTE)&bbpsp.fOriginalUseGlobalSettings, &dwSize1) != ERROR_SUCCESS ||
  2554. RegQueryValueEx(g_hkBitBucket, TEXT("Percent"), NULL, NULL, (LPBYTE)&bbpsp.iOriginalPercent, &dwSize2) != ERROR_SUCCESS ||
  2555. RegQueryValueEx(g_hkBitBucket, TEXT("NukeOnDelete"), NULL, NULL, (LPBYTE)&bbpsp.fOriginalNukeOnDelete, &dwSize3) != ERROR_SUCCESS)
  2556. {
  2557. ASSERTMSG(FALSE, "Bitbucket: could not read global settings from the registry, re-regsvr32 shell32.dll!!");
  2558. bbpsp.fUseGlobalSettings = TRUE;
  2559. bbpsp.iOriginalPercent = 10;
  2560. bbpsp.fOriginalNukeOnDelete = FALSE;
  2561. }
  2562. // Check policies
  2563. bbpsp.fPolicyNukeOnDelete = SHRestricted(REST_BITBUCKNUKEONDELETE);
  2564. if (bbpsp.fPolicyNukeOnDelete)
  2565. {
  2566. bbpsp.fOriginalNukeOnDelete = TRUE;
  2567. bbpsp.fOriginalUseGlobalSettings = TRUE;
  2568. }
  2569. bbpsp.fPolicyPercent = (ReadPolicySetting(NULL,
  2570. L"Explorer",
  2571. L"RecycleBinSize",
  2572. (LPBYTE)&bbpsp.iPercent,
  2573. sizeof(bbpsp.iPercent)) == ERROR_SUCCESS);
  2574. if (bbpsp.fPolicyPercent)
  2575. {
  2576. bbpsp.iOriginalPercent = bbpsp.iPercent;
  2577. }
  2578. else
  2579. {
  2580. bbpsp.iPercent = bbpsp.iOriginalPercent;
  2581. }
  2582. bbpsp.fUseGlobalSettings = bbpsp.fOriginalUseGlobalSettings;
  2583. bbpsp.fNukeOnDelete = bbpsp.fOriginalNukeOnDelete;
  2584. bbpsp.psp.dwSize = sizeof(bbpsp);
  2585. bbpsp.psp.dwFlags = PSP_DEFAULT | PSP_USECALLBACK;
  2586. bbpsp.psp.hInstance = HINST_THISDLL;
  2587. bbpsp.psp.pszTemplate = MAKEINTRESOURCE(DLG_BITBUCKET_GENCONFIG);
  2588. bbpsp.psp.pfnDlgProc = _GlobalPropDlgProc;
  2589. bbpsp.psp.lParam = (LPARAM)&bbpsp;
  2590. // the callback will fill the bbpsp.pGlobal with the pointer to the "real" psp after it has been copied
  2591. // so that the other drive pages can get to the global information
  2592. bbpsp.psp.pfnCallback = _GlobalSettingsCalback;
  2593. // add the "Global" settings page
  2594. HPROPSHEETPAGE hpage = CreatePropertySheetPage(&bbpsp.psp);
  2595. #ifdef UNICODE
  2596. // If this assertion fires, it means that comctl32 lost
  2597. // backwards-compatibility with Win95 shell, WinNT4 shell,
  2598. // and IE4 shell, all of which relied on this undocumented
  2599. // behavior.
  2600. ASSERT(bbpsp.pGlobal == (BBPROPSHEETINFO *)((LPBYTE)hpage + 2 * sizeof(void *)));
  2601. #else
  2602. ASSERT(bbpsp.pGlobal == (BBPROPSHEETINFO *)hpage);
  2603. #endif
  2604. pfnAddPage(hpage, lParam);
  2605. // now create the pages for the individual drives
  2606. bbpsp.psp.dwFlags = PSP_USETITLE;
  2607. bbpsp.psp.pszTemplate = MAKEINTRESOURCE(DLG_BITBUCKET_CONFIG);
  2608. bbpsp.psp.pfnDlgProc = _DriveDlgProc;
  2609. bbpsp.psp.pszTitle = szTitle;
  2610. int idDrive, iPage;
  2611. for (idDrive = 0, iPage = 1; (idDrive < MAX_BITBUCKETS) && (iPage < MAXPROPPAGES); idDrive++)
  2612. {
  2613. if (MakeBitBucket(idDrive))
  2614. {
  2615. dwSize1 = sizeof(bbpsp.iOriginalPercent);
  2616. dwSize2 = sizeof(bbpsp.fOriginalNukeOnDelete);
  2617. if (RegQueryValueEx(g_pBitBucket[idDrive]->hkey, TEXT("Percent"), NULL, NULL, (LPBYTE)&bbpsp.iOriginalPercent, &dwSize1) != ERROR_SUCCESS ||
  2618. RegQueryValueEx(g_pBitBucket[idDrive]->hkey, TEXT("NukeOnDelete"), NULL, NULL, (LPBYTE)&bbpsp.fOriginalNukeOnDelete, &dwSize2) != ERROR_SUCCESS)
  2619. {
  2620. TraceMsg(TF_BITBUCKET, "Bitbucket: could not read settings from the registry for drive %d, using lame defaults", idDrive);
  2621. bbpsp.iOriginalPercent = 10;
  2622. bbpsp.fOriginalNukeOnDelete = FALSE;
  2623. }
  2624. if (bbpsp.fPolicyNukeOnDelete)
  2625. {
  2626. bbpsp.fOriginalNukeOnDelete = TRUE;
  2627. }
  2628. if (bbpsp.fPolicyPercent)
  2629. {
  2630. bbpsp.iOriginalPercent = bbpsp.iPercent;
  2631. }
  2632. bbpsp.iPercent = bbpsp.iOriginalPercent;
  2633. bbpsp.fNukeOnDelete = bbpsp.fOriginalNukeOnDelete;
  2634. bbpsp.idDrive = idDrive;
  2635. _GetDriveDisplayName(idDrive, szTitle, ARRAYSIZE(szTitle));
  2636. hpage = CreatePropertySheetPage(&bbpsp.psp);
  2637. pfnAddPage(hpage, lParam);
  2638. }
  2639. }
  2640. return S_OK;
  2641. }
  2642. STDMETHODIMP CBitBucket::ReplacePage(UINT uPageID, LPFNADDPROPSHEETPAGE pfnReplaceWith, LPARAM lParam)
  2643. {
  2644. return E_NOTIMPL;
  2645. }
  2646. STDAPI CBitBucket_CreateInstance(IUnknown* punkOuter, REFIID riid, void **ppv)
  2647. {
  2648. *ppv = NULL;
  2649. HRESULT hr = E_OUTOFMEMORY;
  2650. CBitBucket *pbb = new CBitBucket();
  2651. if (pbb)
  2652. {
  2653. if (InitBBGlobals())
  2654. hr = pbb->QueryInterface(riid, ppv);
  2655. pbb->Release();
  2656. }
  2657. return hr;
  2658. }