Leaked source code of windows server 2003
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.

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