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

2648 lines
81 KiB

  1. #include "shellprv.h"
  2. #include "caggunk.h"
  3. #include "views.h"
  4. #include "drives.h"
  5. #include "netview.h"
  6. #include "propsht.h"
  7. #include "infotip.h"
  8. #include "mtpt.h"
  9. #include "prop.h"
  10. #include "defcm.h"
  11. #include "basefvcb.h"
  12. #include "fstreex.h"
  13. #include "ovrlaymn.h"
  14. #include "shitemid.h"
  15. #include "clsobj.h"
  16. #include "deskfldr.h"
  17. #include "datautil.h"
  18. #include <ntddcdrm.h>
  19. #include <cfgmgr32.h> // MAX_GUID_STRING_LEN
  20. #include "ole2dup.h"
  21. #include "category.h"
  22. #define EXCLUDE_COMPPROPSHEET
  23. #include "unicpp\dcomp.h"
  24. #undef EXCLUDE_COMPPROPSHEET
  25. #include "enumidlist.h"
  26. #include <enumt.h>
  27. #define ShowDriveInfo(_iDrive) (!IsRemovableDrive(_iDrive))
  28. #define CDRIVES_REGITEM_CONTROL 0
  29. #define IDLIST_DRIVES ((LPCITEMIDLIST)&c_idlDrives)
  30. // These are the sort order for items in MyComputer
  31. #define CONTROLS_SORT_INDEX 30
  32. #define CDRIVES_REGITEM_CONTROL 0
  33. REQREGITEM g_asDrivesReqItems[] =
  34. {
  35. { &CLSID_ControlPanel, IDS_CONTROLPANEL, c_szShell32Dll, -IDI_CPLFLD, CONTROLS_SORT_INDEX, SFGAO_FOLDER | SFGAO_HASSUBFOLDER, NULL},
  36. };
  37. STDAPI CDriveExtractImage_Create(LPCIDDRIVE pidd, REFIID riid, void **ppvObj);
  38. class CDrivesViewCallback;
  39. class CDrivesFolderEnum;
  40. class CDrivesBackgroundMenuCB : public IContextMenuCB
  41. {
  42. public:
  43. CDrivesBackgroundMenuCB(LPITEMIDLIST pidlFolder);
  44. ~CDrivesBackgroundMenuCB();
  45. // IUnknown
  46. STDMETHOD(QueryInterface)(REFIID riid, void **ppv);
  47. STDMETHOD_(ULONG,AddRef)();
  48. STDMETHOD_(ULONG,Release)();
  49. // IContextMenuCB
  50. STDMETHOD(CallBack) (IShellFolder *psf, HWND hwndOwner, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam);
  51. private:
  52. STDMETHOD(_GetHelpText) (UINT offset, BOOL bWide, LPARAM lParam, UINT cch);
  53. LPITEMIDLIST _pidlFolder;
  54. LONG _cRef;
  55. };
  56. class CDrivesFolder : public CAggregatedUnknown, IShellFolder2, IPersistFolder2, IShellIconOverlay
  57. {
  58. public:
  59. // IUnknown
  60. STDMETHODIMP QueryInterface(REFIID riid, void ** ppvObj)
  61. { return CAggregatedUnknown::QueryInterface(riid, ppvObj); };
  62. STDMETHODIMP_(ULONG) AddRef(void)
  63. { return CAggregatedUnknown::AddRef(); };
  64. STDMETHODIMP_(ULONG) Release(void)
  65. { return CAggregatedUnknown::Release(); };
  66. // IShellFolder
  67. STDMETHODIMP ParseDisplayName(HWND hwnd, IBindCtx *pbc, LPOLESTR pszDisplayName,
  68. ULONG* pchEaten, LPITEMIDLIST* ppidl, ULONG* pdwAttributes);
  69. STDMETHODIMP EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList ** ppenumIDList);
  70. STDMETHODIMP BindToObject(LPCITEMIDLIST pidl, IBindCtx *pbc, REFIID riid, void** ppvOut);
  71. STDMETHODIMP BindToStorage(LPCITEMIDLIST pidl, IBindCtx *pbc, REFIID riid, void** ppvObj);
  72. STDMETHODIMP CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
  73. STDMETHODIMP CreateViewObject (HWND hwndOwner, REFIID riid, void** ppvOut);
  74. STDMETHODIMP GetAttributesOf(UINT cidl, LPCITEMIDLIST* apidl, ULONG* rgfInOut);
  75. STDMETHODIMP GetUIObjectOf(HWND hwndOwner, UINT cidl, LPCITEMIDLIST* apidl,
  76. REFIID riid, UINT* prgfInOut, void** ppvOut);
  77. STDMETHODIMP GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD uFlags, STRRET *pName);
  78. STDMETHODIMP SetNameOf(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR pszName, DWORD uFlags,
  79. LPITEMIDLIST* ppidlOut);
  80. // IShellFolder2
  81. STDMETHODIMP GetDefaultSearchGUID(GUID *pGuid);
  82. STDMETHODIMP EnumSearches(IEnumExtraSearch **ppenum);
  83. STDMETHODIMP GetDefaultColumn(DWORD dwRes, ULONG* pSort, ULONG* pDisplay);
  84. STDMETHODIMP GetDefaultColumnState(UINT iColumn, DWORD* pbState);
  85. STDMETHODIMP GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID* pscid, VARIANT* pv);
  86. STDMETHODIMP GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS* pDetails);
  87. STDMETHODIMP MapColumnToSCID(UINT iColumn, SHCOLUMNID* pscid);
  88. // IPersist
  89. STDMETHODIMP GetClassID(CLSID* pClassID);
  90. // IPersistFolder
  91. STDMETHODIMP Initialize(LPCITEMIDLIST pidl);
  92. // IPersistFolder2
  93. STDMETHODIMP GetCurFolder(LPITEMIDLIST* ppidl);
  94. // IShellIconOverlay
  95. STDMETHODIMP GetOverlayIndex(LPCITEMIDLIST pidl, int* pIndex);
  96. STDMETHODIMP GetOverlayIconIndex(LPCITEMIDLIST pidl, int* pIconIndex);
  97. STDMETHODIMP GetMaxNameLength(LPCITEMIDLIST pidlItem, UINT *pcchMax);
  98. protected:
  99. CDrivesFolder(IUnknown* punkOuter);
  100. ~CDrivesFolder();
  101. // used by the CAggregatedUnknown stuff
  102. HRESULT v_InternalQueryInterface(REFIID riid, void **ppvObj);
  103. BOOL v_HandleDelete(PLONG pcRef);
  104. STDMETHODIMP CompareItemIDs(LPCIDDRIVE pidd1, LPCIDDRIVE pidd2);
  105. static BOOL _GetFreeSpace(LPCIDDRIVE pidd, ULONGLONG *pSize, ULONGLONG *pFree);
  106. static HRESULT _OnChangeNotify(LPARAM lNotification, LPCITEMIDLIST *ppidl);
  107. static HRESULT _GetCLSIDFromPidl(LPCIDDRIVE pidd, CLSID *pclsid);
  108. static HRESULT _CheckDriveType(int iDrive, LPCTSTR pszCLSID);
  109. static HRESULT _FindExtCLSID(int iDrive, CLSID *pclsid);
  110. static HRESULT _FillIDDrive(DRIVE_IDLIST *piddl, int iDrive, BOOL fNoCLSID, IBindCtx* pbc);
  111. static LPCIDDRIVE _IsValidID(LPCITEMIDLIST pidl);
  112. static HRESULT _GetDisplayNameStrRet(LPCIDDRIVE pidd, STRRET *pStrRet);
  113. static HRESULT _GetDisplayName(LPCIDDRIVE pidd, LPTSTR pszName, UINT cchMax);
  114. static HRESULT _CreateFSFolderObj(IBindCtx *pbc, LPCITEMIDLIST pidlDrive, LPCIDDRIVE pidd, REFIID riid, void **ppv);
  115. static HRESULT _CreateFSFolder(IBindCtx *pbc, LPCITEMIDLIST pidlDrive, LPCIDDRIVE pidd, REFIID riid, void **ppv);
  116. static HRESULT _GetEditTextStrRet(LPCIDDRIVE pidd, STRRET *pstr);
  117. static BOOL _IsReg(LPCIDDRIVE pidd) { return pidd->bFlags == SHID_COMPUTER_REGITEM; }
  118. static HRESULT _GetIconOverlayInfo(LPCIDDRIVE pidd, int *pIndex, DWORD dwFlags);
  119. static CDrivesFolder* _spThis;
  120. private:
  121. friend HRESULT CDrives_CreateInstance(IUnknown* punkOuter, REFIID riid, void** ppv);
  122. friend void CDrives_Terminate(void);
  123. friend CDrivesViewCallback;
  124. friend class CDrivesFolderEnum;
  125. IUnknown* _punkReg;
  126. };
  127. #define DRIVES_EVENTS \
  128. SHCNE_DRIVEADD | \
  129. SHCNE_DRIVEREMOVED | \
  130. SHCNE_MEDIAINSERTED | \
  131. SHCNE_MEDIAREMOVED | \
  132. SHCNE_NETSHARE | \
  133. SHCNE_NETUNSHARE | \
  134. SHCNE_CREATE | \
  135. SHCNE_DELETE | \
  136. SHCNE_RENAMEITEM | \
  137. SHCNE_RENAMEFOLDER | \
  138. SHCNE_UPDATEITEM
  139. // return S_OK if non NULL CLSID copied out
  140. HRESULT CDrivesFolder::_GetCLSIDFromPidl(LPCIDDRIVE pidd, CLSID *pclsid)
  141. {
  142. *pclsid = CLSID_NULL;
  143. if ((pidd->cb >= sizeof(IDDRIVE)) &&
  144. ((pidd->wSig & IDDRIVE_ORDINAL_MASK) == IDDRIVE_ORDINAL_DRIVEEXT) &&
  145. (pidd->wSig & IDDRIVE_FLAGS_DRIVEEXT_HASCLSID))
  146. {
  147. *pclsid = pidd->clsid;
  148. return S_OK;
  149. }
  150. return S_FALSE; // does not have a CLSID
  151. }
  152. HRESULT CDrivesFolder::GetMaxNameLength(LPCITEMIDLIST pidlItem, UINT *pcchMax)
  153. {
  154. HRESULT hr = E_INVALIDARG;
  155. LPCIDDRIVE pidd = _IsValidID(pidlItem);
  156. if (pidd)
  157. {
  158. if (pidd->bFlags == SHID_COMPUTER_REGITEM)
  159. {
  160. // this is bogus, we are handling stuff for regfldr
  161. *pcchMax = MAX_REGITEMCCH;
  162. hr = S_OK;
  163. }
  164. else
  165. {
  166. CMountPoint* pMtPt = CMountPoint::GetMountPoint(DRIVEID(pidd->cName));
  167. if (pMtPt)
  168. {
  169. TCHAR szLabel[MAX_LABEL_NTFS + 1];
  170. hr = pMtPt->GetLabel(szLabel, ARRAYSIZE(szLabel));
  171. if (SUCCEEDED(hr))
  172. {
  173. if (pMtPt->IsNTFS())
  174. *pcchMax = MAX_LABEL_NTFS;
  175. else
  176. *pcchMax = MAX_LABEL_FAT;
  177. }
  178. pMtPt->Release();
  179. }
  180. }
  181. }
  182. return hr;
  183. }
  184. class CDrivesViewCallback : public CBaseShellFolderViewCB, public IFolderFilter
  185. {
  186. public:
  187. CDrivesViewCallback(CDrivesFolder *pfolder);
  188. STDMETHODIMP RealMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
  189. // IUnknown
  190. STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
  191. STDMETHODIMP_(ULONG) AddRef(void) { return CBaseShellFolderViewCB::AddRef(); };
  192. STDMETHODIMP_(ULONG) Release(void) { return CBaseShellFolderViewCB::Release(); };
  193. // IFolderFilter
  194. STDMETHODIMP ShouldShow(IShellFolder* psf, LPCITEMIDLIST pidlFolder, LPCITEMIDLIST pidlItem);
  195. STDMETHODIMP GetEnumFlags(IShellFolder* psf, LPCITEMIDLIST pidlFolder, HWND *phwnd, DWORD *pgrfFlags);
  196. private:
  197. ~CDrivesViewCallback();
  198. HRESULT OnMergeMenu(DWORD pv, QCMINFO*lP)
  199. {
  200. return S_OK;
  201. }
  202. HRESULT OnInsertItem(DWORD pv, LPCITEMIDLIST wP)
  203. {
  204. LPIDDRIVE pidd = (LPIDDRIVE)wP;
  205. if (pidd && pidd->bFlags != SHID_COMPUTER_REGITEM)
  206. {
  207. // clear the size info
  208. pidd->qwSize = pidd->qwFree = 0;
  209. }
  210. return S_OK;
  211. }
  212. HRESULT OnWindowCreated(DWORD pv, HWND wP)
  213. {
  214. InitializeStatus(_punkSite);
  215. return S_OK;
  216. }
  217. HRESULT OnSize(DWORD pv, UINT cx, UINT cy)
  218. {
  219. ResizeStatus(_punkSite, cx);
  220. return S_OK;
  221. }
  222. HRESULT OnGetPane(DWORD pv, LPARAM dwPaneID, DWORD *pdwPane)
  223. {
  224. if (PANE_ZONE == dwPaneID)
  225. *pdwPane = 2;
  226. return S_OK;
  227. }
  228. HRESULT OnDefViewMode(DWORD pv, FOLDERVIEWMODE* pfvm)
  229. {
  230. *pfvm = FVM_TILE;
  231. return S_OK;
  232. }
  233. HRESULT OnGetWebViewLayout(DWORD pv, UINT uViewMode, SFVM_WEBVIEW_LAYOUT_DATA* pData);
  234. HRESULT OnGetWebViewContent(DWORD pv, SFVM_WEBVIEW_CONTENT_DATA* pData);
  235. HRESULT OnGetWebViewTasks(DWORD pv, SFVM_WEBVIEW_TASKSECTION_DATA* pTasks);
  236. HRESULT OnUpdateStatusBar(DWORD pv, BOOL fIniting)
  237. {
  238. // Ask DefView to set the default text but not initialize
  239. // since we did the initialization in our OnSize handler.
  240. return SFVUSB_INITED;
  241. }
  242. HRESULT OnFSNotify(DWORD pv, LPCITEMIDLIST*wP, LPARAM lP)
  243. {
  244. return CDrivesFolder::_OnChangeNotify(lP, wP);
  245. }
  246. HRESULT OnBACKGROUNDENUM(DWORD pv)
  247. {
  248. return S_OK;
  249. }
  250. HRESULT OnGetCCHMax(DWORD pv, LPCITEMIDLIST pidlItem, UINT *pcchMax)
  251. {
  252. return _pfolder->GetMaxNameLength(pidlItem, pcchMax);
  253. }
  254. CDrivesFolder *_pfolder;
  255. LONG _cRef;
  256. public:
  257. // Web View Task implementations
  258. static HRESULT _CanEject(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState);
  259. static HRESULT _CanChangeSettings(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState);
  260. static HRESULT _CanSysProperties(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState);
  261. static HRESULT _CanAddRemovePrograms(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState);
  262. static HRESULT _OnSystemProperties(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc);
  263. static HRESULT _OnAddRemovePrograms(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc);
  264. static HRESULT _OnChangeSettings(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc);
  265. static HRESULT _OnEject(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc);
  266. };
  267. CDrivesViewCallback::CDrivesViewCallback(CDrivesFolder *pfolder) :
  268. CBaseShellFolderViewCB((LPCITEMIDLIST)&c_idlDrives, DRIVES_EVENTS), _pfolder(pfolder), _cRef(1)
  269. {
  270. _pfolder->AddRef();
  271. }
  272. CDrivesViewCallback::~CDrivesViewCallback()
  273. {
  274. _pfolder->Release();
  275. }
  276. STDMETHODIMP CDrivesViewCallback::RealMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
  277. {
  278. switch (uMsg)
  279. {
  280. HANDLE_MSG(0, SFVM_MERGEMENU, OnMergeMenu);
  281. HANDLE_MSG(0, SFVM_INSERTITEM, OnInsertItem);
  282. HANDLE_MSG(0, SFVM_UPDATESTATUSBAR, OnUpdateStatusBar);
  283. HANDLE_MSG(0, SFVM_FSNOTIFY, OnFSNotify);
  284. HANDLE_MSG(0, SFVM_BACKGROUNDENUM, OnBACKGROUNDENUM);
  285. HANDLE_MSG(0, SFVM_DEFVIEWMODE, OnDefViewMode);
  286. HANDLE_MSG(0, SFVM_GETWEBVIEWLAYOUT, OnGetWebViewLayout);
  287. HANDLE_MSG(0, SFVM_GETWEBVIEWCONTENT, OnGetWebViewContent);
  288. HANDLE_MSG(0, SFVM_GETWEBVIEWTASKS, OnGetWebViewTasks);
  289. HANDLE_MSG(0, SFVM_ADDPROPERTYPAGES, SFVCB_OnAddPropertyPages);
  290. HANDLE_MSG(0, SFVM_WINDOWCREATED, OnWindowCreated);
  291. HANDLE_MSG(0, SFVM_SIZE, OnSize);
  292. HANDLE_MSG(0, SFVM_GETPANE, OnGetPane);
  293. HANDLE_MSG(0, SFVM_GETCCHMAX, OnGetCCHMax);
  294. default:
  295. return E_FAIL;
  296. }
  297. return S_OK;
  298. }
  299. HRESULT CDrivesViewCallback::QueryInterface(REFIID riid, void **ppv)
  300. {
  301. HRESULT hr = CBaseShellFolderViewCB::QueryInterface(riid, ppv);
  302. if (FAILED(hr))
  303. {
  304. static const QITAB qit[] = {
  305. QITABENT(CDrivesViewCallback, IFolderFilter),
  306. { 0 },
  307. };
  308. hr = QISearch(this, qit, riid, ppv);
  309. }
  310. return hr;
  311. }
  312. STDMETHODIMP CDrivesViewCallback::ShouldShow(IShellFolder* psf, LPCITEMIDLIST pidlFolder, LPCITEMIDLIST pidlItem)
  313. {
  314. HRESULT hr = S_OK; //Assume that this item should be shown!
  315. if (SHRestricted(REST_NOMYCOMPUTERICON)) // this policy means hide my computer everywhere AND hide the contents if the user is sneaky and gets in anyway
  316. {
  317. hr = S_FALSE;
  318. }
  319. else
  320. {
  321. IShellFolder2 *psf2;
  322. if (SUCCEEDED(psf->QueryInterface(IID_PPV_ARG(IShellFolder2, &psf2))))
  323. {
  324. // Get the GUID in the pidl, which requires IShellFolder2.
  325. CLSID guidItem;
  326. if (SUCCEEDED(GetItemCLSID(psf2, pidlItem, &guidItem)))
  327. {
  328. //Convert the guid to a string
  329. TCHAR szGuidValue[MAX_GUID_STRING_LEN];
  330. SHStringFromGUID(guidItem, szGuidValue, ARRAYSIZE(szGuidValue));
  331. //See if this item is turned off in the registry.
  332. if (SHRegGetBoolUSValue(REGSTR_PATH_HIDDEN_MYCOMP_ICONS, szGuidValue, FALSE, /* default */FALSE))
  333. hr = S_FALSE; //They want to hide it; So, return S_FALSE.
  334. }
  335. psf2->Release();
  336. }
  337. }
  338. return hr;
  339. }
  340. STDMETHODIMP CDrivesViewCallback::GetEnumFlags(IShellFolder* psf, LPCITEMIDLIST pidlFolder, HWND *phwnd, DWORD *pgrfFlags)
  341. {
  342. return E_NOTIMPL;
  343. }
  344. HRESULT CDrivesViewCallback::OnGetWebViewLayout(DWORD pv, UINT uViewMode, SFVM_WEBVIEW_LAYOUT_DATA* pData)
  345. {
  346. ZeroMemory(pData, sizeof(*pData));
  347. pData->dwLayout = SFVMWVL_DETAILS;
  348. return S_OK;
  349. }
  350. HRESULT CDrivesViewCallback::_CanAddRemovePrograms(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState)
  351. {
  352. *puisState = (SHRestricted(REST_ARP_NOARP)) ? UIS_DISABLED : UIS_ENABLED;
  353. return S_OK;
  354. }
  355. // Note:
  356. // This method is NOT designed to handle multi-select cases. If you enhance
  357. // the task list and wish to multi-eject (?why?), make sure you fix this up!
  358. //
  359. HRESULT CDrivesViewCallback::_CanEject(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState)
  360. {
  361. *puisState = UIS_DISABLED;
  362. IDataObject *pdo;
  363. // should just use the ShellItemArray directly
  364. if (psiItemArray && SUCCEEDED(psiItemArray->BindToHandler(NULL,BHID_DataObject,IID_PPV_ARG(IDataObject,&pdo))))
  365. {
  366. STGMEDIUM medium;
  367. LPIDA pida = DataObj_GetHIDA(pdo, &medium);
  368. if (pida)
  369. {
  370. ASSERT(pida->cidl == 1); // Only allow eject if a single item is selected.
  371. LPCIDDRIVE pidd = CDrivesFolder::_IsValidID(IDA_GetIDListPtr(pida, 0));
  372. if (pidd)
  373. {
  374. CMountPoint* pmtpt = CMountPoint::GetMountPoint(DRIVEID(pidd->cName));
  375. if (pmtpt)
  376. {
  377. if (pmtpt->IsEjectable())
  378. *puisState = UIS_ENABLED;
  379. pmtpt->Release();
  380. }
  381. }
  382. HIDA_ReleaseStgMedium(pida, &medium);
  383. }
  384. pdo->Release();
  385. }
  386. return S_OK;
  387. }
  388. HRESULT CDrivesViewCallback::_CanSysProperties(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState)
  389. {
  390. *puisState = SHRestricted(REST_MYCOMPNOPROP) ? UIS_DISABLED : UIS_ENABLED;
  391. return S_OK;
  392. }
  393. HRESULT CDrivesViewCallback::_OnSystemProperties(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc)
  394. {
  395. CDrivesViewCallback* pThis = (CDrivesViewCallback*)(void*)pv;
  396. return SHInvokeCommandOnPidl(pThis->_hwndMain, NULL, pThis->_pidl, 0, "properties");
  397. }
  398. HRESULT CDrivesViewCallback::_OnAddRemovePrograms(IUnknown* pv,IShellItemArray *psiItemArray, IBindCtx *pbc)
  399. {
  400. BOOL fRet = SHRunControlPanel(L"appwiz.cpl", NULL);
  401. return (fRet) ? S_OK : E_FAIL;
  402. }
  403. HRESULT CDrivesViewCallback::_CanChangeSettings(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState)
  404. {
  405. *puisState = SHRestricted(REST_NOCONTROLPANEL) ? UIS_DISABLED : UIS_ENABLED;
  406. return S_OK;
  407. }
  408. HRESULT CDrivesViewCallback::_OnChangeSettings(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc)
  409. {
  410. CDrivesViewCallback* pThis = (CDrivesViewCallback*)(void*)pv;
  411. IShellBrowser* psb;
  412. HRESULT hr = IUnknown_QueryService(pThis->_punkSite, SID_STopLevelBrowser, IID_PPV_ARG(IShellBrowser, &psb));
  413. if (SUCCEEDED(hr))
  414. {
  415. LPITEMIDLIST pidl;
  416. hr = SHGetFolderLocation(NULL, CSIDL_CONTROLS, NULL, 0, &pidl);
  417. if (SUCCEEDED(hr))
  418. {
  419. hr = psb->BrowseObject(pidl, 0);
  420. ILFree(pidl);
  421. }
  422. psb->Release();
  423. }
  424. return hr;
  425. }
  426. HRESULT CDrivesViewCallback::_OnEject(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc)
  427. {
  428. CDrivesViewCallback* pThis = (CDrivesViewCallback*)(void*)pv;
  429. IDataObject *pdo;
  430. HRESULT hr = E_FAIL;
  431. if (psiItemArray && SUCCEEDED(psiItemArray->BindToHandler(NULL,BHID_DataObject,IID_PPV_ARG(IDataObject,&pdo))))
  432. {
  433. hr = SHInvokeCommandOnDataObject(pThis->_hwndMain, NULL, pdo, 0, "eject");
  434. pdo->Release();
  435. }
  436. return hr;
  437. }
  438. const WVTASKITEM c_MyComputerTaskHeader = WVTI_HEADER(L"shell32.dll", IDS_HEADER_MYCOMPUTER, IDS_HEADER_MYCOMPUTER_TT);
  439. const WVTASKITEM c_MyComputerTaskList[] =
  440. {
  441. WVTI_ENTRY_ALL(CLSID_NULL, L"shell32.dll", IDS_TASK_MYCOMPUTER_SYSTEMPROPERTIES, IDS_TASK_MYCOMPUTER_SYSTEMPROPERTIES_TT, IDI_TASK_PROPERTIES,CDrivesViewCallback::_CanSysProperties, CDrivesViewCallback::_OnSystemProperties),
  442. WVTI_ENTRY_ALL(UICID_AddRemovePrograms, L"shell32.dll", IDS_TASK_ARP, IDS_TASK_ARP_TT, IDI_CPCAT_ARP, CDrivesViewCallback::_CanAddRemovePrograms, CDrivesViewCallback::_OnAddRemovePrograms),
  443. WVTI_ENTRY_ALL(CLSID_NULL, L"shell32.dll", IDS_TASK_CHANGESETTINGS, IDS_TASK_CHANGESETTINGS_TT, IDI_CPLFLD, CDrivesViewCallback::_CanChangeSettings,CDrivesViewCallback::_OnChangeSettings),
  444. WVTI_ENTRY_TITLE(CLSID_NULL, L"shell32.dll", 0, IDS_TASK_EJECTDISK, 0, IDS_TASK_EJECTDISK_TT, IDI_STEJECT, CDrivesViewCallback::_CanEject, CDrivesViewCallback::_OnEject),
  445. };
  446. HRESULT CDrivesViewCallback::OnGetWebViewContent(DWORD pv, SFVM_WEBVIEW_CONTENT_DATA* pData)
  447. {
  448. ZeroMemory(pData, sizeof(*pData));
  449. Create_IUIElement(&c_MyComputerTaskHeader, &(pData->pFolderTaskHeader));
  450. // My Computer wants a different order than the default,
  451. // and it doesn't want to expose "Desktop" as a place to go
  452. LPCTSTR rgCSIDLs[] = { MAKEINTRESOURCE(CSIDL_NETWORK), MAKEINTRESOURCE(CSIDL_PERSONAL), MAKEINTRESOURCE(CSIDL_COMMON_DOCUMENTS), MAKEINTRESOURCE(CSIDL_CONTROLS) };
  453. CreateIEnumIDListOnCSIDLs(NULL, rgCSIDLs, ARRAYSIZE(rgCSIDLs), &(pData->penumOtherPlaces));
  454. return S_OK;
  455. }
  456. HRESULT CDrivesViewCallback::OnGetWebViewTasks(DWORD pv, SFVM_WEBVIEW_TASKSECTION_DATA* pTasks)
  457. {
  458. ZeroMemory(pTasks, sizeof(*pTasks));
  459. Create_IEnumUICommand((IUnknown*)(void*)this, c_MyComputerTaskList, ARRAYSIZE(c_MyComputerTaskList), &pTasks->penumFolderTasks);
  460. return S_OK;
  461. }
  462. STDAPI_(IShellFolderViewCB*) CDrives_CreateSFVCB(CDrivesFolder *pfolder)
  463. {
  464. return new CDrivesViewCallback(pfolder);
  465. }
  466. typedef struct
  467. {
  468. DWORD dwDrivesMask;
  469. int nLastFoundDrive;
  470. DWORD dwRestricted;
  471. DWORD dwSavedErrorMode;
  472. DWORD grfFlags;
  473. } EnumDrives;
  474. typedef enum
  475. {
  476. DRIVES_ICOL_NAME = 0,
  477. DRIVES_ICOL_TYPE,
  478. DRIVES_ICOL_CAPACITY,
  479. DRIVES_ICOL_FREE,
  480. DRIVES_ICOL_FILESYSTEM,
  481. DRIVES_ICOL_COMMENT,
  482. };
  483. const COLUMN_INFO c_drives_cols[] =
  484. {
  485. DEFINE_COL_STR_ENTRY(SCID_NAME, 20, IDS_NAME_COL),
  486. DEFINE_COL_STR_ENTRY(SCID_TYPE, 25, IDS_TYPE_COL),
  487. DEFINE_COL_SIZE_ENTRY(SCID_CAPACITY, IDS_DRIVES_CAPACITY),
  488. DEFINE_COL_SIZE_ENTRY(SCID_FREESPACE, IDS_DRIVES_FREE),
  489. DEFINE_COL_STR_MENU_ENTRY(SCID_FILESYSTEM, 15, IDS_DRIVES_FILESYSTEM),
  490. DEFINE_COL_STR_ENTRY(SCID_Comment, 20, IDS_EXCOL_COMMENT),
  491. };
  492. CDrivesFolder* CDrivesFolder::_spThis = NULL;
  493. HRESULT CDrives_CreateInstance(IUnknown* punkOuter, REFIID riid, void** ppv)
  494. {
  495. HRESULT hr;
  496. ASSERT(NULL != ppv);
  497. ENTERCRITICAL;
  498. if (NULL != CDrivesFolder::_spThis)
  499. {
  500. hr = CDrivesFolder::_spThis->QueryInterface(riid, ppv);
  501. LEAVECRITICAL;
  502. }
  503. else
  504. {
  505. LEAVECRITICAL;
  506. CDrivesFolder* pDF = new CDrivesFolder(punkOuter);
  507. if (NULL != pDF)
  508. {
  509. ASSERT(NULL == pDF->_punkReg);
  510. if (SHRestricted(REST_NOCONTROLPANEL) || SHRestricted(REST_NOSETFOLDERS))
  511. g_asDrivesReqItems[CDRIVES_REGITEM_CONTROL].dwAttributes |= SFGAO_NONENUMERATED;
  512. REGITEMSINFO sDrivesRegInfo =
  513. {
  514. REGSTR_PATH_EXPLORER TEXT("\\MyComputer\\NameSpace"),
  515. NULL,
  516. TEXT(':'),
  517. SHID_COMPUTER_REGITEM,
  518. -1,
  519. SFGAO_CANLINK,
  520. ARRAYSIZE(g_asDrivesReqItems),
  521. g_asDrivesReqItems,
  522. RIISA_ORIGINAL,
  523. NULL,
  524. 0,
  525. 0,
  526. };
  527. CRegFolder_CreateInstance(&sDrivesRegInfo,
  528. (IUnknown*)(IShellFolder2*) pDF,
  529. IID_PPV_ARG(IUnknown, &pDF->_punkReg));
  530. if (SHInterlockedCompareExchange((void**) &CDrivesFolder::_spThis, pDF, NULL))
  531. {
  532. // Someone else snuck in and initialized a CDrivesFolder first,
  533. // so release our object and then recurse so we should get the other instance
  534. pDF->Release();
  535. hr = CDrives_CreateInstance(punkOuter, riid, ppv);
  536. }
  537. else
  538. {
  539. hr = pDF->QueryInterface(riid, ppv);
  540. // release the self-reference, but keep _spThis intact
  541. // (it will be reset to NULL in the destructor)
  542. pDF->Release();
  543. }
  544. }
  545. else
  546. {
  547. hr = E_OUTOFMEMORY;
  548. *ppv = NULL;
  549. }
  550. }
  551. return hr;
  552. }
  553. // This should only be called during process detach
  554. void CDrives_Terminate(void)
  555. {
  556. if (NULL != CDrivesFolder::_spThis)
  557. {
  558. delete CDrivesFolder::_spThis;
  559. }
  560. }
  561. CDrivesFolder::CDrivesFolder(IUnknown* punkOuter) :
  562. CAggregatedUnknown (punkOuter),
  563. _punkReg (NULL)
  564. {
  565. DllAddRef();
  566. }
  567. CDrivesFolder::~CDrivesFolder()
  568. {
  569. SHReleaseInnerInterface(SAFECAST(this, IShellFolder *), &_punkReg);
  570. SHInterlockedCompareExchange((void**) &CDrivesFolder::_spThis, NULL, this);
  571. DllRelease();
  572. }
  573. HRESULT CDrivesFolder::v_InternalQueryInterface(REFIID riid, void** ppv)
  574. {
  575. static const QITAB qit[] = {
  576. QITABENT(CDrivesFolder, IShellFolder2), // IID_IShellFolder2
  577. QITABENTMULTI(CDrivesFolder, IShellFolder, IShellFolder2), // IID_IShellFolder
  578. QITABENT(CDrivesFolder, IPersistFolder2), // IID_IPersistFolder2
  579. QITABENTMULTI(CDrivesFolder, IPersistFolder, IPersistFolder2), // IID_IPersistFolder
  580. QITABENTMULTI(CDrivesFolder, IPersist, IPersistFolder2), // IID_IPersist
  581. QITABENTMULTI2(CDrivesFolder, IID_IPersistFreeThreadedObject, IPersist), // IID_IPersistFreeThreadedObject
  582. QITABENT(CDrivesFolder, IShellIconOverlay), // IID_IShellIconOverlay
  583. { 0 },
  584. };
  585. HRESULT hr;
  586. if (_punkReg && RegGetsFirstShot(riid))
  587. {
  588. hr = _punkReg->QueryInterface(riid, ppv);
  589. }
  590. else
  591. {
  592. hr = QISearch(this, qit, riid, ppv);
  593. if ((E_NOINTERFACE == hr) && _punkReg)
  594. {
  595. hr = _punkReg->QueryInterface(riid, ppv);
  596. }
  597. }
  598. return hr;
  599. }
  600. BOOL CDrivesFolder::v_HandleDelete(PLONG pcRef)
  601. {
  602. ASSERT(NULL != pcRef);
  603. ENTERCRITICAL;
  604. //
  605. // The same bad thing can happen here as in
  606. // CNetRootFolder::v_HandleDelete. See that function for gory details.
  607. //
  608. if (this == _spThis && 0 == *pcRef)
  609. {
  610. *pcRef = 1000; // protect against cached pointers bumping us up then down
  611. delete this;
  612. }
  613. LEAVECRITICAL;
  614. // return TRUE to indicate that we've implemented this function
  615. // (regardless of whether or not this object was actually deleted)
  616. return TRUE;
  617. }
  618. HRESULT CDrivesFolder::_GetDisplayName(LPCIDDRIVE pidd, LPTSTR pszName, UINT cchMax)
  619. {
  620. HRESULT hr = E_FAIL;
  621. CMountPoint* pMtPt = CMountPoint::GetMountPoint(DRIVEID(pidd->cName));
  622. if (pMtPt)
  623. {
  624. hr = pMtPt->GetDisplayName(pszName, cchMax);
  625. pMtPt->Release();
  626. }
  627. return hr;
  628. }
  629. HRESULT CDrivesFolder::_GetDisplayNameStrRet(LPCIDDRIVE pidd, STRRET *pStrRet)
  630. {
  631. HRESULT hr = E_FAIL;
  632. CMountPoint* pMtPt = CMountPoint::GetMountPoint(DRIVEID(pidd->cName));
  633. if (pMtPt)
  634. {
  635. TCHAR szName[MAX_DISPLAYNAME];
  636. hr = pMtPt->GetDisplayName(szName, ARRAYSIZE(szName));
  637. if (SUCCEEDED(hr))
  638. hr = StringToStrRet(szName, pStrRet);
  639. pMtPt->Release();
  640. }
  641. return hr;
  642. }
  643. #define REGKEY_DRIVE_FOLDEREXT L"Drive\\shellex\\FolderExtensions"
  644. HRESULT CDrivesFolder::_CheckDriveType(int iDrive, LPCTSTR pszCLSID)
  645. {
  646. HRESULT hr = E_FAIL;
  647. TCHAR szKey[MAX_PATH];
  648. StrCpyN(szKey, REGKEY_DRIVE_FOLDEREXT L"\\", ARRAYSIZE(szKey));
  649. StrCatBuff(szKey, pszCLSID, ARRAYSIZE(szKey));
  650. DWORD dwDriveMask;
  651. DWORD cb = sizeof(DWORD);
  652. if (ERROR_SUCCESS == SHGetValue(HKEY_CLASSES_ROOT, szKey, L"DriveMask", NULL, &dwDriveMask, &cb))
  653. {
  654. TCHAR szDrive[4];
  655. if (PathBuildRoot(szDrive, iDrive))
  656. {
  657. int iType = GetDriveType(szDrive);
  658. // its possible that we're asked to parse a drive that's no longer mounted,
  659. // so GetDriveType will fail with DRIVE_NO_ROOT_DIR.
  660. // in that case, pass it on down to the handler anyway.
  661. // let's say it's the handler's job to remember the last drive it matched on.
  662. if ((DRIVE_NO_ROOT_DIR == iType) || ((1 << iType) & dwDriveMask))
  663. {
  664. hr = S_OK;
  665. }
  666. }
  667. }
  668. return hr;
  669. }
  670. HRESULT CDrivesFolder::_FindExtCLSID(int iDrive, CLSID *pclsid)
  671. {
  672. *pclsid = CLSID_NULL;
  673. HRESULT hr = E_FAIL;
  674. HKEY hk;
  675. if (ERROR_SUCCESS == RegOpenKey(HKEY_CLASSES_ROOT, REGKEY_DRIVE_FOLDEREXT, &hk))
  676. {
  677. TCHAR szCLSID[MAX_GUID_STRING_LEN];
  678. for (int i = 0; FAILED(hr) && (ERROR_SUCCESS == RegEnumKey(hk, i, szCLSID, ARRAYSIZE(szCLSID))); i++)
  679. {
  680. IDriveFolderExt *pdfe;
  681. if (SUCCEEDED(_CheckDriveType(iDrive, szCLSID)) &&
  682. SUCCEEDED(SHExtCoCreateInstance(szCLSID, NULL, NULL, IID_PPV_ARG(IDriveFolderExt, &pdfe))))
  683. {
  684. if (SUCCEEDED(pdfe->DriveMatches(iDrive)))
  685. {
  686. SHCLSIDFromString(szCLSID, pclsid);
  687. }
  688. pdfe->Release();
  689. }
  690. // if we successfully matched one, break out.
  691. if (!IsEqualCLSID(*pclsid, CLSID_NULL))
  692. hr = S_OK;
  693. }
  694. RegCloseKey(hk);
  695. }
  696. return hr;
  697. }
  698. // this is called from parse and enum, both times passing a stack var into piddl.
  699. // we reset the cb manually and then the callers will do ILClone to allocate the correct amount
  700. // of memory.
  701. HRESULT CDrivesFolder::_FillIDDrive(DRIVE_IDLIST *piddl, int iDrive, BOOL fNoCLSID, IBindCtx* pbc)
  702. {
  703. HRESULT hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
  704. BOOL fDoIt = FALSE;
  705. ZeroMemory(piddl, sizeof(*piddl));
  706. PathBuildRootA(piddl->idd.cName, iDrive);
  707. if (S_OK == SHIsFileSysBindCtx(pbc, NULL))
  708. {
  709. fDoIt = TRUE;
  710. }
  711. else
  712. {
  713. if (BindCtx_GetMode(pbc, 0) & STGM_CREATE)
  714. {
  715. fDoIt = TRUE;
  716. }
  717. else
  718. {
  719. CMountPoint* pmtpt = CMountPoint::GetMountPoint(iDrive, FALSE);
  720. if (pmtpt)
  721. {
  722. fDoIt = TRUE;
  723. pmtpt->Release();
  724. }
  725. }
  726. }
  727. if (fDoIt)
  728. {
  729. // start the cb as the IDDRIVE less the clsid at the end
  730. // this is so that in the usual case when we dont have a clsid, the pidl will look
  731. // just like all our pidls on win2k.
  732. piddl->idd.cb = FIELD_OFFSET(IDDRIVE, clsid);
  733. piddl->idd.bFlags = SHID_COMPUTER_MISC;
  734. if (!fNoCLSID)
  735. {
  736. CLSID clsid;
  737. if (SUCCEEDED(_FindExtCLSID(iDrive, &clsid)))
  738. {
  739. piddl->idd.clsid = clsid;
  740. // boost the cb to include the whole thing
  741. piddl->idd.cb = sizeof(IDDRIVE);
  742. // mark the flags of the pidl to say "hey im a drive extension with a clsid"
  743. piddl->idd.wSig = IDDRIVE_ORDINAL_DRIVEEXT | IDDRIVE_FLAGS_DRIVEEXT_HASCLSID;
  744. }
  745. }
  746. hr = S_OK;
  747. }
  748. ASSERT(piddl->cbNext == 0);
  749. return hr;
  750. }
  751. STDMETHODIMP CDrivesFolder::ParseDisplayName(HWND hwnd, IBindCtx *pbc, LPOLESTR pwzDisplayName,
  752. ULONG* pchEaten, LPITEMIDLIST* ppidlOut, ULONG* pdwAttributes)
  753. {
  754. HRESULT hr = E_INVALIDARG;
  755. if (ppidlOut)
  756. {
  757. *ppidlOut = NULL; // assume error
  758. if (pwzDisplayName && pwzDisplayName[0] &&
  759. pwzDisplayName[1] == TEXT(':') && pwzDisplayName[2] == TEXT('\\'))
  760. {
  761. DRIVE_IDLIST idlDrive;
  762. if (InRange(*pwzDisplayName, 'a', 'z') ||
  763. InRange(*pwzDisplayName, 'A', 'Z'))
  764. {
  765. hr = _FillIDDrive(&idlDrive, DRIVEID(pwzDisplayName), SHSkipJunctionBinding(pbc, NULL), pbc);
  766. }
  767. if (SUCCEEDED(hr))
  768. {
  769. // Check if there are any subdirs
  770. if (pwzDisplayName[3])
  771. {
  772. IShellFolder *psfDrive;
  773. hr = BindToObject((LPITEMIDLIST)&idlDrive, pbc, IID_PPV_ARG(IShellFolder, &psfDrive));
  774. if (SUCCEEDED(hr))
  775. {
  776. ULONG chEaten;
  777. LPITEMIDLIST pidlDir;
  778. hr = psfDrive->ParseDisplayName(hwnd, pbc, pwzDisplayName + 3,
  779. &chEaten, &pidlDir, pdwAttributes);
  780. if (SUCCEEDED(hr))
  781. {
  782. hr = SHILCombine((LPCITEMIDLIST)&idlDrive, pidlDir, ppidlOut);
  783. SHFree(pidlDir);
  784. }
  785. psfDrive->Release();
  786. }
  787. }
  788. else
  789. {
  790. hr = SHILClone((LPITEMIDLIST)&idlDrive, ppidlOut);
  791. if (pdwAttributes && *pdwAttributes)
  792. GetAttributesOf(1, (LPCITEMIDLIST *)ppidlOut, pdwAttributes);
  793. }
  794. }
  795. }
  796. #if 0
  797. else if (0 == StrCmpNI(TEXT("\\\\?\\Volume{"), pwzDisplayName, ARRAYSIZE(TEXT("\\\\?\\Volume{"))))
  798. {
  799. //check if dealing with mounteddrive
  800. //something like: "\\?\Volume{9e2df3f5-c7f1-11d1-84d5-000000000000}\" (without quotes)
  801. }
  802. #endif
  803. }
  804. if (FAILED(hr))
  805. TraceMsg(TF_WARNING, "CDrivesFolder::ParseDisplayName(), hr:%x %ls", hr, pwzDisplayName);
  806. return hr;
  807. }
  808. BOOL IsShareable(int iDrive)
  809. {
  810. return !IsRemoteDrive(iDrive);
  811. }
  812. class CDrivesFolderEnum : public CEnumIDListBase
  813. {
  814. public:
  815. // IEnumIDList
  816. STDMETHOD(Next)(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched);
  817. private:
  818. CDrivesFolderEnum(CDrivesFolder *psf, DWORD grfFlags);
  819. ~CDrivesFolderEnum();
  820. friend HRESULT Create_DrivesFolderEnum(CDrivesFolder* psf, DWORD grfFlags, IEnumIDList** ppenum);
  821. CDrivesFolder *_pdsf; // CDrivesFolder object we're enumerating
  822. DWORD _dwDrivesMask;
  823. int _nLastFoundDrive;
  824. DWORD _dwRestricted;
  825. DWORD _dwSavedErrorMode;
  826. DWORD _grfFlags;
  827. };
  828. CDrivesFolderEnum::CDrivesFolderEnum(CDrivesFolder *pdsf, DWORD grfFlags) : CEnumIDListBase()
  829. {
  830. _pdsf = pdsf;
  831. _pdsf->AddRef();
  832. _dwDrivesMask = CMountPoint::GetDrivesMask();
  833. _nLastFoundDrive = -1;
  834. _dwRestricted = SHRestricted(REST_NODRIVES);
  835. _dwSavedErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
  836. _grfFlags = grfFlags;
  837. }
  838. HRESULT Create_DrivesFolderEnum(CDrivesFolder *psf, DWORD grfFlags, IEnumIDList** ppenum)
  839. {
  840. HRESULT hr;
  841. CDrivesFolderEnum* p= new CDrivesFolderEnum(psf, grfFlags);
  842. if (p)
  843. {
  844. hr = p->QueryInterface(IID_PPV_ARG(IEnumIDList, ppenum));
  845. p->Release();
  846. }
  847. else
  848. {
  849. hr = E_OUTOFMEMORY;
  850. *ppenum = NULL;
  851. }
  852. return hr;
  853. }
  854. CDrivesFolderEnum::~CDrivesFolderEnum()
  855. {
  856. _pdsf->Release(); // release the "this" ptr we have
  857. }
  858. STDMETHODIMP CDrivesFolderEnum::Next(ULONG celt, LPITEMIDLIST *ppidl, ULONG *pceltFetched)
  859. {
  860. HRESULT hr = S_FALSE; // assume "no more element"
  861. LPITEMIDLIST pidl = NULL;
  862. for (int iDrive = _nLastFoundDrive + 1; iDrive < 26; iDrive++)
  863. {
  864. if (_dwRestricted & (1 << iDrive))
  865. {
  866. TraceMsg(DM_TRACE, "s.cd_ecb: Drive %d restricted.", iDrive);
  867. }
  868. else if ((_dwDrivesMask & (1 << iDrive)) || IsUnavailableNetDrive(iDrive))
  869. {
  870. if (!(SHCONTF_SHAREABLE & _grfFlags) || IsShareable(iDrive))
  871. {
  872. DRIVE_IDLIST iddrive;
  873. hr = _pdsf->_FillIDDrive(&iddrive, iDrive, FALSE, NULL);
  874. if (SUCCEEDED(hr))
  875. {
  876. hr = SHILClone((LPITEMIDLIST)&iddrive, &pidl);
  877. if (SUCCEEDED(hr))
  878. {
  879. CMountPoint* pmtpt = CMountPoint::GetMountPoint(iDrive, FALSE);
  880. if (pmtpt)
  881. {
  882. pmtpt->ChangeNotifyRegisterAlias();
  883. pmtpt->Release();
  884. }
  885. _nLastFoundDrive = iDrive;
  886. }
  887. break;
  888. }
  889. else
  890. {
  891. hr = S_FALSE;
  892. }
  893. }
  894. }
  895. }
  896. *ppidl = pidl;
  897. if (pceltFetched)
  898. *pceltFetched = (S_OK == hr) ? 1 : 0;
  899. return hr;
  900. }
  901. STDMETHODIMP CDrivesFolder::EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList ** ppenum)
  902. {
  903. return Create_DrivesFolderEnum(this, grfFlags, ppenum);
  904. }
  905. LPCIDDRIVE CDrivesFolder::_IsValidID(LPCITEMIDLIST pidl)
  906. {
  907. if (pidl && (SIL_GetType(pidl) & SHID_GROUPMASK) == SHID_COMPUTER)
  908. return (LPCIDDRIVE)pidl;
  909. return NULL;
  910. }
  911. HRESULT CDrivesFolder::_CreateFSFolderObj(IBindCtx *pbc, LPCITEMIDLIST pidlDrive, LPCIDDRIVE pidd, REFIID riid, void **ppv)
  912. {
  913. PERSIST_FOLDER_TARGET_INFO pfti = {0};
  914. pfti.pidlTargetFolder = (LPITEMIDLIST)pidlDrive;
  915. SHAnsiToUnicode(pidd->cName, pfti.szTargetParsingName, ARRAYSIZE(pfti.szTargetParsingName));
  916. pfti.dwAttributes = FILE_ATTRIBUTE_DIRECTORY; // maybe add system?
  917. pfti.csidl = -1;
  918. return CFSFolder_CreateFolder(NULL, pbc, pidlDrive, &pfti, riid, ppv);
  919. }
  920. HRESULT CDrivesFolder::_CreateFSFolder(IBindCtx *pbc, LPCITEMIDLIST pidlDrive, LPCIDDRIVE pidd, REFIID riid, void **ppv)
  921. {
  922. HRESULT hr;
  923. CLSID clsid;
  924. if (S_OK == _GetCLSIDFromPidl(pidd, &clsid) && (!SHSkipJunctionBinding(pbc, NULL)))
  925. {
  926. IDriveFolderExt *pdfe;
  927. // SHExtCoCreateInstance since this shell extension needs to go through approval
  928. hr = SHExtCoCreateInstance(NULL, &clsid, NULL, IID_PPV_ARG(IDriveFolderExt, &pdfe));
  929. if (SUCCEEDED(hr))
  930. {
  931. hr = pdfe->Bind(pidlDrive, pbc, riid, ppv);
  932. pdfe->Release();
  933. }
  934. }
  935. else
  936. {
  937. hr = _CreateFSFolderObj(pbc, pidlDrive, pidd, riid, ppv);
  938. }
  939. return hr;
  940. }
  941. STDMETHODIMP CDrivesFolder::BindToObject(LPCITEMIDLIST pidl, IBindCtx *pbc, REFIID riid, void** ppv)
  942. {
  943. HRESULT hr;
  944. *ppv = NULL;
  945. LPCIDDRIVE pidd = _IsValidID(pidl);
  946. if (pidd)
  947. {
  948. LPCITEMIDLIST pidlNext = _ILNext(pidl);
  949. LPITEMIDLIST pidlDrive = ILCombineParentAndFirst(IDLIST_DRIVES, pidl, pidlNext);
  950. if (pidlDrive)
  951. {
  952. // we only try ask for the riid at the end of the pidl binding.
  953. if (ILIsEmpty(pidlNext))
  954. {
  955. hr = _CreateFSFolder(pbc, pidlDrive, pidd, riid, ppv);
  956. }
  957. else
  958. {
  959. // now we need to get the subfolder from which to grab our goodies
  960. IShellFolder *psfDrive;
  961. hr = _CreateFSFolder(pbc, pidlDrive, pidd, IID_PPV_ARG(IShellFolder, &psfDrive));
  962. if (SUCCEEDED(hr))
  963. {
  964. // this means that there is more to bind to, we must pass it on...
  965. hr = psfDrive->BindToObject(pidlNext, pbc, riid, ppv);
  966. psfDrive->Release();
  967. }
  968. }
  969. ILFree(pidlDrive);
  970. }
  971. else
  972. hr = E_OUTOFMEMORY;
  973. }
  974. else
  975. {
  976. hr = E_INVALIDARG;
  977. TraceMsg(TF_WARNING, "CDrivesFolder::BindToObject(), bad PIDL %s", DumpPidl(pidl));
  978. }
  979. return hr;
  980. }
  981. STDMETHODIMP CDrivesFolder::BindToStorage(LPCITEMIDLIST pidl, IBindCtx *pbc, REFIID riid, void** ppv)
  982. {
  983. return BindToObject(pidl, pbc, riid, ppv);
  984. }
  985. BOOL CDrivesFolder::_GetFreeSpace(LPCIDDRIVE pidd, ULONGLONG *pSize, ULONGLONG *pFree)
  986. {
  987. BOOL bRet = FALSE;
  988. CLSID clsid;
  989. if (S_OK == _GetCLSIDFromPidl(pidd, &clsid))
  990. {
  991. IDriveFolderExt *pdfe;
  992. // SHExtCoCreateInstance since this shell extension needs to go through approval
  993. if (SUCCEEDED(SHExtCoCreateInstance(NULL, &clsid, NULL, IID_PPV_ARG(IDriveFolderExt, &pdfe))))
  994. {
  995. bRet = SUCCEEDED(pdfe->GetSpace(pSize, pFree));
  996. pdfe->Release();
  997. }
  998. }
  999. if (!bRet && !_IsReg(pidd) && ShowDriveInfo(DRIVEID(pidd->cName)))
  1000. {
  1001. if (pidd->qwSize || pidd->qwFree)
  1002. {
  1003. *pSize = pidd->qwSize; // cache hit
  1004. *pFree = pidd->qwFree;
  1005. bRet = TRUE;
  1006. }
  1007. else
  1008. {
  1009. int iDrive = DRIVEID(pidd->cName);
  1010. // Don't wake up sleeping net connections
  1011. if (!IsRemoteDrive(iDrive) || !IsDisconnectedNetDrive(iDrive))
  1012. {
  1013. // Call our helper function Who understands
  1014. // OSR2 and NT as well as old W95...
  1015. ULARGE_INTEGER qwFreeUser, qwTotal, qwTotalFree;
  1016. bRet = SHGetDiskFreeSpaceExA(pidd->cName, &qwFreeUser, &qwTotal, &qwTotalFree);
  1017. if (bRet)
  1018. {
  1019. *pSize = qwTotal.QuadPart;
  1020. *pFree = qwFreeUser.QuadPart;
  1021. }
  1022. }
  1023. }
  1024. }
  1025. return bRet;
  1026. }
  1027. STDMETHODIMP CDrivesFolder::CompareIDs(LPARAM iCol, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  1028. {
  1029. LPCIDDRIVE pidd1 = _IsValidID(pidl1);
  1030. LPCIDDRIVE pidd2 = _IsValidID(pidl2);
  1031. if (!pidd1 || !pidd2)
  1032. {
  1033. TraceMsg(TF_WARNING, "CDrivesFolder::CompareIDs(), bad(s) pidl11:%s, pidl2:%s", DumpPidl(pidl1), DumpPidl(pidl2));
  1034. return E_INVALIDARG;
  1035. }
  1036. // For any column other than DRIVES_ICOL_NAME, we force an
  1037. // all-fields comparison to break ties.
  1038. if ((iCol & SHCIDS_COLUMNMASK) != DRIVES_ICOL_NAME)
  1039. iCol |= SHCIDS_ALLFIELDS;
  1040. HRESULT hr;
  1041. switch (iCol & SHCIDS_COLUMNMASK)
  1042. {
  1043. default: // If asking for unknown column, just use name
  1044. case DRIVES_ICOL_NAME:
  1045. hr = ResultFromShort(StrCmpICA(pidd1->cName, pidd2->cName));
  1046. break;
  1047. case DRIVES_ICOL_TYPE:
  1048. {
  1049. TCHAR szName1[80], szName2[80];
  1050. if (SHID_COMPUTER_REGITEM != pidd1->bFlags)
  1051. {
  1052. CMountPoint::GetTypeString(DRIVEID(pidd1->cName), szName1, ARRAYSIZE(szName1));
  1053. }
  1054. else
  1055. {
  1056. LoadString(HINST_THISDLL, IDS_DRIVES_REGITEM, szName1, ARRAYSIZE(szName1));
  1057. }
  1058. if (SHID_COMPUTER_REGITEM != pidd1->bFlags)
  1059. {
  1060. CMountPoint::GetTypeString(DRIVEID(pidd2->cName), szName2, ARRAYSIZE(szName2));
  1061. }
  1062. else
  1063. {
  1064. LoadString(HINST_THISDLL, IDS_DRIVES_REGITEM, szName2, ARRAYSIZE(szName2));
  1065. }
  1066. hr = ResultFromShort(ustrcmpi(szName1, szName2));
  1067. break;
  1068. }
  1069. case DRIVES_ICOL_CAPACITY:
  1070. case DRIVES_ICOL_FREE:
  1071. {
  1072. ULONGLONG qwSize1, qwFree1;
  1073. ULONGLONG qwSize2, qwFree2;
  1074. BOOL fGotInfo1 = _GetFreeSpace(pidd1, &qwSize1, &qwFree1);
  1075. BOOL fGotInfo2 = _GetFreeSpace(pidd2, &qwSize2, &qwFree2);
  1076. if (fGotInfo1 && fGotInfo2)
  1077. {
  1078. ULONGLONG i1, i2; // this is a "guess" at the disk size and free space
  1079. if ((iCol & SHCIDS_COLUMNMASK) == DRIVES_ICOL_CAPACITY)
  1080. {
  1081. i1 = qwSize1;
  1082. i2 = qwSize2;
  1083. }
  1084. else
  1085. {
  1086. i1 = qwFree1;
  1087. i2 = qwFree2;
  1088. }
  1089. if (i1 == i2)
  1090. hr = ResultFromShort(0);
  1091. else if (i1 < i2)
  1092. hr = ResultFromShort(-1);
  1093. else
  1094. hr = ResultFromShort(1);
  1095. }
  1096. else if (!fGotInfo1 && !fGotInfo2)
  1097. {
  1098. hr = ResultFromShort(0);
  1099. }
  1100. else
  1101. {
  1102. hr = ResultFromShort(fGotInfo1 - fGotInfo2);
  1103. }
  1104. break;
  1105. }
  1106. }
  1107. if (0 == HRESULT_CODE(hr))
  1108. {
  1109. // check if clsids are equivalent, if they're different then we're done.
  1110. // duh... this should be checked AFTER the other checks so sort order is preserved.
  1111. CLSID clsid1, clsid2;
  1112. _GetCLSIDFromPidl(pidd1, &clsid1);
  1113. _GetCLSIDFromPidl(pidd2, &clsid2);
  1114. hr = ResultFromShort(memcmp(&clsid1, &clsid2, sizeof(CLSID)));
  1115. }
  1116. // if they were the same so far, and we forcing an all-fields
  1117. // comparison, then use the all-fields comparison to break ties.
  1118. if ((0 == HRESULT_CODE(hr)) && (iCol & SHCIDS_ALLFIELDS))
  1119. {
  1120. hr = CompareItemIDs(pidd1, pidd2);
  1121. }
  1122. // If the items are still the same, then ask ILCompareRelIDs
  1123. // to walk recursively to the next ids.
  1124. if (0 == HRESULT_CODE(hr))
  1125. {
  1126. hr = ILCompareRelIDs(SAFECAST(this, IShellFolder *), pidl1, pidl2, iCol);
  1127. }
  1128. return hr;
  1129. }
  1130. STDAPI CDrivesDropTarget_Create(HWND hwnd, LPCITEMIDLIST pidl, IDropTarget **ppdropt);
  1131. STDMETHODIMP CDrivesFolder::CreateViewObject(HWND hwnd, REFIID riid, void** ppv)
  1132. {
  1133. // We should not get here unless we have initialized properly
  1134. HRESULT hr = E_NOINTERFACE;
  1135. *ppv = NULL;
  1136. if (IsEqualIID(riid, IID_IShellView))
  1137. {
  1138. SFV_CREATE sSFV;
  1139. sSFV.cbSize = sizeof(sSFV);
  1140. sSFV.psvOuter = NULL;
  1141. sSFV.psfvcb = CDrives_CreateSFVCB(this);
  1142. QueryInterface(IID_PPV_ARG(IShellFolder, &sSFV.pshf)); // in case we are agregated
  1143. hr = SHCreateShellFolderView(&sSFV, (IShellView**)ppv);
  1144. if (sSFV.pshf)
  1145. sSFV.pshf->Release();
  1146. if (sSFV.psfvcb)
  1147. sSFV.psfvcb->Release();
  1148. }
  1149. else if (IsEqualIID(riid, IID_IDropTarget))
  1150. {
  1151. hr = CDrivesDropTarget_Create(hwnd, IDLIST_DRIVES, (IDropTarget **)ppv);
  1152. }
  1153. else if (IsEqualIID(riid, IID_IContextMenu))
  1154. {
  1155. LPITEMIDLIST pidlFolder;
  1156. if (SUCCEEDED(GetCurFolder(&pidlFolder)))
  1157. {
  1158. IContextMenuCB *pcmcb = new CDrivesBackgroundMenuCB(pidlFolder);
  1159. if (pcmcb)
  1160. {
  1161. hr = CDefFolderMenu_Create2Ex(IDLIST_DRIVES, hwnd, 0, NULL, SAFECAST(this, IShellFolder*), pcmcb,
  1162. 0, NULL, (IContextMenu **)ppv);
  1163. pcmcb->Release();
  1164. }
  1165. ILFree(pidlFolder);
  1166. }
  1167. }
  1168. else if (IsEqualIID(riid, IID_ICategoryProvider))
  1169. {
  1170. HKEY hk = NULL;
  1171. BEGIN_CATEGORY_LIST(s_DriveCategories)
  1172. CATEGORY_ENTRY_SCIDMAP(SCID_CAPACITY, CLSID_DriveSizeCategorizer)
  1173. CATEGORY_ENTRY_SCIDMAP(SCID_TYPE, CLSID_DriveTypeCategorizer)
  1174. CATEGORY_ENTRY_SCIDMAP(SCID_FREESPACE, CLSID_FreeSpaceCategorizer)
  1175. END_CATEGORY_LIST()
  1176. RegOpenKey(HKEY_CLASSES_ROOT, TEXT("Drive\\shellex\\Category"), &hk);
  1177. hr = CCategoryProvider_Create(&CLSID_DetailCategorizer, &SCID_TYPE, hk, s_DriveCategories, this, riid, ppv);
  1178. if (hk)
  1179. RegCloseKey(hk);
  1180. }
  1181. return hr;
  1182. }
  1183. STDMETHODIMP CDrivesFolder::GetAttributesOf(UINT cidl, LPCITEMIDLIST* apidl, ULONG* prgfInOut)
  1184. {
  1185. UINT rgfOut = SFGAO_HASSUBFOLDER | SFGAO_CANLINK | SFGAO_CANCOPY |
  1186. SFGAO_DROPTARGET | SFGAO_HASPROPSHEET | SFGAO_FOLDER | SFGAO_STORAGE |
  1187. SFGAO_FILESYSTEM | SFGAO_FILESYSANCESTOR | SFGAO_STORAGEANCESTOR;
  1188. if (cidl == 0)
  1189. {
  1190. // We are getting the attributes for the "MyComputer" folder itself.
  1191. rgfOut = (*prgfInOut & g_asDesktopReqItems[CDESKTOP_REGITEM_DRIVES].dwAttributes);
  1192. }
  1193. else if (cidl == 1)
  1194. {
  1195. TCHAR szDrive[MAX_PATH];
  1196. LPCIDDRIVE pidd = _IsValidID(apidl[0]);
  1197. if (!pidd)
  1198. return E_INVALIDARG;
  1199. CMountPoint* pmtpt = CMountPoint::GetMountPoint(DRIVEID(pidd->cName));
  1200. if (pmtpt)
  1201. {
  1202. SHAnsiToTChar(pidd->cName, szDrive, ARRAYSIZE(szDrive));
  1203. if (*prgfInOut & SFGAO_VALIDATE)
  1204. {
  1205. // (tybeam) todo: make this extensible to validate through the clsid object
  1206. // ill do this when i break everything out into IDriveFolderExt or whatever
  1207. CLSID clsid;
  1208. if (S_OK != _GetCLSIDFromPidl(pidd, &clsid))
  1209. {
  1210. DWORD dwAttribs;
  1211. if (!PathFileExistsAndAttributes(szDrive, &dwAttribs))
  1212. return E_FAIL;
  1213. }
  1214. }
  1215. // If caller wants compression status, we need to ask the filesystem
  1216. if (*prgfInOut & SFGAO_COMPRESSED)
  1217. {
  1218. // Don't wake up sleeping net connections
  1219. if (!pmtpt->IsRemote() || !pmtpt->IsDisconnectedNetDrive())
  1220. {
  1221. if (pmtpt->IsCompressed())
  1222. {
  1223. rgfOut |= SFGAO_COMPRESSED;
  1224. }
  1225. }
  1226. }
  1227. if (*prgfInOut & SFGAO_SHARE)
  1228. {
  1229. if (!pmtpt->IsRemote())
  1230. {
  1231. if (IsShared(szDrive, FALSE))
  1232. rgfOut |= SFGAO_SHARE;
  1233. }
  1234. }
  1235. if ((*prgfInOut & SFGAO_REMOVABLE) &&
  1236. (pmtpt->IsStrictRemovable() || pmtpt->IsFloppy() ||
  1237. pmtpt->IsCDROM()))
  1238. {
  1239. rgfOut |= SFGAO_REMOVABLE;
  1240. }
  1241. // we need to also handle the SFGAO_READONLY bit.
  1242. if (*prgfInOut & SFGAO_READONLY)
  1243. {
  1244. DWORD dwAttributes = pmtpt->GetAttributes();
  1245. if (dwAttributes != -1 && dwAttributes & FILE_ATTRIBUTE_READONLY)
  1246. rgfOut |= SFGAO_READONLY;
  1247. }
  1248. // Should we add the write protect stuff and readonly?
  1249. if ((*prgfInOut & SFGAO_CANRENAME) &&
  1250. (pmtpt->IsStrictRemovable() || pmtpt->IsFloppy() ||
  1251. pmtpt->IsFixedDisk() || pmtpt->IsRemote()) ||
  1252. pmtpt->IsDVDRAMMedia())
  1253. {
  1254. rgfOut |= SFGAO_CANRENAME;
  1255. }
  1256. // Is a restriction causing this drive to not be enumerated?
  1257. if (*prgfInOut & SFGAO_NONENUMERATED)
  1258. {
  1259. DWORD dwRestricted = SHRestricted(REST_NODRIVES);
  1260. if (dwRestricted)
  1261. {
  1262. if (((1 << DRIVEID(pidd->cName)) & dwRestricted))
  1263. {
  1264. rgfOut |= SFGAO_NONENUMERATED;
  1265. }
  1266. }
  1267. }
  1268. // We want to allow moving volumes for bulk copy from some media
  1269. // such as dragging pictures from a compact flash to the my pictures
  1270. // folder.
  1271. if (*prgfInOut & SFGAO_CANMOVE)
  1272. {
  1273. if (pmtpt->IsStrictRemovable() || pmtpt->IsFloppy())
  1274. rgfOut |= SFGAO_CANMOVE;
  1275. }
  1276. pmtpt->Release();
  1277. }
  1278. }
  1279. *prgfInOut = rgfOut;
  1280. return S_OK;
  1281. }
  1282. HRESULT CDrivesFolder::_GetEditTextStrRet(LPCIDDRIVE pidd, STRRET *pstr)
  1283. {
  1284. HRESULT hr = E_FAIL;
  1285. CMountPoint* pMtPt = CMountPoint::GetMountPoint(DRIVEID(pidd->cName));
  1286. if (pMtPt)
  1287. {
  1288. TCHAR szEdit[MAX_PATH];
  1289. hr = pMtPt->GetLabel(szEdit, ARRAYSIZE(szEdit));
  1290. if (SUCCEEDED(hr))
  1291. hr = StringToStrRet(szEdit, pstr);
  1292. pMtPt->Release();
  1293. }
  1294. return hr;
  1295. }
  1296. STDMETHODIMP CDrivesFolder::GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD uFlags, STRRET* pStrRet)
  1297. {
  1298. HRESULT hr;
  1299. LPCIDDRIVE pidd = _IsValidID(pidl);
  1300. if (pidd)
  1301. {
  1302. TCHAR szDrive[ARRAYSIZE(pidd->cName)];
  1303. LPCITEMIDLIST pidlNext = _ILNext(pidl); // Check if pidl contains more than one ID
  1304. SHAnsiToTChar(pidd->cName, szDrive, ARRAYSIZE(szDrive));
  1305. if (ILIsEmpty(pidlNext))
  1306. {
  1307. if (uFlags & SHGDN_FORPARSING)
  1308. {
  1309. hr = StringToStrRet(szDrive, pStrRet);
  1310. }
  1311. else if (uFlags & SHGDN_FOREDITING)
  1312. {
  1313. hr = _GetEditTextStrRet(pidd, pStrRet);
  1314. }
  1315. else
  1316. hr = _GetDisplayNameStrRet(pidd, pStrRet);
  1317. }
  1318. else
  1319. {
  1320. LPITEMIDLIST pidlDrive = ILCombineParentAndFirst(IDLIST_DRIVES, pidl, pidlNext);
  1321. if (pidlDrive)
  1322. {
  1323. // now we need to get the subfolder from which to grab our goodies
  1324. IShellFolder *psfDrive;
  1325. hr = _CreateFSFolder(NULL, pidlDrive, pidd, IID_PPV_ARG(IShellFolder, &psfDrive));
  1326. if (SUCCEEDED(hr))
  1327. {
  1328. hr = psfDrive->GetDisplayNameOf(pidlNext, uFlags, pStrRet);
  1329. psfDrive->Release();
  1330. }
  1331. ILFree(pidlDrive);
  1332. }
  1333. else
  1334. hr = E_OUTOFMEMORY;
  1335. }
  1336. }
  1337. else
  1338. {
  1339. hr = E_INVALIDARG;
  1340. TraceMsg(TF_WARNING, "CDrivesFolder::GetDisplayNameOf() bad PIDL %s", DumpPidl(pidl));
  1341. }
  1342. return hr;
  1343. }
  1344. STDMETHODIMP CDrivesFolder::SetNameOf(HWND hwnd, LPCITEMIDLIST pidl,
  1345. LPCWSTR pszName, DWORD dwReserved, LPITEMIDLIST* ppidlOut)
  1346. {
  1347. if (ppidlOut)
  1348. *ppidlOut = NULL;
  1349. HRESULT hr = E_INVALIDARG;
  1350. LPCIDDRIVE pidd = _IsValidID(pidl);
  1351. if (pidd)
  1352. {
  1353. hr = SetDriveLabel(hwnd, NULL, DRIVEID(pidd->cName), pszName);
  1354. if (SUCCEEDED(hr) && ppidlOut)
  1355. {
  1356. *ppidlOut = ILClone(pidl);
  1357. }
  1358. }
  1359. return hr;
  1360. }
  1361. class CDriveAssocEnumData : public CEnumAssociationElements
  1362. {
  1363. public:
  1364. CDriveAssocEnumData(int iDrive) : _iDrive(iDrive) {}
  1365. private:
  1366. virtual BOOL _Next(IAssociationElement **ppae);
  1367. int _iDrive;
  1368. DWORD _dwChecked;
  1369. };
  1370. enum
  1371. {
  1372. DAED_CHECK_KEY = 0x0001,
  1373. DAED_CHECK_CDORDVD = 0x0002,
  1374. DAED_CHECK_TYPE = 0x0004,
  1375. };
  1376. BOOL CDriveAssocEnumData::_Next(IAssociationElement **ppae)
  1377. {
  1378. HRESULT hr = E_FAIL;
  1379. CMountPoint* pmtpt = CMountPoint::GetMountPoint(_iDrive);
  1380. if (pmtpt)
  1381. {
  1382. if (!(_dwChecked & DAED_CHECK_KEY))
  1383. {
  1384. HKEY hk = pmtpt->GetRegKey();
  1385. if (hk)
  1386. {
  1387. hr = AssocElemCreateForKey(&CLSID_AssocShellElement, hk, ppae);
  1388. RegCloseKey(hk);
  1389. }
  1390. _dwChecked |= DAED_CHECK_KEY;
  1391. }
  1392. if (FAILED(hr) && !(_dwChecked & DAED_CHECK_CDORDVD))
  1393. {
  1394. PCWSTR psz = NULL;
  1395. if (pmtpt->IsAudioCD())
  1396. psz = L"AudioCD";
  1397. else if (pmtpt->IsDVD())
  1398. psz = L"DVD";
  1399. if (psz)
  1400. {
  1401. hr = AssocElemCreateForClass(&CLSID_AssocProgidElement, psz, ppae);
  1402. }
  1403. _dwChecked |= DAED_CHECK_CDORDVD;
  1404. }
  1405. if (FAILED(hr) && !(_dwChecked & DAED_CHECK_TYPE))
  1406. {
  1407. hr = pmtpt->GetAssocSystemElement(ppae);
  1408. _dwChecked |= DAED_CHECK_TYPE;
  1409. }
  1410. pmtpt->Release();
  1411. }
  1412. return SUCCEEDED(hr);
  1413. }
  1414. STDAPI_(BOOL) TBCContainsObject(LPCWSTR pszKey)
  1415. {
  1416. IUnknown *punk;
  1417. if (SUCCEEDED(TBCGetObjectParam(pszKey, IID_PPV_ARG(IUnknown, &punk))))
  1418. {
  1419. punk->Release();
  1420. return TRUE;
  1421. }
  1422. return FALSE;
  1423. }
  1424. HRESULT CDrives_AssocCreate(PCSTR pszName, REFIID riid, void **ppv)
  1425. {
  1426. *ppv = NULL;
  1427. IAssociationArrayInitialize *paai;
  1428. HRESULT hr = ::AssocCreate(CLSID_QueryAssociations, IID_PPV_ARG(IAssociationArrayInitialize, &paai));
  1429. if (SUCCEEDED(hr))
  1430. {
  1431. hr = paai->InitClassElements(ASSOCELEM_BASEIS_FOLDER, L"Drive");
  1432. if (SUCCEEDED(hr) && pszName && !TBCContainsObject(L"ShellExec SHGetAssociations"))
  1433. {
  1434. IEnumAssociationElements *penum = new CDriveAssocEnumData(DRIVEID(pszName));
  1435. if (penum)
  1436. {
  1437. paai->InsertElements(ASSOCELEM_DATA, penum);
  1438. penum->Release();
  1439. }
  1440. }
  1441. if (SUCCEEDED(hr))
  1442. hr = paai->QueryInterface(riid, ppv);
  1443. paai->Release();
  1444. }
  1445. return hr;
  1446. }
  1447. STDAPI_(DWORD) CDrives_GetKeys(PCSTR pszName, HKEY *rgk, UINT ck)
  1448. {
  1449. IAssociationArray *paa;
  1450. HRESULT hr = CDrives_AssocCreate(pszName, IID_PPV_ARG(IAssociationArray, &paa));
  1451. if (SUCCEEDED(hr))
  1452. {
  1453. ck = SHGetAssocKeysEx(paa, -1, rgk, ck);
  1454. paa->Release();
  1455. }
  1456. else
  1457. ck = 0;
  1458. return ck;
  1459. }
  1460. STDMETHODIMP CDrivesFolder::GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST* apidl,
  1461. REFIID riid, UINT* prgfInOut, void** ppv)
  1462. {
  1463. HRESULT hr;
  1464. LPCIDDRIVE pidd = (cidl && apidl) ? _IsValidID(apidl[0]) : NULL;
  1465. *ppv = NULL;
  1466. if (!pidd)
  1467. return E_INVALIDARG;
  1468. if (IsEqualIID(riid, IID_IExtractIconA) || IsEqualIID(riid, IID_IExtractIconW) && pidd)
  1469. {
  1470. WCHAR szDrive[MAX_PATH];
  1471. SHAnsiToUnicode(pidd->cName, szDrive, ARRAYSIZE(szDrive));
  1472. hr = SHCreateDrvExtIcon(szDrive, riid, ppv);
  1473. }
  1474. else
  1475. {
  1476. if (IsEqualIID(riid, IID_IContextMenu))
  1477. {
  1478. HKEY rgk[MAX_ASSOC_KEYS];
  1479. DWORD ck = CDrives_GetKeys(pidd->cName, rgk, ARRAYSIZE(rgk));
  1480. hr = CDefFolderMenu_Create2(IDLIST_DRIVES, hwnd, cidl, apidl,
  1481. SAFECAST(this, IShellFolder *), CDrives_DFMCallBack, ck, rgk, (IContextMenu **)ppv);
  1482. SHRegCloseKeys(rgk, ck);
  1483. }
  1484. else if (IsEqualIID(riid, IID_IDataObject))
  1485. {
  1486. hr = SHCreateFileDataObject(IDLIST_DRIVES, cidl, apidl, NULL, (IDataObject **)ppv);
  1487. }
  1488. else if (IsEqualIID(riid, IID_IDropTarget))
  1489. {
  1490. IShellFolder *psfT;
  1491. hr = BindToObject((LPCITEMIDLIST)pidd, NULL, IID_PPV_ARG(IShellFolder, &psfT));
  1492. if (SUCCEEDED(hr))
  1493. {
  1494. hr = psfT->CreateViewObject(hwnd, IID_IDropTarget, ppv);
  1495. psfT->Release();
  1496. }
  1497. }
  1498. else if (IsEqualIID(riid, IID_IQueryInfo))
  1499. {
  1500. // REVIEW: Shouldn't we use IQA to determine the "prop" string dynamically??? (ZekeL / BuzzR)
  1501. hr = CreateInfoTipFromItem(SAFECAST(this, IShellFolder2 *), (LPCITEMIDLIST)pidd, L"prop:FreeSpace;Capacity", riid, ppv);
  1502. }
  1503. else if (IsEqualIID(riid, IID_IQueryAssociations)
  1504. || IsEqualIID(riid, IID_IAssociationArray))
  1505. {
  1506. hr = CDrives_AssocCreate(pidd->cName, riid, ppv);
  1507. }
  1508. else if (IsEqualIID(riid, IID_IExtractImage))
  1509. {
  1510. CMountPoint* pmtpt = CMountPoint::GetMountPoint(DRIVEID(pidd->cName));
  1511. hr = E_NOINTERFACE;
  1512. if (pmtpt)
  1513. {
  1514. if (pmtpt->IsFixedDisk() || pmtpt->IsRAMDisk())
  1515. {
  1516. hr = CDriveExtractImage_Create(pidd, riid, ppv);
  1517. }
  1518. pmtpt->Release();
  1519. }
  1520. }
  1521. else
  1522. {
  1523. hr = E_NOINTERFACE;
  1524. }
  1525. }
  1526. return hr;
  1527. }
  1528. STDMETHODIMP CDrivesFolder::GetDefaultSearchGUID(GUID *pGuid)
  1529. {
  1530. return DefaultSearchGUID(pGuid);
  1531. }
  1532. STDMETHODIMP CDrivesFolder::EnumSearches(IEnumExtraSearch **ppenum)
  1533. {
  1534. *ppenum = NULL;
  1535. return E_NOTIMPL;
  1536. }
  1537. STDMETHODIMP CDrivesFolder::GetDefaultColumn(DWORD dwRes, ULONG* pSort, ULONG* pDisplay)
  1538. {
  1539. return E_NOTIMPL;
  1540. }
  1541. STDMETHODIMP CDrivesFolder::GetDefaultColumnState(UINT iColumn, DWORD* pdwState)
  1542. {
  1543. HRESULT hr;
  1544. if (iColumn < ARRAYSIZE(c_drives_cols))
  1545. {
  1546. *pdwState = c_drives_cols[iColumn].csFlags;
  1547. if (iColumn == DRIVES_ICOL_COMMENT)
  1548. {
  1549. *pdwState |= SHCOLSTATE_SLOW; // It takes a long time to extract the comment from drives
  1550. }
  1551. hr = S_OK;
  1552. }
  1553. else
  1554. {
  1555. hr = E_INVALIDARG;
  1556. }
  1557. return hr;
  1558. }
  1559. STDMETHODIMP CDrivesFolder::GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID* pscid, VARIANT* pv)
  1560. {
  1561. HRESULT hr = E_INVALIDARG;
  1562. LPCIDDRIVE pidd = _IsValidID(pidl);
  1563. if (pidd)
  1564. {
  1565. if (IsEqualSCID(*pscid, SCID_DESCRIPTIONID))
  1566. {
  1567. SHDESCRIPTIONID did;
  1568. CMountPoint* pmtpt = CMountPoint::GetMountPoint(DRIVEID(pidd->cName));
  1569. if (pmtpt)
  1570. {
  1571. did.dwDescriptionId = pmtpt->GetShellDescriptionID();
  1572. pmtpt->Release();
  1573. }
  1574. else
  1575. {
  1576. did.dwDescriptionId = SHDID_COMPUTER_OTHER;
  1577. }
  1578. did.clsid = CLSID_NULL;
  1579. hr = InitVariantFromBuffer(pv, &did, sizeof(did));
  1580. }
  1581. else if (IsEqualSCID(*pscid, SCID_DetailsProperties))
  1582. {
  1583. // DUI webview properties
  1584. // shouldnt we use IQA??? - ZekeL
  1585. hr = InitVariantFromStr(pv, TEXT("prop:Name;Type;FileSystem;FreeSpace;Capacity"));
  1586. }
  1587. else
  1588. {
  1589. int iCol = FindSCID(c_drives_cols, ARRAYSIZE(c_drives_cols), pscid);
  1590. if (iCol >= 0)
  1591. {
  1592. switch (iCol)
  1593. {
  1594. case DRIVES_ICOL_CAPACITY:
  1595. case DRIVES_ICOL_FREE:
  1596. {
  1597. ULONGLONG ullSize, ullFree;
  1598. hr = E_FAIL;
  1599. if (_GetFreeSpace(pidd, &ullSize, &ullFree))
  1600. {
  1601. pv->vt = VT_UI8;
  1602. pv->ullVal = iCol == DRIVES_ICOL_CAPACITY ? ullSize : ullFree;
  1603. hr = S_OK;
  1604. }
  1605. }
  1606. break;
  1607. default:
  1608. {
  1609. SHELLDETAILS sd;
  1610. hr = GetDetailsOf(pidl, iCol, &sd);
  1611. if (SUCCEEDED(hr))
  1612. {
  1613. hr = InitVariantFromStrRet(&sd.str, pidl, pv);
  1614. }
  1615. }
  1616. }
  1617. }
  1618. }
  1619. }
  1620. return hr;
  1621. }
  1622. STDMETHODIMP CDrivesFolder::GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pDetails)
  1623. {
  1624. TCHAR szTemp[INFOTIPSIZE];
  1625. szTemp[0] = 0;
  1626. pDetails->str.uType = STRRET_CSTR;
  1627. pDetails->str.cStr[0] = 0;
  1628. if (!pidl)
  1629. {
  1630. return GetDetailsOfInfo(c_drives_cols, ARRAYSIZE(c_drives_cols), iColumn, pDetails);
  1631. }
  1632. LPCIDDRIVE pidd = _IsValidID(pidl);
  1633. ASSERTMSG(pidd != NULL, "someone passed us a bad pidl");
  1634. if (!pidd)
  1635. return E_FAIL; // protect faulting code below
  1636. switch (iColumn)
  1637. {
  1638. case DRIVES_ICOL_NAME:
  1639. _GetDisplayName(pidd, szTemp, ARRAYSIZE(szTemp));
  1640. break;
  1641. case DRIVES_ICOL_TYPE:
  1642. CMountPoint::GetTypeString(DRIVEID(pidd->cName), szTemp, ARRAYSIZE(szTemp));
  1643. break;
  1644. case DRIVES_ICOL_COMMENT:
  1645. GetDriveComment(DRIVEID(pidd->cName), szTemp, ARRAYSIZE(szTemp));
  1646. break;
  1647. case DRIVES_ICOL_CAPACITY:
  1648. case DRIVES_ICOL_FREE:
  1649. {
  1650. ULONGLONG ullSize, ullFree;
  1651. if (_GetFreeSpace(pidd, &ullSize, &ullFree))
  1652. {
  1653. StrFormatByteSize64((iColumn == DRIVES_ICOL_CAPACITY) ? ullSize : ullFree, szTemp, ARRAYSIZE(szTemp));
  1654. }
  1655. }
  1656. break;
  1657. case DRIVES_ICOL_FILESYSTEM:
  1658. {
  1659. CMountPoint* pMtPt = CMountPoint::GetMountPoint(DRIVEID(pidd->cName));
  1660. if (pMtPt)
  1661. {
  1662. WCHAR szFileSysName[MAX_FILESYSNAME];
  1663. // GetFileSystemName hits the disk for floppies so disable it.
  1664. // since this is a perf win for defview but disables some functionality, it means
  1665. // do NOT rely on the namespace for getting filesystem information, go direct to
  1666. // the mountpoint instead. if filefldr ever supports SCID_FILESYSTEM like
  1667. // SCID_FREESPACE then this should be munged around.
  1668. if (!pMtPt->IsFloppy() && pMtPt->GetFileSystemName(szFileSysName, ARRAYSIZE(szFileSysName)))
  1669. {
  1670. StrCpyN(szTemp, szFileSysName, min(ARRAYSIZE(szTemp),ARRAYSIZE(szFileSysName)));
  1671. }
  1672. pMtPt->Release();
  1673. }
  1674. }
  1675. break;
  1676. }
  1677. return StringToStrRet(szTemp, &pDetails->str);
  1678. }
  1679. HRESULT CDrivesFolder::MapColumnToSCID(UINT iColumn, SHCOLUMNID* pscid)
  1680. {
  1681. return MapColumnToSCIDImpl(c_drives_cols, ARRAYSIZE(c_drives_cols), iColumn, pscid);
  1682. }
  1683. STDMETHODIMP CDrivesFolder::GetClassID(CLSID* pCLSID)
  1684. {
  1685. *pCLSID = CLSID_MyComputer;
  1686. return S_OK;
  1687. }
  1688. STDMETHODIMP CDrivesFolder::Initialize(LPCITEMIDLIST pidl)
  1689. {
  1690. // Only allow the Drives root on the desktop
  1691. ASSERT(AssertIsIDListInNameSpace(pidl, &CLSID_MyComputer) && ILIsEmpty(_ILNext(pidl)));
  1692. return S_OK;
  1693. }
  1694. STDMETHODIMP CDrivesFolder::GetCurFolder(LPITEMIDLIST *ppidl)
  1695. {
  1696. return GetCurFolderImpl(IDLIST_DRIVES, ppidl);
  1697. }
  1698. STDMETHODIMP CDrivesFolder::_GetIconOverlayInfo(LPCIDDRIVE pidd, int *pIndex, DWORD dwFlags)
  1699. {
  1700. IShellIconOverlayManager *psiom;
  1701. HRESULT hr = GetIconOverlayManager(&psiom);
  1702. if (SUCCEEDED(hr))
  1703. {
  1704. WCHAR wszDrive[10];
  1705. SHAnsiToUnicode(pidd->cName, wszDrive, ARRAYSIZE(wszDrive));
  1706. if (IsShared(wszDrive, FALSE))
  1707. {
  1708. hr = psiom->GetReservedOverlayInfo(wszDrive, 0, pIndex, SIOM_OVERLAYINDEX, SIOM_RESERVED_SHARED);
  1709. }
  1710. else
  1711. {
  1712. hr = psiom->GetFileOverlayInfo(wszDrive, 0, pIndex, dwFlags);
  1713. }
  1714. psiom->Release();
  1715. }
  1716. return hr;
  1717. }
  1718. STDMETHODIMP CDrivesFolder::GetOverlayIndex(LPCITEMIDLIST pidl, int *pIndex)
  1719. {
  1720. HRESULT hr = E_FAIL;
  1721. LPCIDDRIVE pidd = _IsValidID(pidl);
  1722. if (pidd)
  1723. {
  1724. hr = _GetIconOverlayInfo(pidd, pIndex, SIOM_OVERLAYINDEX);
  1725. }
  1726. return hr;
  1727. }
  1728. STDMETHODIMP CDrivesFolder::GetOverlayIconIndex(LPCITEMIDLIST pidl, int *pIndex)
  1729. {
  1730. HRESULT hr = E_FAIL;
  1731. LPCIDDRIVE pidd = _IsValidID(pidl);
  1732. if (pidd)
  1733. {
  1734. hr = _GetIconOverlayInfo(pidd, pIndex, SIOM_ICONINDEX);
  1735. }
  1736. return hr;
  1737. }
  1738. STDMETHODIMP CDrivesFolder::CompareItemIDs(LPCIDDRIVE pidd1, LPCIDDRIVE pidd2)
  1739. {
  1740. // Compare the drive letter for sorting purpose.
  1741. int iRes = StrCmpICA(pidd1->cName, pidd2->cName); // don't need local goo
  1742. // then, compare pidl sizes
  1743. if (iRes == 0)
  1744. {
  1745. iRes = pidd1->cb - pidd2->cb;
  1746. }
  1747. // still equal, compare clsids if both pidls are big and have them
  1748. if ((iRes == 0) && (pidd1->cb >= sizeof(IDDRIVE)))
  1749. {
  1750. iRes = memcmp(&pidd1->clsid, &pidd2->clsid, sizeof(CLSID));
  1751. }
  1752. // still equal, compare on bFlags
  1753. if (iRes == 0)
  1754. {
  1755. iRes = pidd1->bFlags - pidd2->bFlags;
  1756. }
  1757. return ResultFromShort(iRes);
  1758. }
  1759. HRESULT CDrivesFolder::_OnChangeNotify(LPARAM lNotification, LPCITEMIDLIST *ppidl)
  1760. {
  1761. // Get to the last part of this id list...
  1762. if ((lNotification != SHCNE_DRIVEADD) || (ppidl == NULL) || (*ppidl == NULL))
  1763. return S_OK;
  1764. DWORD dwRestricted = SHRestricted(REST_NODRIVES);
  1765. if (dwRestricted == 0)
  1766. return S_OK; // no drives restricted... (majority case)
  1767. LPCIDDRIVE pidd = (LPCIDDRIVE)ILFindLastID(*ppidl);
  1768. if (((1 << DRIVEID(pidd->cName)) & dwRestricted))
  1769. {
  1770. TraceMsg(DM_TRACE, "Drive not added due to restrictions or Drivespace says it should be hidden");
  1771. return S_FALSE;
  1772. }
  1773. return S_OK;
  1774. }
  1775. CDrivesBackgroundMenuCB::CDrivesBackgroundMenuCB(LPITEMIDLIST pidlFolder) : _cRef(1)
  1776. {
  1777. _pidlFolder = ILClone(pidlFolder);
  1778. }
  1779. CDrivesBackgroundMenuCB::~CDrivesBackgroundMenuCB()
  1780. {
  1781. ILFree(_pidlFolder);
  1782. }
  1783. STDMETHODIMP CDrivesBackgroundMenuCB::QueryInterface(REFIID riid, void **ppvObj)
  1784. {
  1785. static const QITAB qit[] = {
  1786. QITABENT(CDrivesBackgroundMenuCB, IContextMenuCB),
  1787. { 0 },
  1788. };
  1789. return QISearch(this, qit, riid, ppvObj);
  1790. }
  1791. STDMETHODIMP_(ULONG) CDrivesBackgroundMenuCB::AddRef()
  1792. {
  1793. return InterlockedIncrement(&_cRef);
  1794. }
  1795. STDMETHODIMP_(ULONG) CDrivesBackgroundMenuCB::Release()
  1796. {
  1797. if (InterlockedDecrement(&_cRef))
  1798. return _cRef;
  1799. delete this;
  1800. return 0;
  1801. }
  1802. STDMETHODIMP CDrivesBackgroundMenuCB::_GetHelpText(UINT offset, BOOL bWide, LPARAM lParam, UINT cch)
  1803. {
  1804. UINT idRes = IDS_MH_FSIDM_FIRST + offset;
  1805. if (bWide)
  1806. LoadStringW(HINST_THISDLL, idRes, (LPWSTR)lParam, cch);
  1807. else
  1808. LoadStringA(HINST_THISDLL, idRes, (LPSTR)lParam, cch);
  1809. return S_OK;
  1810. }
  1811. STDMETHODIMP CDrivesBackgroundMenuCB::CallBack (IShellFolder *psf, HWND hwndOwner, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1812. {
  1813. HRESULT hr = S_OK;
  1814. switch (uMsg)
  1815. {
  1816. case DFM_MERGECONTEXTMENU_BOTTOM:
  1817. if (!(wParam & (CMF_VERBSONLY | CMF_DVFILE)))
  1818. {
  1819. DWORD dwAttr = SFGAO_HASPROPSHEET;
  1820. if (FAILED(SHGetAttributesOf(_pidlFolder, &dwAttr)) ||
  1821. SFGAO_HASPROPSHEET & dwAttr)
  1822. {
  1823. CDefFolderMenu_MergeMenu(HINST_THISDLL, POPUP_PROPERTIES_BG, 0, (LPQCMINFO)lParam);
  1824. }
  1825. }
  1826. break;
  1827. case DFM_GETHELPTEXT:
  1828. case DFM_GETHELPTEXTW:
  1829. hr = _GetHelpText(LOWORD(wParam), uMsg == DFM_GETHELPTEXTW, lParam, HIWORD(wParam));
  1830. break;
  1831. case DFM_INVOKECOMMAND:
  1832. switch (wParam)
  1833. {
  1834. case FSIDM_PROPERTIESBG:
  1835. SHRunControlPanel(TEXT("SYSDM.CPL"), hwndOwner);
  1836. break;
  1837. default:
  1838. hr = S_FALSE;
  1839. break;
  1840. }
  1841. break;
  1842. default:
  1843. hr = E_NOTIMPL;
  1844. break;
  1845. }
  1846. return hr;
  1847. }
  1848. class CDriveExtractImage : public IExtractImage,
  1849. public IPersist
  1850. {
  1851. public:
  1852. CDriveExtractImage();
  1853. STDMETHOD (QueryInterface)(REFIID riid, void **ppv);
  1854. STDMETHOD_(ULONG, AddRef)();
  1855. STDMETHOD_(ULONG, Release)();
  1856. // IExtractImage/IExtractLogo
  1857. STDMETHOD (GetLocation)(LPWSTR pszPathBuffer,
  1858. DWORD cch,
  1859. DWORD * pdwPriority,
  1860. const SIZE * prgSize,
  1861. DWORD dwRecClrDepth,
  1862. DWORD *pdwFlags);
  1863. STDMETHOD (Extract)(HBITMAP * phBmpThumbnail);
  1864. // IPersist
  1865. STDMETHOD(GetClassID)(LPCLSID lpClassID);
  1866. STDMETHOD(Init)(LPCIDDRIVE pidd);
  1867. private:
  1868. ~CDriveExtractImage();
  1869. long _cRef;
  1870. SIZE _size;
  1871. TCHAR _szPath[4];
  1872. DWORDLONG _dwlFreeSpace;
  1873. DWORDLONG _dwlUsedSpace;
  1874. DWORDLONG _dwlTotalSpace;
  1875. DWORD _dwUsedSpacePer1000; // amount of used space /1000
  1876. // root drive
  1877. enum
  1878. {
  1879. PIE_USEDCOLOR = 0,
  1880. PIE_FREECOLOR,
  1881. PIE_USEDSHADOW,
  1882. PIE_FREESHADOW,
  1883. PIE_NUM // keep track of number of PIE_ values
  1884. };
  1885. COLORREF _acrChartColors[PIE_NUM]; // color scheme
  1886. STDMETHOD(_CreateRenderingDC)(HDC* phdc, HBITMAP* phBmpThumbnail, HBITMAP* phbmpOld, int cx, int cy);
  1887. void _DestroyRenderingDC(HDC hdc, HBITMAP hbmpOld);
  1888. STDMETHOD(_RenderToDC)(HDC hdc);
  1889. STDMETHOD(ComputeFreeSpace)(LPTSTR pszFileName);
  1890. DWORD IntSqrt(DWORD dwNum);
  1891. STDMETHOD(Draw3dPie)(HDC hdc, LPRECT lprc, DWORD dwPer1000, const COLORREF *lpColors);
  1892. };
  1893. STDAPI CDriveExtractImage_Create(LPCIDDRIVE pidd, REFIID riid, void **ppvObj)
  1894. {
  1895. HRESULT hr = E_OUTOFMEMORY;
  1896. #if 0
  1897. CDriveExtractImage * pObj = new CDriveExtractImage;
  1898. if (pObj)
  1899. {
  1900. hr = pObj->Init(pidd);
  1901. if (SUCCEEDED(hr))
  1902. hr = pObj->QueryInterface(riid, ppvObj);
  1903. pObj->Release();
  1904. }
  1905. #endif
  1906. return hr;
  1907. }
  1908. CDriveExtractImage::CDriveExtractImage() : _cRef (1)
  1909. {
  1910. }
  1911. CDriveExtractImage::~CDriveExtractImage()
  1912. {
  1913. }
  1914. STDMETHODIMP CDriveExtractImage::QueryInterface(REFIID riid, void **ppv)
  1915. {
  1916. static const QITAB qit[] = {
  1917. QITABENT(CDriveExtractImage, IExtractImage),
  1918. QITABENTMULTI2(CDriveExtractImage, IID_IExtractLogo, IExtractImage),
  1919. QITABENT(CDriveExtractImage, IPersist),
  1920. { 0 },
  1921. };
  1922. return QISearch(this, qit, riid, ppv);
  1923. }
  1924. STDMETHODIMP_(ULONG) CDriveExtractImage::AddRef()
  1925. {
  1926. return InterlockedIncrement(&_cRef);
  1927. }
  1928. STDMETHODIMP_(ULONG) CDriveExtractImage::Release()
  1929. {
  1930. if (InterlockedDecrement(&_cRef))
  1931. return _cRef;
  1932. delete this;
  1933. return 0;
  1934. }
  1935. STDMETHODIMP CDriveExtractImage::GetLocation(LPWSTR pszPathBuffer, DWORD cch,
  1936. DWORD * pdwPriority, const SIZE * prgSize,
  1937. DWORD dwRecClrDepth, DWORD *pdwFlags)
  1938. {
  1939. // Sets the size of the thumbnail
  1940. _size = *prgSize;
  1941. SHTCharToUnicode(_szPath, pszPathBuffer, sizeof(_szPath));
  1942. *pdwFlags &= ~IEIFLAG_CACHE;
  1943. *pdwFlags |= IEIFLAG_ASYNC | IEIFLAG_NOBORDER;
  1944. return S_OK;
  1945. }
  1946. STDMETHODIMP CDriveExtractImage::_CreateRenderingDC(HDC* phdc, HBITMAP* phBmpThumbnail, HBITMAP* phbmpOld, int cx, int cy)
  1947. {
  1948. HRESULT hr = E_OUTOFMEMORY;
  1949. HDC hdc = GetDC(NULL);
  1950. if (hdc)
  1951. {
  1952. *phdc = CreateCompatibleDC(hdc);
  1953. if (*phdc)
  1954. {
  1955. *phBmpThumbnail = CreateCompatibleBitmap (hdc, cx, cy);
  1956. if (*phBmpThumbnail)
  1957. {
  1958. *phbmpOld = (HBITMAP) SelectObject(*phdc, *phBmpThumbnail);
  1959. hr = S_OK;
  1960. }
  1961. }
  1962. ReleaseDC(NULL, hdc);
  1963. }
  1964. return hr;
  1965. }
  1966. void CDriveExtractImage::_DestroyRenderingDC(HDC hdc, HBITMAP hbmpOld) // Unselects the bitmap, and deletes the Dc
  1967. {
  1968. if (hbmpOld)
  1969. SelectObject (hdc, hbmpOld);
  1970. DeleteDC(hdc);
  1971. }
  1972. STDMETHODIMP CDriveExtractImage::_RenderToDC(HDC hdc) // Does a generic render of child pidl
  1973. {
  1974. RECT rc = { 0, 0, (long)_size.cx, (long)_size.cy };
  1975. HBRUSH hbr = CreateSolidBrush(GetSysColor(COLOR_WINDOW));
  1976. if (hbr)
  1977. {
  1978. FillRect (hdc, (const RECT*) &rc, hbr);
  1979. DeleteObject(hbr);
  1980. }
  1981. if (SUCCEEDED(ComputeFreeSpace(_szPath)))
  1982. {
  1983. _acrChartColors[PIE_USEDCOLOR] = GetSysColor(COLOR_3DFACE);
  1984. _acrChartColors[PIE_FREECOLOR] = GetSysColor(COLOR_3DHILIGHT);
  1985. _acrChartColors[PIE_USEDSHADOW] = GetSysColor(COLOR_3DSHADOW);
  1986. _acrChartColors[PIE_FREESHADOW] = GetSysColor(COLOR_3DFACE);
  1987. rc.top += 10;
  1988. rc.left += 10;
  1989. rc.bottom -= 10;
  1990. rc.right -= 10;
  1991. Draw3dPie(hdc, &rc, _dwUsedSpacePer1000, _acrChartColors);
  1992. }
  1993. return S_OK;
  1994. }
  1995. STDMETHODIMP CDriveExtractImage::Extract(HBITMAP * phBmpThumbnail)
  1996. {
  1997. HDC hdc;
  1998. HBITMAP hbmpOld;
  1999. // Creates the rendering DC based on the screen
  2000. HRESULT hr = _CreateRenderingDC(&hdc, phBmpThumbnail, &hbmpOld, _size.cx, _size.cy);
  2001. if (SUCCEEDED(hr))
  2002. {
  2003. // Does a generic render of child pidl
  2004. hr = _RenderToDC(hdc);
  2005. // Unselects the bitmap, restores the old bitmap and deletes the DC
  2006. _DestroyRenderingDC(hdc, hbmpOld);
  2007. }
  2008. if (FAILED(hr) && *phBmpThumbnail)
  2009. {
  2010. DeleteObject(*phBmpThumbnail);
  2011. *phBmpThumbnail = NULL;
  2012. }
  2013. return hr;
  2014. }
  2015. STDMETHODIMP CDriveExtractImage::GetClassID(CLSID *pClassID)
  2016. {
  2017. return E_NOTIMPL;
  2018. }
  2019. STDMETHODIMP CDriveExtractImage::Init(LPCIDDRIVE pidd)
  2020. {
  2021. SHAnsiToTChar(pidd->cName, _szPath, ARRAYSIZE(_szPath));
  2022. return S_OK;
  2023. }
  2024. // Pie Chart functions
  2025. STDMETHODIMP CDriveExtractImage::ComputeFreeSpace(LPTSTR pszFileName)
  2026. {
  2027. ULARGE_INTEGER qwFreeCaller; // use this for free space -- this will take into account disk quotas and such on NT
  2028. ULARGE_INTEGER qwTotal;
  2029. ULARGE_INTEGER qwFree; // unused
  2030. // Compute free & total space and check for valid results
  2031. // if have a fn pointer call SHGetDiskFreeSpaceA
  2032. if (SHGetDiskFreeSpaceEx(pszFileName, &qwFreeCaller, &qwTotal, &qwFree))
  2033. {
  2034. _dwlFreeSpace = qwFreeCaller.QuadPart;
  2035. _dwlTotalSpace = qwTotal.QuadPart;
  2036. _dwlUsedSpace = _dwlTotalSpace - _dwlFreeSpace;
  2037. if (EVAL(_dwlTotalSpace > 0 && _dwlFreeSpace <= _dwlTotalSpace))
  2038. {
  2039. // some special cases require interesting treatment
  2040. if (_dwlTotalSpace == 0 || _dwlFreeSpace == _dwlTotalSpace)
  2041. {
  2042. _dwUsedSpacePer1000 = 0;
  2043. }
  2044. else if (_dwlFreeSpace == 0)
  2045. {
  2046. _dwUsedSpacePer1000 = 1000;
  2047. }
  2048. else
  2049. {
  2050. // not completely full or empty
  2051. _dwUsedSpacePer1000 = (DWORD)(_dwlUsedSpace * 1000 / _dwlTotalSpace);
  2052. // Trick: if user has extremely little free space, the user expects to still see
  2053. // a tiny free slice -- not a full drive. Similarly for almost free drive.
  2054. if (_dwUsedSpacePer1000 == 0)
  2055. {
  2056. _dwUsedSpacePer1000 = 1;
  2057. }
  2058. else if (_dwUsedSpacePer1000 == 1000)
  2059. {
  2060. _dwUsedSpacePer1000 = 999;
  2061. }
  2062. }
  2063. return S_OK;
  2064. }
  2065. }
  2066. return E_FAIL;
  2067. }
  2068. DWORD CDriveExtractImage::IntSqrt(DWORD dwNum)
  2069. {
  2070. // This code came from "drawpie.c"
  2071. DWORD dwSqrt = 0;
  2072. DWORD dwRemain = 0;
  2073. DWORD dwTry = 0;
  2074. for (int i=0; i<16; ++i)
  2075. {
  2076. dwRemain = (dwRemain<<2) | (dwNum>>30);
  2077. dwSqrt <<= 1;
  2078. dwTry = dwSqrt*2 + 1;
  2079. if (dwRemain >= dwTry)
  2080. {
  2081. dwRemain -= dwTry;
  2082. dwSqrt |= 0x01;
  2083. }
  2084. dwNum <<= 2;
  2085. }
  2086. return dwSqrt;
  2087. } // IntSqrt
  2088. STDMETHODIMP CDriveExtractImage::Draw3dPie(HDC hdc, LPRECT lprc, DWORD dwPer1000, const COLORREF *lpColors)
  2089. {
  2090. ASSERT(lprc != NULL && lpColors != NULL);
  2091. enum
  2092. {
  2093. COLOR_UP = 0,
  2094. COLOR_DN,
  2095. COLOR_UPSHADOW,
  2096. COLOR_DNSHADOW,
  2097. COLOR_NUM // #of entries
  2098. };
  2099. // The majority of this code came from "drawpie.c"
  2100. const LONG c_lShadowScale = 6; // ratio of shadow depth to height
  2101. const LONG c_lAspectRatio = 2; // ratio of width : height of ellipse
  2102. // We make sure that the aspect ratio of the pie-chart is always preserved
  2103. // regardless of the shape of the given rectangle
  2104. // Stabilize the aspect ratio now...
  2105. LONG lHeight = lprc->bottom - lprc->top;
  2106. LONG lWidth = lprc->right - lprc->left;
  2107. LONG lTargetHeight = (lHeight * c_lAspectRatio <= lWidth? lHeight: lWidth / c_lAspectRatio);
  2108. LONG lTargetWidth = lTargetHeight * c_lAspectRatio; // need to adjust because w/c * c isn't always == w
  2109. // Shrink the rectangle on both sides to the correct size
  2110. lprc->top += (lHeight - lTargetHeight) / 2;
  2111. lprc->bottom = lprc->top + lTargetHeight;
  2112. lprc->left += (lWidth - lTargetWidth) / 2;
  2113. lprc->right = lprc->left + lTargetWidth;
  2114. // Compute a shadow depth based on height of the image
  2115. LONG lShadowDepth = lTargetHeight / c_lShadowScale;
  2116. // check dwPer1000 to ensure within bounds
  2117. if (dwPer1000 > 1000)
  2118. dwPer1000 = 1000;
  2119. // Now the drawing function
  2120. int cx, cy, rx, ry, x, y;
  2121. int uQPctX10;
  2122. RECT rcItem;
  2123. HRGN hEllRect, hEllipticRgn, hRectRgn;
  2124. HBRUSH hBrush, hOldBrush;
  2125. HPEN hPen, hOldPen;
  2126. rcItem = *lprc;
  2127. rcItem.left = lprc->left;
  2128. rcItem.top = lprc->top;
  2129. rcItem.right = lprc->right - rcItem.left;
  2130. rcItem.bottom = lprc->bottom - rcItem.top - lShadowDepth;
  2131. rx = rcItem.right / 2;
  2132. cx = rcItem.left + rx - 1;
  2133. ry = rcItem.bottom / 2;
  2134. cy = rcItem.top + ry - 1;
  2135. if (rx<=10 || ry<=10)
  2136. {
  2137. return S_FALSE;
  2138. }
  2139. rcItem.right = rcItem.left+2*rx;
  2140. rcItem.bottom = rcItem.top+2*ry;
  2141. /* Translate to first quadrant of a Cartesian system
  2142. */
  2143. uQPctX10 = (dwPer1000 % 500) - 250;
  2144. if (uQPctX10 < 0)
  2145. {
  2146. uQPctX10 = -uQPctX10;
  2147. }
  2148. /* Calc x and y. I am trying to make the area be the right percentage.
  2149. ** I don't know how to calculate the area of a pie slice exactly, so I
  2150. ** approximate it by using the triangle area instead.
  2151. */
  2152. // NOTE-- *** in response to the above comment ***
  2153. // Calculating the area of a pie slice exactly is actually very
  2154. // easy by conceptually rescaling into a circle but the complications
  2155. // introduced by having to work in fixed-point arithmetic makes it
  2156. // unworthwhile to code this-- CemP
  2157. if (uQPctX10 < 120)
  2158. {
  2159. x = IntSqrt(((DWORD)rx*(DWORD)rx*(DWORD)uQPctX10*(DWORD)uQPctX10)
  2160. /((DWORD)uQPctX10*(DWORD)uQPctX10+(250L-(DWORD)uQPctX10)*(250L-(DWORD)uQPctX10)));
  2161. y = IntSqrt(((DWORD)rx*(DWORD)rx-(DWORD)x*(DWORD)x)*(DWORD)ry*(DWORD)ry/((DWORD)rx*(DWORD)rx));
  2162. }
  2163. else
  2164. {
  2165. y = IntSqrt((DWORD)ry*(DWORD)ry*(250L-(DWORD)uQPctX10)*(250L-(DWORD)uQPctX10)
  2166. /((DWORD)uQPctX10*(DWORD)uQPctX10+(250L-(DWORD)uQPctX10)*(250L-(DWORD)uQPctX10)));
  2167. x = IntSqrt(((DWORD)ry*(DWORD)ry-(DWORD)y*(DWORD)y)*(DWORD)rx*(DWORD)rx/((DWORD)ry*(DWORD)ry));
  2168. }
  2169. /* Switch on the actual quadrant
  2170. */
  2171. switch (dwPer1000 / 250)
  2172. {
  2173. case 1:
  2174. y = -y;
  2175. break;
  2176. case 2:
  2177. break;
  2178. case 3:
  2179. x = -x;
  2180. break;
  2181. default: // case 0 and case 4
  2182. x = -x;
  2183. y = -y;
  2184. break;
  2185. }
  2186. /* Now adjust for the center.
  2187. */
  2188. x += cx;
  2189. y += cy;
  2190. //
  2191. // Hack to get around bug in NTGDI
  2192. x = x < 0 ? 0 : x;
  2193. /* Draw the shadows using regions (to reduce flicker).
  2194. */
  2195. hEllipticRgn = CreateEllipticRgnIndirect(&rcItem);
  2196. OffsetRgn(hEllipticRgn, 0, lShadowDepth);
  2197. hEllRect = CreateRectRgn(rcItem.left, cy, rcItem.right, cy+lShadowDepth);
  2198. hRectRgn = CreateRectRgn(0, 0, 0, 0);
  2199. CombineRgn(hRectRgn, hEllipticRgn, hEllRect, RGN_OR);
  2200. OffsetRgn(hEllipticRgn, 0, -(int)lShadowDepth);
  2201. CombineRgn(hEllRect, hRectRgn, hEllipticRgn, RGN_DIFF);
  2202. /* Always draw the whole area in the free shadow/
  2203. */
  2204. hBrush = CreateSolidBrush(lpColors[COLOR_DNSHADOW]);
  2205. if (hBrush)
  2206. {
  2207. FillRgn(hdc, hEllRect, hBrush);
  2208. DeleteObject(hBrush);
  2209. }
  2210. /* Draw the used shadow only if the disk is at least half used.
  2211. */
  2212. if (dwPer1000>500 && (hBrush = CreateSolidBrush(lpColors[COLOR_UPSHADOW]))!=NULL)
  2213. {
  2214. DeleteObject(hRectRgn);
  2215. hRectRgn = CreateRectRgn(x, cy, rcItem.right, lprc->bottom);
  2216. CombineRgn(hEllipticRgn, hEllRect, hRectRgn, RGN_AND);
  2217. FillRgn(hdc, hEllipticRgn, hBrush);
  2218. DeleteObject(hBrush);
  2219. }
  2220. DeleteObject(hRectRgn);
  2221. DeleteObject(hEllipticRgn);
  2222. DeleteObject(hEllRect);
  2223. hPen = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_WINDOWFRAME));
  2224. hOldPen = (HPEN__*) SelectObject(hdc, hPen);
  2225. // if per1000 is 0 or 1000, draw full elipse, otherwise, also draw a pie section.
  2226. // we might have a situation where per1000 isn't 0 or 1000 but y == cy due to approx error,
  2227. // so make sure to draw the ellipse the correct color, and draw a line (with Pie()) to
  2228. // indicate not completely full or empty pie.
  2229. hBrush = CreateSolidBrush(lpColors[dwPer1000 < 500 && y == cy && x < cx? COLOR_DN: COLOR_UP]);
  2230. hOldBrush = (HBRUSH__*) SelectObject(hdc, hBrush);
  2231. Ellipse(hdc, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom);
  2232. SelectObject(hdc, hOldBrush);
  2233. DeleteObject(hBrush);
  2234. if (dwPer1000 != 0 && dwPer1000 != 1000)
  2235. {
  2236. // display small sub-section of ellipse for smaller part
  2237. hBrush = CreateSolidBrush(lpColors[COLOR_DN]);
  2238. hOldBrush = (HBRUSH__*) SelectObject(hdc, hBrush);
  2239. Pie(hdc, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom,
  2240. rcItem.left, cy, x, y);
  2241. SelectObject(hdc, hOldBrush);
  2242. DeleteObject(hBrush);
  2243. }
  2244. Arc(hdc, rcItem.left, rcItem.top+lShadowDepth, rcItem.right - 1, rcItem.bottom+lShadowDepth - 1,
  2245. rcItem.left, cy+lShadowDepth, rcItem.right, cy+lShadowDepth-1);
  2246. MoveToEx(hdc, rcItem.left, cy, NULL);
  2247. LineTo(hdc, rcItem.left, cy+lShadowDepth);
  2248. MoveToEx(hdc, rcItem.right-1, cy, NULL);
  2249. LineTo(hdc, rcItem.right-1, cy+lShadowDepth);
  2250. if (dwPer1000 > 500 && dwPer1000 < 1000)
  2251. {
  2252. MoveToEx(hdc, x, y, NULL);
  2253. LineTo(hdc, x, y+lShadowDepth);
  2254. }
  2255. SelectObject(hdc, hOldPen);
  2256. DeleteObject(hPen);
  2257. return S_OK; // Everything worked fine
  2258. } // Draw3dPie