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.

2149 lines
69 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. class CDrivesViewCallback;
  38. class CDrivesFolderEnum;
  39. class CDrivesBackgroundMenuCB : public IContextMenuCB
  40. {
  41. public:
  42. CDrivesBackgroundMenuCB(LPITEMIDLIST pidlFolder);
  43. ~CDrivesBackgroundMenuCB();
  44. // IUnknown
  45. STDMETHOD(QueryInterface)(REFIID riid, void **ppv);
  46. STDMETHOD_(ULONG,AddRef)();
  47. STDMETHOD_(ULONG,Release)();
  48. // IContextMenuCB
  49. STDMETHOD(CallBack) (IShellFolder *psf, HWND hwndOwner, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam);
  50. private:
  51. STDMETHOD(_GetHelpText) (UINT offset, BOOL bWide, LPARAM lParam, UINT cch);
  52. LPITEMIDLIST _pidlFolder;
  53. LONG _cRef;
  54. };
  55. class CDrivesFolder : public CAggregatedUnknown, IShellFolder2, IPersistFolder2, IShellIconOverlay
  56. {
  57. public:
  58. // IUnknown
  59. STDMETHODIMP QueryInterface(REFIID riid, void ** ppvObj)
  60. { return CAggregatedUnknown::QueryInterface(riid, ppvObj); };
  61. STDMETHODIMP_(ULONG) AddRef(void)
  62. { return CAggregatedUnknown::AddRef(); };
  63. STDMETHODIMP_(ULONG) Release(void)
  64. { return CAggregatedUnknown::Release(); };
  65. // IShellFolder
  66. STDMETHODIMP ParseDisplayName(HWND hwnd, IBindCtx *pbc, LPOLESTR pszDisplayName,
  67. ULONG* pchEaten, LPITEMIDLIST* ppidl, ULONG* pdwAttributes);
  68. STDMETHODIMP EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList ** ppenumIDList);
  69. STDMETHODIMP BindToObject(LPCITEMIDLIST pidl, IBindCtx *pbc, REFIID riid, void** ppvOut);
  70. STDMETHODIMP BindToStorage(LPCITEMIDLIST pidl, IBindCtx *pbc, REFIID riid, void** ppvObj);
  71. STDMETHODIMP CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
  72. STDMETHODIMP CreateViewObject (HWND hwndOwner, REFIID riid, void** ppvOut);
  73. STDMETHODIMP GetAttributesOf(UINT cidl, LPCITEMIDLIST* apidl, ULONG* rgfInOut);
  74. STDMETHODIMP GetUIObjectOf(HWND hwndOwner, UINT cidl, LPCITEMIDLIST* apidl,
  75. REFIID riid, UINT* prgfInOut, void** ppvOut);
  76. STDMETHODIMP GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD uFlags, STRRET *pName);
  77. STDMETHODIMP SetNameOf(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR pszName, DWORD uFlags,
  78. LPITEMIDLIST* ppidlOut);
  79. // IShellFolder2
  80. STDMETHODIMP GetDefaultSearchGUID(GUID *pGuid);
  81. STDMETHODIMP EnumSearches(IEnumExtraSearch **ppenum);
  82. STDMETHODIMP GetDefaultColumn(DWORD dwRes, ULONG* pSort, ULONG* pDisplay);
  83. STDMETHODIMP GetDefaultColumnState(UINT iColumn, DWORD* pbState);
  84. STDMETHODIMP GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID* pscid, VARIANT* pv);
  85. STDMETHODIMP GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS* pDetails);
  86. STDMETHODIMP MapColumnToSCID(UINT iColumn, SHCOLUMNID* pscid);
  87. // IPersist
  88. STDMETHODIMP GetClassID(CLSID* pClassID);
  89. // IPersistFolder
  90. STDMETHODIMP Initialize(LPCITEMIDLIST pidl);
  91. // IPersistFolder2
  92. STDMETHODIMP GetCurFolder(LPITEMIDLIST* ppidl);
  93. // IShellIconOverlay
  94. STDMETHODIMP GetOverlayIndex(LPCITEMIDLIST pidl, int* pIndex);
  95. STDMETHODIMP GetOverlayIconIndex(LPCITEMIDLIST pidl, int* pIconIndex);
  96. STDMETHODIMP GetMaxNameLength(LPCITEMIDLIST pidlItem, UINT *pcchMax);
  97. protected:
  98. CDrivesFolder(IUnknown* punkOuter);
  99. ~CDrivesFolder();
  100. // used by the CAggregatedUnknown stuff
  101. HRESULT v_InternalQueryInterface(REFIID riid, void **ppvObj);
  102. BOOL v_HandleDelete(PLONG pcRef);
  103. STDMETHODIMP CompareItemIDs(LPCIDDRIVE pidd1, LPCIDDRIVE pidd2);
  104. static BOOL _GetFreeSpace(LPCIDDRIVE pidd, ULONGLONG *pSize, ULONGLONG *pFree);
  105. static HRESULT _OnChangeNotify(LPARAM lNotification, LPCITEMIDLIST *ppidl);
  106. static HRESULT _GetCLSIDFromPidl(LPCIDDRIVE pidd, CLSID *pclsid);
  107. static HRESULT _CheckDriveType(int iDrive, LPCTSTR pszCLSID);
  108. static HRESULT _FindExtCLSID(int iDrive, CLSID *pclsid);
  109. static HRESULT _FillIDDrive(DRIVE_IDLIST *piddl, int iDrive, BOOL fNoCLSID, IBindCtx* pbc);
  110. static LPCIDDRIVE _IsValidID(LPCITEMIDLIST pidl);
  111. static HRESULT _GetDisplayNameStrRet(LPCIDDRIVE pidd, STRRET *pStrRet);
  112. static HRESULT _GetDisplayName(LPCIDDRIVE pidd, LPTSTR pszName, UINT cchMax);
  113. static HRESULT _CreateFSFolderObj(IBindCtx *pbc, LPCITEMIDLIST pidlDrive, LPCIDDRIVE pidd, REFIID riid, void **ppv);
  114. static HRESULT _CreateFSFolder(IBindCtx *pbc, LPCITEMIDLIST pidlDrive, LPCIDDRIVE pidd, REFIID riid, void **ppv);
  115. static HRESULT _GetEditTextStrRet(LPCIDDRIVE pidd, STRRET *pstr);
  116. static BOOL _IsReg(LPCIDDRIVE pidd) { return pidd->bFlags == SHID_COMPUTER_REGITEM; }
  117. static HRESULT _GetIconOverlayInfo(LPCIDDRIVE pidd, int *pIndex, DWORD dwFlags);
  118. static CDrivesFolder* _spThis;
  119. private:
  120. friend HRESULT CDrives_CreateInstance(IUnknown* punkOuter, REFIID riid, void** ppv);
  121. friend void CDrives_Terminate(void);
  122. friend CDrivesViewCallback;
  123. friend class CDrivesFolderEnum;
  124. IUnknown* _punkReg;
  125. };
  126. #define DRIVES_EVENTS \
  127. SHCNE_DRIVEADD | \
  128. SHCNE_DRIVEREMOVED | \
  129. SHCNE_MEDIAINSERTED | \
  130. SHCNE_MEDIAREMOVED | \
  131. SHCNE_NETSHARE | \
  132. SHCNE_NETUNSHARE | \
  133. SHCNE_CREATE | \
  134. SHCNE_DELETE | \
  135. SHCNE_RENAMEITEM | \
  136. SHCNE_RENAMEFOLDER | \
  137. SHCNE_UPDATEITEM
  138. // return S_OK if non NULL CLSID copied out
  139. HRESULT CDrivesFolder::_GetCLSIDFromPidl(LPCIDDRIVE pidd, CLSID *pclsid)
  140. {
  141. *pclsid = CLSID_NULL;
  142. if ((pidd->cb >= sizeof(IDDRIVE)) &&
  143. ((pidd->wSig & IDDRIVE_ORDINAL_MASK) == IDDRIVE_ORDINAL_DRIVEEXT) &&
  144. (pidd->wSig & IDDRIVE_FLAGS_DRIVEEXT_HASCLSID))
  145. {
  146. *pclsid = pidd->clsid;
  147. return S_OK;
  148. }
  149. return S_FALSE; // does not have a CLSID
  150. }
  151. HRESULT CDrivesFolder::GetMaxNameLength(LPCITEMIDLIST pidlItem, UINT *pcchMax)
  152. {
  153. HRESULT hr = E_INVALIDARG;
  154. LPCIDDRIVE pidd = _IsValidID(pidlItem);
  155. if (pidd)
  156. {
  157. if (pidd->bFlags == SHID_COMPUTER_REGITEM)
  158. {
  159. // this is bogus, we are handling stuff for regfldr
  160. *pcchMax = MAX_REGITEMCCH;
  161. hr = S_OK;
  162. }
  163. else
  164. {
  165. CMountPoint* pMtPt = CMountPoint::GetMountPoint(DRIVEID(pidd->cName));
  166. if (pMtPt)
  167. {
  168. TCHAR szLabel[MAX_LABEL_NTFS + 1];
  169. hr = pMtPt->GetLabel(szLabel, ARRAYSIZE(szLabel));
  170. if (SUCCEEDED(hr))
  171. {
  172. if (pMtPt->IsNTFS())
  173. *pcchMax = MAX_LABEL_NTFS;
  174. else
  175. *pcchMax = MAX_LABEL_FAT;
  176. }
  177. pMtPt->Release();
  178. }
  179. }
  180. }
  181. return hr;
  182. }
  183. class CDrivesViewCallback : public CBaseShellFolderViewCB, public IFolderFilter
  184. {
  185. public:
  186. CDrivesViewCallback(CDrivesFolder *pfolder);
  187. STDMETHODIMP RealMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
  188. // IUnknown
  189. STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
  190. STDMETHODIMP_(ULONG) AddRef(void) { return CBaseShellFolderViewCB::AddRef(); };
  191. STDMETHODIMP_(ULONG) Release(void) { return CBaseShellFolderViewCB::Release(); };
  192. // IFolderFilter
  193. STDMETHODIMP ShouldShow(IShellFolder* psf, LPCITEMIDLIST pidlFolder, LPCITEMIDLIST pidlItem);
  194. STDMETHODIMP GetEnumFlags(IShellFolder* psf, LPCITEMIDLIST pidlFolder, HWND *phwnd, DWORD *pgrfFlags);
  195. private:
  196. ~CDrivesViewCallback();
  197. HRESULT OnMergeMenu(DWORD pv, QCMINFO*lP)
  198. {
  199. return S_OK;
  200. }
  201. HRESULT OnInsertItem(DWORD pv, LPCITEMIDLIST wP)
  202. {
  203. LPIDDRIVE pidd = (LPIDDRIVE)wP;
  204. if (pidd && pidd->bFlags != SHID_COMPUTER_REGITEM)
  205. {
  206. // clear the size info
  207. pidd->qwSize = pidd->qwFree = 0;
  208. }
  209. return S_OK;
  210. }
  211. HRESULT OnWindowCreated(DWORD pv, HWND wP)
  212. {
  213. InitializeStatus(_punkSite);
  214. return S_OK;
  215. }
  216. HRESULT OnSize(DWORD pv, UINT cx, UINT cy)
  217. {
  218. ResizeStatus(_punkSite, cx);
  219. return S_OK;
  220. }
  221. HRESULT OnGetPane(DWORD pv, LPARAM dwPaneID, DWORD *pdwPane)
  222. {
  223. if (PANE_ZONE == dwPaneID)
  224. *pdwPane = 2;
  225. return S_OK;
  226. }
  227. HRESULT OnDefViewMode(DWORD pv, FOLDERVIEWMODE* pfvm)
  228. {
  229. *pfvm = IsOS(OS_SERVERADMINUI) ? FVM_DETAILS : FVM_TILE;
  230. return S_OK;
  231. }
  232. HRESULT OnGetWebViewLayout(DWORD pv, UINT uViewMode, SFVM_WEBVIEW_LAYOUT_DATA* pData);
  233. HRESULT OnGetWebViewContent(DWORD pv, SFVM_WEBVIEW_CONTENT_DATA* pData);
  234. HRESULT OnGetWebViewTasks(DWORD pv, SFVM_WEBVIEW_TASKSECTION_DATA* pTasks);
  235. HRESULT OnUpdateStatusBar(DWORD pv, BOOL fIniting)
  236. {
  237. // Ask DefView to set the default text but not initialize
  238. // since we did the initialization in our OnSize handler.
  239. return SFVUSB_INITED;
  240. }
  241. HRESULT OnFSNotify(DWORD pv, LPCITEMIDLIST*wP, LPARAM lP)
  242. {
  243. return CDrivesFolder::_OnChangeNotify(lP, wP);
  244. }
  245. HRESULT OnBACKGROUNDENUM(DWORD pv)
  246. {
  247. return S_OK;
  248. }
  249. HRESULT OnGetCCHMax(DWORD pv, LPCITEMIDLIST pidlItem, UINT *pcchMax)
  250. {
  251. return _pfolder->GetMaxNameLength(pidlItem, pcchMax);
  252. }
  253. CDrivesFolder *_pfolder;
  254. LONG _cRef;
  255. public:
  256. // Web View Task implementations
  257. static HRESULT _CanEject(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState);
  258. static HRESULT _CanChangeSettings(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState);
  259. static HRESULT _CanSysProperties(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState);
  260. static HRESULT _CanAddRemovePrograms(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState);
  261. static HRESULT _OnSystemProperties(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc);
  262. static HRESULT _OnAddRemovePrograms(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc);
  263. static HRESULT _OnChangeSettings(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc);
  264. static HRESULT _OnEject(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc);
  265. };
  266. CDrivesViewCallback::CDrivesViewCallback(CDrivesFolder *pfolder) :
  267. CBaseShellFolderViewCB((LPCITEMIDLIST)&c_idlDrives, DRIVES_EVENTS), _pfolder(pfolder), _cRef(1)
  268. {
  269. _pfolder->AddRef();
  270. }
  271. CDrivesViewCallback::~CDrivesViewCallback()
  272. {
  273. _pfolder->Release();
  274. }
  275. STDMETHODIMP CDrivesViewCallback::RealMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
  276. {
  277. switch (uMsg)
  278. {
  279. HANDLE_MSG(0, SFVM_MERGEMENU, OnMergeMenu);
  280. HANDLE_MSG(0, SFVM_INSERTITEM, OnInsertItem);
  281. HANDLE_MSG(0, SFVM_UPDATESTATUSBAR, OnUpdateStatusBar);
  282. HANDLE_MSG(0, SFVM_FSNOTIFY, OnFSNotify);
  283. HANDLE_MSG(0, SFVM_BACKGROUNDENUM, OnBACKGROUNDENUM);
  284. HANDLE_MSG(0, SFVM_DEFVIEWMODE, OnDefViewMode);
  285. HANDLE_MSG(0, SFVM_GETWEBVIEWLAYOUT, OnGetWebViewLayout);
  286. HANDLE_MSG(0, SFVM_GETWEBVIEWCONTENT, OnGetWebViewContent);
  287. HANDLE_MSG(0, SFVM_GETWEBVIEWTASKS, OnGetWebViewTasks);
  288. HANDLE_MSG(0, SFVM_ADDPROPERTYPAGES, SFVCB_OnAddPropertyPages);
  289. HANDLE_MSG(0, SFVM_WINDOWCREATED, OnWindowCreated);
  290. HANDLE_MSG(0, SFVM_SIZE, OnSize);
  291. HANDLE_MSG(0, SFVM_GETPANE, OnGetPane);
  292. HANDLE_MSG(0, SFVM_GETCCHMAX, OnGetCCHMax);
  293. default:
  294. return E_FAIL;
  295. }
  296. return S_OK;
  297. }
  298. HRESULT CDrivesViewCallback::QueryInterface(REFIID riid, void **ppv)
  299. {
  300. HRESULT hr = CBaseShellFolderViewCB::QueryInterface(riid, ppv);
  301. if (FAILED(hr))
  302. {
  303. static const QITAB qit[] = {
  304. QITABENT(CDrivesViewCallback, IFolderFilter),
  305. { 0 },
  306. };
  307. hr = QISearch(this, qit, riid, ppv);
  308. }
  309. return hr;
  310. }
  311. STDMETHODIMP CDrivesViewCallback::ShouldShow(IShellFolder* psf, LPCITEMIDLIST pidlFolder, LPCITEMIDLIST pidlItem)
  312. {
  313. HRESULT hr = S_OK; //Assume that this item should be shown!
  314. if (SHRestricted(REST_NOMYCOMPUTERICON)) // this policy means hide my computer everywhere AND hide the contents if the user is sneaky and gets in anyway
  315. {
  316. hr = S_FALSE;
  317. }
  318. else
  319. {
  320. IShellFolder2 *psf2;
  321. if (SUCCEEDED(psf->QueryInterface(IID_PPV_ARG(IShellFolder2, &psf2))))
  322. {
  323. // Get the GUID in the pidl, which requires IShellFolder2.
  324. CLSID guidItem;
  325. if (SUCCEEDED(GetItemCLSID(psf2, pidlItem, &guidItem)))
  326. {
  327. //Convert the guid to a string
  328. TCHAR szGuidValue[MAX_GUID_STRING_LEN];
  329. SHStringFromGUID(guidItem, szGuidValue, ARRAYSIZE(szGuidValue));
  330. //See if this item is turned off in the registry.
  331. if (SHRegGetBoolUSValue(REGSTR_PATH_HIDDEN_MYCOMP_ICONS, szGuidValue, FALSE, /* default */FALSE))
  332. hr = S_FALSE; //They want to hide it; So, return S_FALSE.
  333. }
  334. psf2->Release();
  335. }
  336. }
  337. return hr;
  338. }
  339. STDMETHODIMP CDrivesViewCallback::GetEnumFlags(IShellFolder* psf, LPCITEMIDLIST pidlFolder, HWND *phwnd, DWORD *pgrfFlags)
  340. {
  341. return E_NOTIMPL;
  342. }
  343. HRESULT CDrivesViewCallback::OnGetWebViewLayout(DWORD pv, UINT uViewMode, SFVM_WEBVIEW_LAYOUT_DATA* pData)
  344. {
  345. SecureZeroMemory(pData, sizeof(*pData));
  346. pData->dwLayout = SFVMWVL_DETAILS;
  347. return S_OK;
  348. }
  349. HRESULT CDrivesViewCallback::_CanAddRemovePrograms(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState)
  350. {
  351. *puisState = (SHRestricted(REST_ARP_NOARP)) ? UIS_DISABLED : UIS_ENABLED;
  352. return S_OK;
  353. }
  354. // Note:
  355. // This method is NOT designed to handle multi-select cases. If you enhance
  356. // the task list and wish to multi-eject (?why?), make sure you fix this up!
  357. //
  358. HRESULT CDrivesViewCallback::_CanEject(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState)
  359. {
  360. *puisState = UIS_DISABLED;
  361. IDataObject *pdo;
  362. // should just use the ShellItemArray directly
  363. if (psiItemArray && SUCCEEDED(psiItemArray->BindToHandler(NULL,BHID_DataObject,IID_PPV_ARG(IDataObject,&pdo))))
  364. {
  365. STGMEDIUM medium;
  366. LPIDA pida = DataObj_GetHIDA(pdo, &medium);
  367. if (pida)
  368. {
  369. ASSERT(pida->cidl == 1); // Only allow eject if a single item is selected.
  370. LPCIDDRIVE pidd = CDrivesFolder::_IsValidID(IDA_GetIDListPtr(pida, 0));
  371. if (pidd)
  372. {
  373. CMountPoint* pmtpt = CMountPoint::GetMountPoint(DRIVEID(pidd->cName));
  374. if (pmtpt)
  375. {
  376. if (pmtpt->IsEjectable())
  377. *puisState = UIS_ENABLED;
  378. pmtpt->Release();
  379. }
  380. }
  381. HIDA_ReleaseStgMedium(pida, &medium);
  382. }
  383. pdo->Release();
  384. }
  385. return S_OK;
  386. }
  387. HRESULT CDrivesViewCallback::_CanSysProperties(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState)
  388. {
  389. *puisState = SHRestricted(REST_MYCOMPNOPROP) ? UIS_DISABLED : UIS_ENABLED;
  390. return S_OK;
  391. }
  392. HRESULT CDrivesViewCallback::_OnSystemProperties(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc)
  393. {
  394. CDrivesViewCallback* pThis = (CDrivesViewCallback*)(void*)pv;
  395. return SHInvokeCommandOnPidl(pThis->_hwndMain, NULL, pThis->_pidl, 0, "properties");
  396. }
  397. HRESULT CDrivesViewCallback::_OnAddRemovePrograms(IUnknown* pv,IShellItemArray *psiItemArray, IBindCtx *pbc)
  398. {
  399. BOOL fRet = SHRunControlPanel(L"appwiz.cpl", NULL);
  400. return (fRet) ? S_OK : E_FAIL;
  401. }
  402. HRESULT CDrivesViewCallback::_CanChangeSettings(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState)
  403. {
  404. *puisState = SHRestricted(REST_NOCONTROLPANEL) ? UIS_DISABLED : UIS_ENABLED;
  405. return S_OK;
  406. }
  407. HRESULT CDrivesViewCallback::_OnChangeSettings(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc)
  408. {
  409. CDrivesViewCallback* pThis = (CDrivesViewCallback*)(void*)pv;
  410. IShellBrowser* psb;
  411. HRESULT hr = IUnknown_QueryService(pThis->_punkSite, SID_STopLevelBrowser, IID_PPV_ARG(IShellBrowser, &psb));
  412. if (SUCCEEDED(hr))
  413. {
  414. LPITEMIDLIST pidl;
  415. hr = SHGetFolderLocation(NULL, CSIDL_CONTROLS, NULL, 0, &pidl);
  416. if (SUCCEEDED(hr))
  417. {
  418. hr = psb->BrowseObject(pidl, 0);
  419. ILFree(pidl);
  420. }
  421. psb->Release();
  422. }
  423. return hr;
  424. }
  425. HRESULT CDrivesViewCallback::_OnEject(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc)
  426. {
  427. CDrivesViewCallback* pThis = (CDrivesViewCallback*)(void*)pv;
  428. IDataObject *pdo;
  429. HRESULT hr = E_FAIL;
  430. if (psiItemArray && SUCCEEDED(psiItemArray->BindToHandler(NULL,BHID_DataObject,IID_PPV_ARG(IDataObject,&pdo))))
  431. {
  432. hr = SHInvokeCommandOnDataObject(pThis->_hwndMain, NULL, pdo, 0, "eject");
  433. pdo->Release();
  434. }
  435. return hr;
  436. }
  437. const WVTASKITEM c_MyComputerTaskHeader = WVTI_HEADER(L"shell32.dll", IDS_HEADER_MYCOMPUTER, IDS_HEADER_MYCOMPUTER_TT);
  438. const WVTASKITEM c_MyComputerTaskList[] =
  439. {
  440. WVTI_ENTRY_ALL(CLSID_NULL, L"shell32.dll", IDS_TASK_MYCOMPUTER_SYSTEMPROPERTIES, IDS_TASK_MYCOMPUTER_SYSTEMPROPERTIES_TT, IDI_TASK_PROPERTIES,CDrivesViewCallback::_CanSysProperties, CDrivesViewCallback::_OnSystemProperties),
  441. WVTI_ENTRY_ALL(UICID_AddRemovePrograms, L"shell32.dll", IDS_TASK_ARP, IDS_TASK_ARP_TT, IDI_CPCAT_ARP, CDrivesViewCallback::_CanAddRemovePrograms, CDrivesViewCallback::_OnAddRemovePrograms),
  442. WVTI_ENTRY_ALL(CLSID_NULL, L"shell32.dll", IDS_TASK_CHANGESETTINGS, IDS_TASK_CHANGESETTINGS_TT, IDI_CPLFLD, CDrivesViewCallback::_CanChangeSettings,CDrivesViewCallback::_OnChangeSettings),
  443. WVTI_ENTRY_TITLE(CLSID_NULL, L"shell32.dll", 0, IDS_TASK_EJECTDISK, 0, IDS_TASK_EJECTDISK_TT, IDI_STEJECT, CDrivesViewCallback::_CanEject, CDrivesViewCallback::_OnEject),
  444. };
  445. HRESULT CDrivesViewCallback::OnGetWebViewContent(DWORD pv, SFVM_WEBVIEW_CONTENT_DATA* pData)
  446. {
  447. SecureZeroMemory(pData, sizeof(*pData));
  448. Create_IUIElement(&c_MyComputerTaskHeader, &(pData->pFolderTaskHeader));
  449. // My Computer wants a different order than the default,
  450. // and it doesn't want to expose "Desktop" as a place to go
  451. LPCTSTR rgCSIDLs[] = { MAKEINTRESOURCE(CSIDL_NETWORK), MAKEINTRESOURCE(CSIDL_PERSONAL), MAKEINTRESOURCE(CSIDL_COMMON_DOCUMENTS), MAKEINTRESOURCE(CSIDL_CONTROLS) };
  452. CreateIEnumIDListOnCSIDLs(NULL, rgCSIDLs, ARRAYSIZE(rgCSIDLs), &(pData->penumOtherPlaces));
  453. return S_OK;
  454. }
  455. HRESULT CDrivesViewCallback::OnGetWebViewTasks(DWORD pv, SFVM_WEBVIEW_TASKSECTION_DATA* pTasks)
  456. {
  457. SecureZeroMemory(pTasks, sizeof(*pTasks));
  458. Create_IEnumUICommand((IUnknown*)(void*)this, c_MyComputerTaskList, ARRAYSIZE(c_MyComputerTaskList), &pTasks->penumFolderTasks);
  459. return S_OK;
  460. }
  461. STDAPI_(IShellFolderViewCB*) CDrives_CreateSFVCB(CDrivesFolder *pfolder)
  462. {
  463. return new CDrivesViewCallback(pfolder);
  464. }
  465. typedef struct
  466. {
  467. DWORD dwDrivesMask;
  468. int nLastFoundDrive;
  469. DWORD dwRestricted;
  470. DWORD dwSavedErrorMode;
  471. DWORD grfFlags;
  472. } EnumDrives;
  473. typedef enum
  474. {
  475. DRIVES_ICOL_NAME = 0,
  476. DRIVES_ICOL_TYPE,
  477. DRIVES_ICOL_CAPACITY,
  478. DRIVES_ICOL_FREE,
  479. DRIVES_ICOL_FILESYSTEM,
  480. DRIVES_ICOL_COMMENT,
  481. };
  482. const COLUMN_INFO c_drives_cols[] =
  483. {
  484. DEFINE_COL_STR_ENTRY(SCID_NAME, 20, IDS_NAME_COL),
  485. DEFINE_COL_STR_ENTRY(SCID_TYPE, 25, IDS_TYPE_COL),
  486. DEFINE_COL_SIZE_ENTRY(SCID_CAPACITY, IDS_DRIVES_CAPACITY),
  487. DEFINE_COL_SIZE_ENTRY(SCID_FREESPACE, IDS_DRIVES_FREE),
  488. DEFINE_COL_STR_MENU_ENTRY(SCID_FILESYSTEM, 15, IDS_DRIVES_FILESYSTEM),
  489. DEFINE_COL_STR_ENTRY(SCID_Comment, 20, IDS_EXCOL_COMMENT),
  490. };
  491. CDrivesFolder* CDrivesFolder::_spThis = NULL;
  492. HRESULT CDrives_CreateInstance(IUnknown* punkOuter, REFIID riid, void** ppv)
  493. {
  494. HRESULT hr;
  495. ASSERT(NULL != ppv);
  496. ENTERCRITICAL;
  497. if (NULL != CDrivesFolder::_spThis)
  498. {
  499. hr = CDrivesFolder::_spThis->QueryInterface(riid, ppv);
  500. LEAVECRITICAL;
  501. }
  502. else
  503. {
  504. LEAVECRITICAL;
  505. CDrivesFolder* pDF = new CDrivesFolder(punkOuter);
  506. if (NULL != pDF)
  507. {
  508. ASSERT(NULL == pDF->_punkReg);
  509. if (SHRestricted(REST_NOCONTROLPANEL) || SHRestricted(REST_NOSETFOLDERS))
  510. g_asDrivesReqItems[CDRIVES_REGITEM_CONTROL].dwAttributes |= SFGAO_NONENUMERATED;
  511. REGITEMSINFO sDrivesRegInfo =
  512. {
  513. REGSTR_PATH_EXPLORER TEXT("\\MyComputer\\NameSpace"),
  514. NULL,
  515. TEXT(':'),
  516. SHID_COMPUTER_REGITEM,
  517. -1,
  518. SFGAO_CANLINK,
  519. ARRAYSIZE(g_asDrivesReqItems),
  520. g_asDrivesReqItems,
  521. RIISA_ORIGINAL,
  522. NULL,
  523. 0,
  524. 0,
  525. };
  526. CRegFolder_CreateInstance(&sDrivesRegInfo,
  527. (IUnknown*)(IShellFolder2*) pDF,
  528. IID_PPV_ARG(IUnknown, &pDF->_punkReg));
  529. if (SHInterlockedCompareExchange((void**) &CDrivesFolder::_spThis, pDF, NULL))
  530. {
  531. // Someone else snuck in and initialized a CDrivesFolder first,
  532. // so release our object and then recurse so we should get the other instance
  533. pDF->Release();
  534. hr = CDrives_CreateInstance(punkOuter, riid, ppv);
  535. }
  536. else
  537. {
  538. hr = pDF->QueryInterface(riid, ppv);
  539. // release the self-reference, but keep _spThis intact
  540. // (it will be reset to NULL in the destructor)
  541. pDF->Release();
  542. }
  543. }
  544. else
  545. {
  546. hr = E_OUTOFMEMORY;
  547. *ppv = NULL;
  548. }
  549. }
  550. return hr;
  551. }
  552. // This should only be called during process detach
  553. void CDrives_Terminate(void)
  554. {
  555. if (NULL != CDrivesFolder::_spThis)
  556. {
  557. delete CDrivesFolder::_spThis;
  558. }
  559. }
  560. CDrivesFolder::CDrivesFolder(IUnknown* punkOuter) :
  561. CAggregatedUnknown (punkOuter),
  562. _punkReg (NULL)
  563. {
  564. DllAddRef();
  565. }
  566. CDrivesFolder::~CDrivesFolder()
  567. {
  568. SHReleaseInnerInterface(SAFECAST(this, IShellFolder *), &_punkReg);
  569. SHInterlockedCompareExchange((void**) &CDrivesFolder::_spThis, NULL, this);
  570. DllRelease();
  571. }
  572. HRESULT CDrivesFolder::v_InternalQueryInterface(REFIID riid, void** ppv)
  573. {
  574. static const QITAB qit[] = {
  575. QITABENT(CDrivesFolder, IShellFolder2), // IID_IShellFolder2
  576. QITABENTMULTI(CDrivesFolder, IShellFolder, IShellFolder2), // IID_IShellFolder
  577. QITABENT(CDrivesFolder, IPersistFolder2), // IID_IPersistFolder2
  578. QITABENTMULTI(CDrivesFolder, IPersistFolder, IPersistFolder2), // IID_IPersistFolder
  579. QITABENTMULTI(CDrivesFolder, IPersist, IPersistFolder2), // IID_IPersist
  580. QITABENTMULTI2(CDrivesFolder, IID_IPersistFreeThreadedObject, IPersist), // IID_IPersistFreeThreadedObject
  581. QITABENT(CDrivesFolder, IShellIconOverlay), // IID_IShellIconOverlay
  582. { 0 },
  583. };
  584. HRESULT hr;
  585. if (_punkReg && RegGetsFirstShot(riid))
  586. {
  587. hr = _punkReg->QueryInterface(riid, ppv);
  588. }
  589. else
  590. {
  591. hr = QISearch(this, qit, riid, ppv);
  592. if ((E_NOINTERFACE == hr) && _punkReg)
  593. {
  594. hr = _punkReg->QueryInterface(riid, ppv);
  595. }
  596. }
  597. return hr;
  598. }
  599. BOOL CDrivesFolder::v_HandleDelete(PLONG pcRef)
  600. {
  601. ASSERT(NULL != pcRef);
  602. ENTERCRITICAL;
  603. //
  604. // The same bad thing can happen here as in
  605. // CNetRootFolder::v_HandleDelete. See that function for gory details.
  606. //
  607. if (this == _spThis && 0 == *pcRef)
  608. {
  609. *pcRef = 1000; // protect against cached pointers bumping us up then down
  610. delete this;
  611. }
  612. LEAVECRITICAL;
  613. // return TRUE to indicate that we've implemented this function
  614. // (regardless of whether or not this object was actually deleted)
  615. return TRUE;
  616. }
  617. HRESULT CDrivesFolder::_GetDisplayName(LPCIDDRIVE pidd, LPTSTR pszName, UINT cchMax)
  618. {
  619. HRESULT hr = E_FAIL;
  620. CMountPoint* pMtPt = CMountPoint::GetMountPoint(DRIVEID(pidd->cName));
  621. if (pMtPt)
  622. {
  623. hr = pMtPt->GetDisplayName(pszName, cchMax);
  624. pMtPt->Release();
  625. }
  626. return hr;
  627. }
  628. HRESULT CDrivesFolder::_GetDisplayNameStrRet(LPCIDDRIVE pidd, STRRET *pStrRet)
  629. {
  630. HRESULT hr = E_FAIL;
  631. CMountPoint* pMtPt = CMountPoint::GetMountPoint(DRIVEID(pidd->cName));
  632. if (pMtPt)
  633. {
  634. TCHAR szName[MAX_DISPLAYNAME];
  635. hr = pMtPt->GetDisplayName(szName, ARRAYSIZE(szName));
  636. if (SUCCEEDED(hr))
  637. hr = StringToStrRet(szName, pStrRet);
  638. pMtPt->Release();
  639. }
  640. return hr;
  641. }
  642. #define REGKEY_DRIVE_FOLDEREXT L"Drive\\shellex\\FolderExtensions"
  643. HRESULT CDrivesFolder::_CheckDriveType(int iDrive, LPCTSTR pszCLSID)
  644. {
  645. HRESULT hr = E_FAIL;
  646. TCHAR szKey[MAX_PATH];
  647. StringCchCopy(szKey, ARRAYSIZE(szKey), REGKEY_DRIVE_FOLDEREXT L"\\");
  648. StringCchCat(szKey, ARRAYSIZE(szKey), pszCLSID);
  649. DWORD dwDriveMask;
  650. DWORD cb = sizeof(DWORD);
  651. if (ERROR_SUCCESS == SHGetValue(HKEY_CLASSES_ROOT, szKey, L"DriveMask", NULL, &dwDriveMask, &cb))
  652. {
  653. TCHAR szDrive[4];
  654. if (PathBuildRoot(szDrive, iDrive))
  655. {
  656. int iType = GetDriveType(szDrive);
  657. // its possible that we're asked to parse a drive that's no longer mounted,
  658. // so GetDriveType will fail with DRIVE_NO_ROOT_DIR.
  659. // in that case, pass it on down to the handler anyway.
  660. // let's say it's the handler's job to remember the last drive it matched on.
  661. if ((DRIVE_NO_ROOT_DIR == iType) || ((1 << iType) & dwDriveMask))
  662. {
  663. hr = S_OK;
  664. }
  665. }
  666. }
  667. return hr;
  668. }
  669. HRESULT CDrivesFolder::_FindExtCLSID(int iDrive, CLSID *pclsid)
  670. {
  671. *pclsid = CLSID_NULL;
  672. HRESULT hr = E_FAIL;
  673. HKEY hk;
  674. if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CLASSES_ROOT, REGKEY_DRIVE_FOLDEREXT, 0, KEY_ENUMERATE_SUB_KEYS, &hk))
  675. {
  676. TCHAR szCLSID[MAX_GUID_STRING_LEN];
  677. for (int i = 0; FAILED(hr) && (ERROR_SUCCESS == RegEnumKey(hk, i, szCLSID, ARRAYSIZE(szCLSID))); i++)
  678. {
  679. IDriveFolderExt *pdfe;
  680. if (SUCCEEDED(_CheckDriveType(iDrive, szCLSID)) &&
  681. SUCCEEDED(SHExtCoCreateInstance(szCLSID, NULL, NULL, IID_PPV_ARG(IDriveFolderExt, &pdfe))))
  682. {
  683. if (SUCCEEDED(pdfe->DriveMatches(iDrive)))
  684. {
  685. SHCLSIDFromString(szCLSID, pclsid);
  686. }
  687. pdfe->Release();
  688. }
  689. // if we successfully matched one, break out.
  690. if (!IsEqualCLSID(*pclsid, CLSID_NULL))
  691. hr = S_OK;
  692. }
  693. RegCloseKey(hk);
  694. }
  695. return hr;
  696. }
  697. // this is called from parse and enum, both times passing a stack var into piddl.
  698. // we reset the cb manually and then the callers will do ILClone to allocate the correct amount
  699. // of memory.
  700. HRESULT CDrivesFolder::_FillIDDrive(DRIVE_IDLIST *piddl, int iDrive, BOOL fNoCLSID, IBindCtx* pbc)
  701. {
  702. HRESULT hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
  703. BOOL fDoIt = FALSE;
  704. SecureZeroMemory(piddl, sizeof(*piddl));
  705. PathBuildRootA(piddl->idd.cName, iDrive);
  706. if (S_OK == SHIsFileSysBindCtx(pbc, NULL))
  707. {
  708. fDoIt = TRUE;
  709. }
  710. else
  711. {
  712. if (BindCtx_GetMode(pbc, 0) & STGM_CREATE)
  713. {
  714. fDoIt = TRUE;
  715. }
  716. else
  717. {
  718. CMountPoint* pmtpt = CMountPoint::GetMountPoint(iDrive, FALSE);
  719. if (pmtpt)
  720. {
  721. fDoIt = TRUE;
  722. pmtpt->Release();
  723. }
  724. }
  725. }
  726. if (fDoIt)
  727. {
  728. // start the cb as the IDDRIVE less the clsid at the end
  729. // this is so that in the usual case when we dont have a clsid, the pidl will look
  730. // just like all our pidls on win2k.
  731. piddl->idd.cb = FIELD_OFFSET(IDDRIVE, clsid);
  732. piddl->idd.bFlags = SHID_COMPUTER_MISC;
  733. if (!fNoCLSID)
  734. {
  735. CLSID clsid;
  736. if (SUCCEEDED(_FindExtCLSID(iDrive, &clsid)))
  737. {
  738. piddl->idd.clsid = clsid;
  739. // boost the cb to include the whole thing
  740. piddl->idd.cb = sizeof(IDDRIVE);
  741. // mark the flags of the pidl to say "hey im a drive extension with a clsid"
  742. piddl->idd.wSig = IDDRIVE_ORDINAL_DRIVEEXT | IDDRIVE_FLAGS_DRIVEEXT_HASCLSID;
  743. }
  744. }
  745. hr = S_OK;
  746. }
  747. ASSERT(piddl->cbNext == 0);
  748. return hr;
  749. }
  750. STDMETHODIMP CDrivesFolder::ParseDisplayName(HWND hwnd, IBindCtx *pbc, LPOLESTR pwzDisplayName,
  751. ULONG* pchEaten, LPITEMIDLIST* ppidlOut, ULONG* pdwAttributes)
  752. {
  753. HRESULT hr = E_INVALIDARG;
  754. if (ppidlOut)
  755. {
  756. *ppidlOut = NULL; // assume error
  757. if (pwzDisplayName && pwzDisplayName[0] &&
  758. pwzDisplayName[1] == TEXT(':') && pwzDisplayName[2] == TEXT('\\'))
  759. {
  760. DRIVE_IDLIST idlDrive;
  761. if (InRange(*pwzDisplayName, 'a', 'z') ||
  762. InRange(*pwzDisplayName, 'A', 'Z'))
  763. {
  764. hr = _FillIDDrive(&idlDrive, DRIVEID(pwzDisplayName), SHSkipJunctionBinding(pbc, NULL), pbc);
  765. }
  766. if (SUCCEEDED(hr))
  767. {
  768. // Check if there are any subdirs
  769. if (pwzDisplayName[3])
  770. {
  771. IShellFolder *psfDrive;
  772. hr = BindToObject((LPITEMIDLIST)&idlDrive, pbc, IID_PPV_ARG(IShellFolder, &psfDrive));
  773. if (SUCCEEDED(hr))
  774. {
  775. ULONG chEaten;
  776. LPITEMIDLIST pidlDir;
  777. hr = psfDrive->ParseDisplayName(hwnd, pbc, pwzDisplayName + 3,
  778. &chEaten, &pidlDir, pdwAttributes);
  779. if (SUCCEEDED(hr))
  780. {
  781. hr = SHILCombine((LPCITEMIDLIST)&idlDrive, pidlDir, ppidlOut);
  782. SHFree(pidlDir);
  783. }
  784. psfDrive->Release();
  785. }
  786. }
  787. else
  788. {
  789. hr = SHILClone((LPITEMIDLIST)&idlDrive, ppidlOut);
  790. if (pdwAttributes && *pdwAttributes)
  791. GetAttributesOf(1, (LPCITEMIDLIST *)ppidlOut, pdwAttributes);
  792. }
  793. }
  794. }
  795. }
  796. if (FAILED(hr))
  797. TraceMsg(TF_WARNING, "CDrivesFolder::ParseDisplayName(), hr:%x %ls", hr, pwzDisplayName);
  798. return hr;
  799. }
  800. BOOL IsShareable(int iDrive)
  801. {
  802. return !IsRemoteDrive(iDrive);
  803. }
  804. class CDrivesFolderEnum : public CEnumIDListBase
  805. {
  806. public:
  807. // IEnumIDList
  808. STDMETHOD(Next)(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched);
  809. private:
  810. CDrivesFolderEnum(CDrivesFolder *psf, DWORD grfFlags);
  811. ~CDrivesFolderEnum();
  812. friend HRESULT Create_DrivesFolderEnum(CDrivesFolder* psf, DWORD grfFlags, IEnumIDList** ppenum);
  813. CDrivesFolder *_pdsf; // CDrivesFolder object we're enumerating
  814. DWORD _dwDrivesMask;
  815. int _nLastFoundDrive;
  816. DWORD _dwRestricted;
  817. DWORD _dwSavedErrorMode;
  818. DWORD _grfFlags;
  819. };
  820. CDrivesFolderEnum::CDrivesFolderEnum(CDrivesFolder *pdsf, DWORD grfFlags) : CEnumIDListBase()
  821. {
  822. _pdsf = pdsf;
  823. _pdsf->AddRef();
  824. _dwDrivesMask = CMountPoint::GetDrivesMask();
  825. _nLastFoundDrive = -1;
  826. _dwRestricted = SHRestricted(REST_NODRIVES);
  827. _dwSavedErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
  828. _grfFlags = grfFlags;
  829. }
  830. HRESULT Create_DrivesFolderEnum(CDrivesFolder *psf, DWORD grfFlags, IEnumIDList** ppenum)
  831. {
  832. HRESULT hr;
  833. CDrivesFolderEnum* p= new CDrivesFolderEnum(psf, grfFlags);
  834. if (p)
  835. {
  836. hr = p->QueryInterface(IID_PPV_ARG(IEnumIDList, ppenum));
  837. p->Release();
  838. }
  839. else
  840. {
  841. hr = E_OUTOFMEMORY;
  842. *ppenum = NULL;
  843. }
  844. return hr;
  845. }
  846. CDrivesFolderEnum::~CDrivesFolderEnum()
  847. {
  848. _pdsf->Release(); // release the "this" ptr we have
  849. }
  850. STDMETHODIMP CDrivesFolderEnum::Next(ULONG celt, LPITEMIDLIST *ppidl, ULONG *pceltFetched)
  851. {
  852. HRESULT hr = S_FALSE; // assume "no more element"
  853. LPITEMIDLIST pidl = NULL;
  854. for (int iDrive = _nLastFoundDrive + 1; iDrive < 26; iDrive++)
  855. {
  856. if (_dwRestricted & (1 << iDrive))
  857. {
  858. TraceMsg(DM_TRACE, "s.cd_ecb: Drive %d restricted.", iDrive);
  859. }
  860. else if ((_dwDrivesMask & (1 << iDrive)) || IsUnavailableNetDrive(iDrive))
  861. {
  862. if (!(SHCONTF_SHAREABLE & _grfFlags) || IsShareable(iDrive))
  863. {
  864. DRIVE_IDLIST iddrive;
  865. hr = _pdsf->_FillIDDrive(&iddrive, iDrive, FALSE, NULL);
  866. if (SUCCEEDED(hr))
  867. {
  868. hr = SHILClone((LPITEMIDLIST)&iddrive, &pidl);
  869. if (SUCCEEDED(hr))
  870. {
  871. CMountPoint* pmtpt = CMountPoint::GetMountPoint(iDrive, FALSE);
  872. if (pmtpt)
  873. {
  874. pmtpt->ChangeNotifyRegisterAlias();
  875. pmtpt->Release();
  876. }
  877. _nLastFoundDrive = iDrive;
  878. }
  879. break;
  880. }
  881. else
  882. {
  883. hr = S_FALSE;
  884. }
  885. }
  886. }
  887. }
  888. *ppidl = pidl;
  889. if (pceltFetched)
  890. *pceltFetched = (S_OK == hr) ? 1 : 0;
  891. return hr;
  892. }
  893. STDMETHODIMP CDrivesFolder::EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList ** ppenum)
  894. {
  895. return Create_DrivesFolderEnum(this, grfFlags, ppenum);
  896. }
  897. LPCIDDRIVE CDrivesFolder::_IsValidID(LPCITEMIDLIST pidl)
  898. {
  899. if (pidl && (SIL_GetType(pidl) & SHID_GROUPMASK) == SHID_COMPUTER)
  900. return (LPCIDDRIVE)pidl;
  901. return NULL;
  902. }
  903. HRESULT CDrivesFolder::_CreateFSFolderObj(IBindCtx *pbc, LPCITEMIDLIST pidlDrive, LPCIDDRIVE pidd, REFIID riid, void **ppv)
  904. {
  905. PERSIST_FOLDER_TARGET_INFO pfti = {0};
  906. pfti.pidlTargetFolder = (LPITEMIDLIST)pidlDrive;
  907. SHAnsiToUnicode(pidd->cName, pfti.szTargetParsingName, ARRAYSIZE(pfti.szTargetParsingName));
  908. pfti.dwAttributes = FILE_ATTRIBUTE_DIRECTORY; // maybe add system?
  909. pfti.csidl = -1;
  910. return CFSFolder_CreateFolder(NULL, pbc, pidlDrive, &pfti, riid, ppv);
  911. }
  912. HRESULT CDrivesFolder::_CreateFSFolder(IBindCtx *pbc, LPCITEMIDLIST pidlDrive, LPCIDDRIVE pidd, REFIID riid, void **ppv)
  913. {
  914. HRESULT hr;
  915. CLSID clsid;
  916. if (S_OK == _GetCLSIDFromPidl(pidd, &clsid) && (!SHSkipJunctionBinding(pbc, NULL)))
  917. {
  918. IDriveFolderExt *pdfe;
  919. // SHExtCoCreateInstance since this shell extension needs to go through approval
  920. hr = SHExtCoCreateInstance(NULL, &clsid, NULL, IID_PPV_ARG(IDriveFolderExt, &pdfe));
  921. if (SUCCEEDED(hr))
  922. {
  923. hr = pdfe->Bind(pidlDrive, pbc, riid, ppv);
  924. pdfe->Release();
  925. }
  926. }
  927. else
  928. {
  929. hr = _CreateFSFolderObj(pbc, pidlDrive, pidd, riid, ppv);
  930. }
  931. return hr;
  932. }
  933. STDMETHODIMP CDrivesFolder::BindToObject(LPCITEMIDLIST pidl, IBindCtx *pbc, REFIID riid, void** ppv)
  934. {
  935. HRESULT hr;
  936. *ppv = NULL;
  937. LPCIDDRIVE pidd = _IsValidID(pidl);
  938. if (pidd)
  939. {
  940. LPCITEMIDLIST pidlNext = _ILNext(pidl);
  941. LPITEMIDLIST pidlDrive = ILCombineParentAndFirst(IDLIST_DRIVES, pidl, pidlNext);
  942. if (pidlDrive)
  943. {
  944. // we only try ask for the riid at the end of the pidl binding.
  945. if (ILIsEmpty(pidlNext))
  946. {
  947. hr = _CreateFSFolder(pbc, pidlDrive, pidd, riid, ppv);
  948. }
  949. else
  950. {
  951. // now we need to get the subfolder from which to grab our goodies
  952. IShellFolder *psfDrive;
  953. hr = _CreateFSFolder(pbc, pidlDrive, pidd, IID_PPV_ARG(IShellFolder, &psfDrive));
  954. if (SUCCEEDED(hr))
  955. {
  956. // this means that there is more to bind to, we must pass it on...
  957. hr = psfDrive->BindToObject(pidlNext, pbc, riid, ppv);
  958. psfDrive->Release();
  959. }
  960. }
  961. ILFree(pidlDrive);
  962. }
  963. else
  964. hr = E_OUTOFMEMORY;
  965. }
  966. else
  967. {
  968. hr = E_INVALIDARG;
  969. TraceMsg(TF_WARNING, "CDrivesFolder::BindToObject(), bad PIDL %s", DumpPidl(pidl));
  970. }
  971. return hr;
  972. }
  973. STDMETHODIMP CDrivesFolder::BindToStorage(LPCITEMIDLIST pidl, IBindCtx *pbc, REFIID riid, void** ppv)
  974. {
  975. return BindToObject(pidl, pbc, riid, ppv);
  976. }
  977. BOOL CDrivesFolder::_GetFreeSpace(LPCIDDRIVE pidd, ULONGLONG *pSize, ULONGLONG *pFree)
  978. {
  979. BOOL bRet = FALSE;
  980. CLSID clsid;
  981. if (S_OK == _GetCLSIDFromPidl(pidd, &clsid))
  982. {
  983. IDriveFolderExt *pdfe;
  984. // SHExtCoCreateInstance since this shell extension needs to go through approval
  985. if (SUCCEEDED(SHExtCoCreateInstance(NULL, &clsid, NULL, IID_PPV_ARG(IDriveFolderExt, &pdfe))))
  986. {
  987. bRet = SUCCEEDED(pdfe->GetSpace(pSize, pFree));
  988. pdfe->Release();
  989. }
  990. }
  991. if (!bRet && !_IsReg(pidd) && ShowDriveInfo(DRIVEID(pidd->cName)))
  992. {
  993. if (pidd->qwSize || pidd->qwFree)
  994. {
  995. *pSize = pidd->qwSize; // cache hit
  996. *pFree = pidd->qwFree;
  997. bRet = TRUE;
  998. }
  999. else
  1000. {
  1001. int iDrive = DRIVEID(pidd->cName);
  1002. // Don't wake up sleeping net connections
  1003. if (!IsRemoteDrive(iDrive) || !IsDisconnectedNetDrive(iDrive))
  1004. {
  1005. // Call our helper function Who understands
  1006. // OSR2 and NT as well as old W95...
  1007. ULARGE_INTEGER qwFreeUser, qwTotal, qwTotalFree;
  1008. bRet = SHGetDiskFreeSpaceExA(pidd->cName, &qwFreeUser, &qwTotal, &qwTotalFree);
  1009. if (bRet)
  1010. {
  1011. *pSize = qwTotal.QuadPart;
  1012. *pFree = qwFreeUser.QuadPart;
  1013. }
  1014. }
  1015. }
  1016. }
  1017. return bRet;
  1018. }
  1019. STDMETHODIMP CDrivesFolder::CompareIDs(LPARAM iCol, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  1020. {
  1021. LPCIDDRIVE pidd1 = _IsValidID(pidl1);
  1022. LPCIDDRIVE pidd2 = _IsValidID(pidl2);
  1023. if (!pidd1 || !pidd2)
  1024. {
  1025. TraceMsg(TF_WARNING, "CDrivesFolder::CompareIDs(), bad(s) pidl11:%s, pidl2:%s", DumpPidl(pidl1), DumpPidl(pidl2));
  1026. return E_INVALIDARG;
  1027. }
  1028. // For any column other than DRIVES_ICOL_NAME, we force an
  1029. // all-fields comparison to break ties.
  1030. if ((iCol & SHCIDS_COLUMNMASK) != DRIVES_ICOL_NAME)
  1031. iCol |= SHCIDS_ALLFIELDS;
  1032. HRESULT hr;
  1033. switch (iCol & SHCIDS_COLUMNMASK)
  1034. {
  1035. default: // If asking for unknown column, just use name
  1036. case DRIVES_ICOL_NAME:
  1037. hr = ResultFromShort(StrCmpICA(pidd1->cName, pidd2->cName));
  1038. break;
  1039. case DRIVES_ICOL_TYPE:
  1040. {
  1041. TCHAR szName1[80], szName2[80];
  1042. if (SHID_COMPUTER_REGITEM != pidd1->bFlags)
  1043. {
  1044. CMountPoint::GetTypeString(DRIVEID(pidd1->cName), szName1, ARRAYSIZE(szName1));
  1045. }
  1046. else
  1047. {
  1048. LoadString(HINST_THISDLL, IDS_DRIVES_REGITEM, szName1, ARRAYSIZE(szName1));
  1049. }
  1050. if (SHID_COMPUTER_REGITEM != pidd1->bFlags)
  1051. {
  1052. CMountPoint::GetTypeString(DRIVEID(pidd2->cName), szName2, ARRAYSIZE(szName2));
  1053. }
  1054. else
  1055. {
  1056. LoadString(HINST_THISDLL, IDS_DRIVES_REGITEM, szName2, ARRAYSIZE(szName2));
  1057. }
  1058. hr = ResultFromShort(ustrcmpi(szName1, szName2));
  1059. break;
  1060. }
  1061. case DRIVES_ICOL_CAPACITY:
  1062. case DRIVES_ICOL_FREE:
  1063. {
  1064. ULONGLONG qwSize1, qwFree1;
  1065. ULONGLONG qwSize2, qwFree2;
  1066. BOOL fGotInfo1 = _GetFreeSpace(pidd1, &qwSize1, &qwFree1);
  1067. BOOL fGotInfo2 = _GetFreeSpace(pidd2, &qwSize2, &qwFree2);
  1068. if (fGotInfo1 && fGotInfo2)
  1069. {
  1070. ULONGLONG i1, i2; // this is a "guess" at the disk size and free space
  1071. if ((iCol & SHCIDS_COLUMNMASK) == DRIVES_ICOL_CAPACITY)
  1072. {
  1073. i1 = qwSize1;
  1074. i2 = qwSize2;
  1075. }
  1076. else
  1077. {
  1078. i1 = qwFree1;
  1079. i2 = qwFree2;
  1080. }
  1081. if (i1 == i2)
  1082. hr = ResultFromShort(0);
  1083. else if (i1 < i2)
  1084. hr = ResultFromShort(-1);
  1085. else
  1086. hr = ResultFromShort(1);
  1087. }
  1088. else if (!fGotInfo1 && !fGotInfo2)
  1089. {
  1090. hr = ResultFromShort(0);
  1091. }
  1092. else
  1093. {
  1094. hr = ResultFromShort(fGotInfo1 - fGotInfo2);
  1095. }
  1096. break;
  1097. }
  1098. }
  1099. if (0 == HRESULT_CODE(hr))
  1100. {
  1101. // check if clsids are equivalent, if they're different then we're done.
  1102. // duh... this should be checked AFTER the other checks so sort order is preserved.
  1103. CLSID clsid1, clsid2;
  1104. _GetCLSIDFromPidl(pidd1, &clsid1);
  1105. _GetCLSIDFromPidl(pidd2, &clsid2);
  1106. hr = ResultFromShort(memcmp(&clsid1, &clsid2, sizeof(CLSID)));
  1107. }
  1108. // if they were the same so far, and we forcing an all-fields
  1109. // comparison, then use the all-fields comparison to break ties.
  1110. if ((0 == HRESULT_CODE(hr)) && (iCol & SHCIDS_ALLFIELDS))
  1111. {
  1112. hr = CompareItemIDs(pidd1, pidd2);
  1113. }
  1114. // If the items are still the same, then ask ILCompareRelIDs
  1115. // to walk recursively to the next ids.
  1116. if (0 == HRESULT_CODE(hr))
  1117. {
  1118. hr = ILCompareRelIDs(SAFECAST(this, IShellFolder *), pidl1, pidl2, iCol);
  1119. }
  1120. return hr;
  1121. }
  1122. STDAPI CDrivesDropTarget_Create(HWND hwnd, LPCITEMIDLIST pidl, IDropTarget **ppdropt);
  1123. STDMETHODIMP CDrivesFolder::CreateViewObject(HWND hwnd, REFIID riid, void** ppv)
  1124. {
  1125. // We should not get here unless we have initialized properly
  1126. HRESULT hr = E_NOINTERFACE;
  1127. *ppv = NULL;
  1128. if (IsEqualIID(riid, IID_IShellView))
  1129. {
  1130. SFV_CREATE sSFV;
  1131. sSFV.cbSize = sizeof(sSFV);
  1132. sSFV.psvOuter = NULL;
  1133. sSFV.psfvcb = CDrives_CreateSFVCB(this);
  1134. QueryInterface(IID_PPV_ARG(IShellFolder, &sSFV.pshf)); // in case we are agregated
  1135. hr = SHCreateShellFolderView(&sSFV, (IShellView**)ppv);
  1136. if (sSFV.pshf)
  1137. sSFV.pshf->Release();
  1138. if (sSFV.psfvcb)
  1139. sSFV.psfvcb->Release();
  1140. }
  1141. else if (IsEqualIID(riid, IID_IDropTarget))
  1142. {
  1143. hr = CDrivesDropTarget_Create(hwnd, IDLIST_DRIVES, (IDropTarget **)ppv);
  1144. }
  1145. else if (IsEqualIID(riid, IID_IContextMenu))
  1146. {
  1147. LPITEMIDLIST pidlFolder;
  1148. if (SUCCEEDED(GetCurFolder(&pidlFolder)))
  1149. {
  1150. IContextMenuCB *pcmcb = new CDrivesBackgroundMenuCB(pidlFolder);
  1151. if (pcmcb)
  1152. {
  1153. hr = CDefFolderMenu_Create2Ex(IDLIST_DRIVES, hwnd, 0, NULL, SAFECAST(this, IShellFolder*), pcmcb,
  1154. 0, NULL, (IContextMenu **)ppv);
  1155. pcmcb->Release();
  1156. }
  1157. ILFree(pidlFolder);
  1158. }
  1159. }
  1160. else if (IsEqualIID(riid, IID_ICategoryProvider))
  1161. {
  1162. HKEY hk = NULL;
  1163. BEGIN_CATEGORY_LIST(s_DriveCategories)
  1164. CATEGORY_ENTRY_SCIDMAP(SCID_CAPACITY, CLSID_DriveSizeCategorizer)
  1165. CATEGORY_ENTRY_SCIDMAP(SCID_TYPE, CLSID_DriveTypeCategorizer)
  1166. CATEGORY_ENTRY_SCIDMAP(SCID_FREESPACE, CLSID_FreeSpaceCategorizer)
  1167. END_CATEGORY_LIST()
  1168. RegOpenKeyEx(HKEY_CLASSES_ROOT, TEXT("Drive\\shellex\\Category"), 0, KEY_READ, &hk);
  1169. hr = CCategoryProvider_Create(&CLSID_DetailCategorizer, &SCID_TYPE, hk, s_DriveCategories, this, riid, ppv);
  1170. if (hk)
  1171. RegCloseKey(hk);
  1172. }
  1173. return hr;
  1174. }
  1175. STDMETHODIMP CDrivesFolder::GetAttributesOf(UINT cidl, LPCITEMIDLIST* apidl, ULONG* prgfInOut)
  1176. {
  1177. UINT rgfOut = SFGAO_HASSUBFOLDER | SFGAO_CANLINK | SFGAO_CANCOPY |
  1178. SFGAO_DROPTARGET | SFGAO_HASPROPSHEET | SFGAO_FOLDER | SFGAO_STORAGE |
  1179. SFGAO_FILESYSTEM | SFGAO_FILESYSANCESTOR | SFGAO_STORAGEANCESTOR;
  1180. if (cidl == 0)
  1181. {
  1182. // We are getting the attributes for the "MyComputer" folder itself.
  1183. rgfOut = (*prgfInOut & g_asDesktopReqItems[CDESKTOP_REGITEM_DRIVES].dwAttributes);
  1184. }
  1185. else if (cidl == 1)
  1186. {
  1187. TCHAR szDrive[MAX_PATH];
  1188. LPCIDDRIVE pidd = _IsValidID(apidl[0]);
  1189. if (!pidd)
  1190. return E_INVALIDARG;
  1191. CMountPoint* pmtpt = CMountPoint::GetMountPoint(DRIVEID(pidd->cName));
  1192. if (pmtpt)
  1193. {
  1194. SHAnsiToTChar(pidd->cName, szDrive, ARRAYSIZE(szDrive));
  1195. if (*prgfInOut & SFGAO_VALIDATE)
  1196. {
  1197. // (tybeam) todo: make this extensible to validate through the clsid object
  1198. // ill do this when i break everything out into IDriveFolderExt or whatever
  1199. CLSID clsid;
  1200. if (S_OK != _GetCLSIDFromPidl(pidd, &clsid))
  1201. {
  1202. DWORD dwAttribs;
  1203. if (!PathFileExistsAndAttributes(szDrive, &dwAttribs))
  1204. return E_FAIL;
  1205. }
  1206. }
  1207. // If caller wants compression status, we need to ask the filesystem
  1208. if (*prgfInOut & SFGAO_COMPRESSED)
  1209. {
  1210. // Don't wake up sleeping net connections
  1211. if (!pmtpt->IsRemote() || !pmtpt->IsDisconnectedNetDrive())
  1212. {
  1213. if (pmtpt->IsCompressed())
  1214. {
  1215. rgfOut |= SFGAO_COMPRESSED;
  1216. }
  1217. }
  1218. }
  1219. if (*prgfInOut & SFGAO_SHARE)
  1220. {
  1221. if (!pmtpt->IsRemote())
  1222. {
  1223. if (IsShared(szDrive, FALSE))
  1224. rgfOut |= SFGAO_SHARE;
  1225. }
  1226. }
  1227. if ((*prgfInOut & SFGAO_REMOVABLE) &&
  1228. (pmtpt->IsStrictRemovable() || pmtpt->IsFloppy() ||
  1229. pmtpt->IsCDROM()))
  1230. {
  1231. rgfOut |= SFGAO_REMOVABLE;
  1232. }
  1233. // we need to also handle the SFGAO_READONLY bit.
  1234. if (*prgfInOut & SFGAO_READONLY)
  1235. {
  1236. DWORD dwAttributes = pmtpt->GetAttributes();
  1237. if (dwAttributes != -1 && dwAttributes & FILE_ATTRIBUTE_READONLY)
  1238. rgfOut |= SFGAO_READONLY;
  1239. }
  1240. // Should we add the write protect stuff and readonly?
  1241. if ((*prgfInOut & SFGAO_CANRENAME) &&
  1242. (pmtpt->IsStrictRemovable() || pmtpt->IsFloppy() ||
  1243. pmtpt->IsFixedDisk() || pmtpt->IsRemote()) ||
  1244. pmtpt->IsDVDRAMMedia())
  1245. {
  1246. rgfOut |= SFGAO_CANRENAME;
  1247. }
  1248. // Is a restriction causing this drive to not be enumerated?
  1249. if (*prgfInOut & SFGAO_NONENUMERATED)
  1250. {
  1251. DWORD dwRestricted = SHRestricted(REST_NODRIVES);
  1252. if (dwRestricted)
  1253. {
  1254. if (((1 << DRIVEID(pidd->cName)) & dwRestricted))
  1255. {
  1256. rgfOut |= SFGAO_NONENUMERATED;
  1257. }
  1258. }
  1259. }
  1260. // We want to allow moving volumes for bulk copy from some media
  1261. // such as dragging pictures from a compact flash to the my pictures
  1262. // folder.
  1263. if (*prgfInOut & SFGAO_CANMOVE)
  1264. {
  1265. if (pmtpt->IsStrictRemovable() || pmtpt->IsFloppy())
  1266. rgfOut |= SFGAO_CANMOVE;
  1267. }
  1268. if (*prgfInOut & SFGAO_HASPROPSHEET)
  1269. {
  1270. if (!pmtpt->IsMounted())
  1271. {
  1272. // Drive is dismounted. Sometimes touching it will remount it.
  1273. // Don't use the pmtpt call, we really want to touch it.
  1274. if (0xFFFFFFFF == GetFileAttributes(szDrive))
  1275. {
  1276. // Failed, check GLE
  1277. if (ERROR_NOT_READY == GetLastError())
  1278. {
  1279. // We get this error on Cluster system, where the drive
  1280. // was passed to the other node.
  1281. rgfOut &= ~SFGAO_HASPROPSHEET;
  1282. }
  1283. }
  1284. }
  1285. }
  1286. pmtpt->Release();
  1287. }
  1288. }
  1289. *prgfInOut = rgfOut;
  1290. return S_OK;
  1291. }
  1292. HRESULT CDrivesFolder::_GetEditTextStrRet(LPCIDDRIVE pidd, STRRET *pstr)
  1293. {
  1294. HRESULT hr = E_FAIL;
  1295. CMountPoint* pMtPt = CMountPoint::GetMountPoint(DRIVEID(pidd->cName));
  1296. if (pMtPt)
  1297. {
  1298. TCHAR szEdit[MAX_PATH];
  1299. hr = pMtPt->GetLabel(szEdit, ARRAYSIZE(szEdit));
  1300. if (SUCCEEDED(hr))
  1301. hr = StringToStrRet(szEdit, pstr);
  1302. pMtPt->Release();
  1303. }
  1304. return hr;
  1305. }
  1306. STDMETHODIMP CDrivesFolder::GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD uFlags, STRRET* pStrRet)
  1307. {
  1308. HRESULT hr;
  1309. LPCIDDRIVE pidd = _IsValidID(pidl);
  1310. if (pidd)
  1311. {
  1312. TCHAR szDrive[ARRAYSIZE(pidd->cName)];
  1313. LPCITEMIDLIST pidlNext = _ILNext(pidl); // Check if pidl contains more than one ID
  1314. SHAnsiToTChar(pidd->cName, szDrive, ARRAYSIZE(szDrive));
  1315. if (ILIsEmpty(pidlNext))
  1316. {
  1317. if (uFlags & SHGDN_FORPARSING)
  1318. {
  1319. hr = StringToStrRet(szDrive, pStrRet);
  1320. }
  1321. else if (uFlags & SHGDN_FOREDITING)
  1322. {
  1323. hr = _GetEditTextStrRet(pidd, pStrRet);
  1324. }
  1325. else
  1326. hr = _GetDisplayNameStrRet(pidd, pStrRet);
  1327. }
  1328. else
  1329. {
  1330. LPITEMIDLIST pidlDrive = ILCombineParentAndFirst(IDLIST_DRIVES, pidl, pidlNext);
  1331. if (pidlDrive)
  1332. {
  1333. // now we need to get the subfolder from which to grab our goodies
  1334. IShellFolder *psfDrive;
  1335. hr = _CreateFSFolder(NULL, pidlDrive, pidd, IID_PPV_ARG(IShellFolder, &psfDrive));
  1336. if (SUCCEEDED(hr))
  1337. {
  1338. hr = psfDrive->GetDisplayNameOf(pidlNext, uFlags, pStrRet);
  1339. psfDrive->Release();
  1340. }
  1341. ILFree(pidlDrive);
  1342. }
  1343. else
  1344. hr = E_OUTOFMEMORY;
  1345. }
  1346. }
  1347. else
  1348. {
  1349. hr = E_INVALIDARG;
  1350. TraceMsg(TF_WARNING, "CDrivesFolder::GetDisplayNameOf() bad PIDL %s", DumpPidl(pidl));
  1351. }
  1352. return hr;
  1353. }
  1354. STDMETHODIMP CDrivesFolder::SetNameOf(HWND hwnd, LPCITEMIDLIST pidl,
  1355. LPCWSTR pszName, DWORD dwReserved, LPITEMIDLIST* ppidlOut)
  1356. {
  1357. if (ppidlOut)
  1358. *ppidlOut = NULL;
  1359. HRESULT hr = E_INVALIDARG;
  1360. LPCIDDRIVE pidd = _IsValidID(pidl);
  1361. if (pidd)
  1362. {
  1363. hr = SetDriveLabel(hwnd, NULL, DRIVEID(pidd->cName), pszName);
  1364. if (SUCCEEDED(hr) && ppidlOut)
  1365. {
  1366. *ppidlOut = ILClone(pidl);
  1367. }
  1368. }
  1369. return hr;
  1370. }
  1371. class CDriveAssocEnumData : public CEnumAssociationElements
  1372. {
  1373. public:
  1374. CDriveAssocEnumData(int iDrive) : _iDrive(iDrive) {}
  1375. private:
  1376. virtual BOOL _Next(IAssociationElement **ppae);
  1377. int _iDrive;
  1378. DWORD _dwChecked;
  1379. };
  1380. enum
  1381. {
  1382. DAED_CHECK_KEY = 0x0001,
  1383. DAED_CHECK_CDORDVD = 0x0002,
  1384. DAED_CHECK_TYPE = 0x0004,
  1385. };
  1386. BOOL CDriveAssocEnumData::_Next(IAssociationElement **ppae)
  1387. {
  1388. HRESULT hr = E_FAIL;
  1389. CMountPoint* pmtpt = CMountPoint::GetMountPoint(_iDrive);
  1390. if (pmtpt)
  1391. {
  1392. if (!(_dwChecked & DAED_CHECK_KEY))
  1393. {
  1394. HKEY hk = pmtpt->GetRegKey();
  1395. if (hk)
  1396. {
  1397. hr = AssocElemCreateForKey(&CLSID_AssocShellElement, hk, ppae);
  1398. RegCloseKey(hk);
  1399. }
  1400. _dwChecked |= DAED_CHECK_KEY;
  1401. }
  1402. if (FAILED(hr) && !(_dwChecked & DAED_CHECK_CDORDVD))
  1403. {
  1404. PCWSTR psz = NULL;
  1405. if (pmtpt->IsAudioCD())
  1406. psz = L"AudioCD";
  1407. else if (pmtpt->IsDVD())
  1408. psz = L"DVD";
  1409. if (psz)
  1410. {
  1411. hr = AssocElemCreateForClass(&CLSID_AssocProgidElement, psz, ppae);
  1412. }
  1413. _dwChecked |= DAED_CHECK_CDORDVD;
  1414. }
  1415. if (FAILED(hr) && !(_dwChecked & DAED_CHECK_TYPE))
  1416. {
  1417. hr = pmtpt->GetAssocSystemElement(ppae);
  1418. _dwChecked |= DAED_CHECK_TYPE;
  1419. }
  1420. pmtpt->Release();
  1421. }
  1422. return SUCCEEDED(hr);
  1423. }
  1424. STDAPI_(BOOL) TBCContainsObject(LPCWSTR pszKey)
  1425. {
  1426. IUnknown *punk;
  1427. if (SUCCEEDED(TBCGetObjectParam(pszKey, IID_PPV_ARG(IUnknown, &punk))))
  1428. {
  1429. punk->Release();
  1430. return TRUE;
  1431. }
  1432. return FALSE;
  1433. }
  1434. HRESULT CDrives_AssocCreate(PCSTR pszName, REFIID riid, void **ppv)
  1435. {
  1436. *ppv = NULL;
  1437. IAssociationArrayInitialize *paai;
  1438. HRESULT hr = ::AssocCreate(CLSID_QueryAssociations, IID_PPV_ARG(IAssociationArrayInitialize, &paai));
  1439. if (SUCCEEDED(hr))
  1440. {
  1441. hr = paai->InitClassElements(ASSOCELEM_BASEIS_FOLDER, L"Drive");
  1442. if (SUCCEEDED(hr) && pszName && !TBCContainsObject(L"ShellExec SHGetAssociations"))
  1443. {
  1444. IEnumAssociationElements *penum = new CDriveAssocEnumData(DRIVEID(pszName));
  1445. if (penum)
  1446. {
  1447. paai->InsertElements(ASSOCELEM_DATA, penum);
  1448. penum->Release();
  1449. }
  1450. }
  1451. if (SUCCEEDED(hr))
  1452. hr = paai->QueryInterface(riid, ppv);
  1453. paai->Release();
  1454. }
  1455. return hr;
  1456. }
  1457. STDAPI_(DWORD) CDrives_GetKeys(PCSTR pszName, HKEY *rgk, UINT ck)
  1458. {
  1459. IAssociationArray *paa;
  1460. HRESULT hr = CDrives_AssocCreate(pszName, IID_PPV_ARG(IAssociationArray, &paa));
  1461. if (SUCCEEDED(hr))
  1462. {
  1463. ck = SHGetAssocKeysEx(paa, -1, rgk, ck);
  1464. paa->Release();
  1465. }
  1466. else
  1467. ck = 0;
  1468. return ck;
  1469. }
  1470. STDMETHODIMP CDrivesFolder::GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST* apidl,
  1471. REFIID riid, UINT* prgfInOut, void** ppv)
  1472. {
  1473. HRESULT hr;
  1474. LPCIDDRIVE pidd = (cidl && apidl) ? _IsValidID(apidl[0]) : NULL;
  1475. *ppv = NULL;
  1476. if (!pidd)
  1477. return E_INVALIDARG;
  1478. if (IsEqualIID(riid, IID_IExtractIconA) || IsEqualIID(riid, IID_IExtractIconW) && pidd)
  1479. {
  1480. WCHAR szDrive[MAX_PATH];
  1481. SHAnsiToUnicode(pidd->cName, szDrive, ARRAYSIZE(szDrive));
  1482. hr = SHCreateDrvExtIcon(szDrive, riid, ppv);
  1483. }
  1484. else
  1485. {
  1486. if (IsEqualIID(riid, IID_IContextMenu))
  1487. {
  1488. HKEY rgk[MAX_ASSOC_KEYS];
  1489. DWORD ck = CDrives_GetKeys(pidd->cName, rgk, ARRAYSIZE(rgk));
  1490. hr = CDefFolderMenu_Create2(IDLIST_DRIVES, hwnd, cidl, apidl,
  1491. SAFECAST(this, IShellFolder *), CDrives_DFMCallBack, ck, rgk, (IContextMenu **)ppv);
  1492. SHRegCloseKeys(rgk, ck);
  1493. }
  1494. else if (IsEqualIID(riid, IID_IDataObject))
  1495. {
  1496. hr = SHCreateFileDataObject(IDLIST_DRIVES, cidl, apidl, NULL, (IDataObject **)ppv);
  1497. }
  1498. else if (IsEqualIID(riid, IID_IDropTarget))
  1499. {
  1500. IShellFolder *psfT;
  1501. hr = BindToObject((LPCITEMIDLIST)pidd, NULL, IID_PPV_ARG(IShellFolder, &psfT));
  1502. if (SUCCEEDED(hr))
  1503. {
  1504. hr = psfT->CreateViewObject(hwnd, IID_IDropTarget, ppv);
  1505. psfT->Release();
  1506. }
  1507. }
  1508. else if (IsEqualIID(riid, IID_IQueryInfo))
  1509. {
  1510. // REVIEW: Shouldn't we use IQA to determine the "prop" string dynamically??? (ZekeL / BuzzR)
  1511. hr = CreateInfoTipFromItem(SAFECAST(this, IShellFolder2 *), (LPCITEMIDLIST)pidd, L"prop:FreeSpace;Capacity", riid, ppv);
  1512. }
  1513. else if (IsEqualIID(riid, IID_IQueryAssociations)
  1514. || IsEqualIID(riid, IID_IAssociationArray))
  1515. {
  1516. hr = CDrives_AssocCreate(pidd->cName, riid, ppv);
  1517. }
  1518. else
  1519. {
  1520. hr = E_NOINTERFACE;
  1521. }
  1522. }
  1523. return hr;
  1524. }
  1525. STDMETHODIMP CDrivesFolder::GetDefaultSearchGUID(GUID *pGuid)
  1526. {
  1527. return DefaultSearchGUID(pGuid);
  1528. }
  1529. STDMETHODIMP CDrivesFolder::EnumSearches(IEnumExtraSearch **ppenum)
  1530. {
  1531. *ppenum = NULL;
  1532. return E_NOTIMPL;
  1533. }
  1534. STDMETHODIMP CDrivesFolder::GetDefaultColumn(DWORD dwRes, ULONG* pSort, ULONG* pDisplay)
  1535. {
  1536. return E_NOTIMPL;
  1537. }
  1538. STDMETHODIMP CDrivesFolder::GetDefaultColumnState(UINT iColumn, DWORD* pdwState)
  1539. {
  1540. HRESULT hr;
  1541. if (iColumn < ARRAYSIZE(c_drives_cols))
  1542. {
  1543. *pdwState = c_drives_cols[iColumn].csFlags;
  1544. if (iColumn == DRIVES_ICOL_COMMENT)
  1545. {
  1546. *pdwState |= SHCOLSTATE_SLOW; // It takes a long time to extract the comment from drives
  1547. }
  1548. hr = S_OK;
  1549. }
  1550. else
  1551. {
  1552. hr = E_INVALIDARG;
  1553. }
  1554. return hr;
  1555. }
  1556. STDMETHODIMP CDrivesFolder::GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID* pscid, VARIANT* pv)
  1557. {
  1558. HRESULT hr = E_INVALIDARG;
  1559. LPCIDDRIVE pidd = _IsValidID(pidl);
  1560. if (pidd)
  1561. {
  1562. if (IsEqualSCID(*pscid, SCID_DESCRIPTIONID))
  1563. {
  1564. SHDESCRIPTIONID did;
  1565. CMountPoint* pmtpt = CMountPoint::GetMountPoint(DRIVEID(pidd->cName));
  1566. if (pmtpt)
  1567. {
  1568. did.dwDescriptionId = pmtpt->GetShellDescriptionID();
  1569. pmtpt->Release();
  1570. }
  1571. else
  1572. {
  1573. did.dwDescriptionId = SHDID_COMPUTER_OTHER;
  1574. }
  1575. did.clsid = CLSID_NULL;
  1576. hr = InitVariantFromBuffer(pv, &did, sizeof(did));
  1577. }
  1578. else if (IsEqualSCID(*pscid, SCID_DetailsProperties))
  1579. {
  1580. // DUI webview properties
  1581. // shouldnt we use IQA??? - ZekeL
  1582. hr = InitVariantFromStr(pv, TEXT("prop:Name;Type;FileSystem;FreeSpace;Capacity"));
  1583. }
  1584. else
  1585. {
  1586. int iCol = FindSCID(c_drives_cols, ARRAYSIZE(c_drives_cols), pscid);
  1587. if (iCol >= 0)
  1588. {
  1589. switch (iCol)
  1590. {
  1591. case DRIVES_ICOL_CAPACITY:
  1592. case DRIVES_ICOL_FREE:
  1593. {
  1594. ULONGLONG ullSize, ullFree;
  1595. hr = E_FAIL;
  1596. if (_GetFreeSpace(pidd, &ullSize, &ullFree))
  1597. {
  1598. pv->vt = VT_UI8;
  1599. pv->ullVal = iCol == DRIVES_ICOL_CAPACITY ? ullSize : ullFree;
  1600. hr = S_OK;
  1601. }
  1602. }
  1603. break;
  1604. default:
  1605. {
  1606. SHELLDETAILS sd;
  1607. hr = GetDetailsOf(pidl, iCol, &sd);
  1608. if (SUCCEEDED(hr))
  1609. {
  1610. hr = InitVariantFromStrRet(&sd.str, pidl, pv);
  1611. }
  1612. }
  1613. }
  1614. }
  1615. }
  1616. }
  1617. return hr;
  1618. }
  1619. STDMETHODIMP CDrivesFolder::GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pDetails)
  1620. {
  1621. TCHAR szTemp[INFOTIPSIZE];
  1622. szTemp[0] = 0;
  1623. pDetails->str.uType = STRRET_CSTR;
  1624. pDetails->str.cStr[0] = 0;
  1625. if (!pidl)
  1626. {
  1627. return GetDetailsOfInfo(c_drives_cols, ARRAYSIZE(c_drives_cols), iColumn, pDetails);
  1628. }
  1629. LPCIDDRIVE pidd = _IsValidID(pidl);
  1630. ASSERTMSG(pidd != NULL, "someone passed us a bad pidl");
  1631. if (!pidd)
  1632. return E_FAIL; // protect faulting code below
  1633. switch (iColumn)
  1634. {
  1635. case DRIVES_ICOL_NAME:
  1636. _GetDisplayName(pidd, szTemp, ARRAYSIZE(szTemp));
  1637. break;
  1638. case DRIVES_ICOL_TYPE:
  1639. CMountPoint::GetTypeString(DRIVEID(pidd->cName), szTemp, ARRAYSIZE(szTemp));
  1640. break;
  1641. case DRIVES_ICOL_COMMENT:
  1642. GetDriveComment(DRIVEID(pidd->cName), szTemp, ARRAYSIZE(szTemp));
  1643. break;
  1644. case DRIVES_ICOL_CAPACITY:
  1645. case DRIVES_ICOL_FREE:
  1646. {
  1647. ULONGLONG ullSize, ullFree;
  1648. if (_GetFreeSpace(pidd, &ullSize, &ullFree))
  1649. {
  1650. StrFormatByteSize64((iColumn == DRIVES_ICOL_CAPACITY) ? ullSize : ullFree, szTemp, ARRAYSIZE(szTemp));
  1651. }
  1652. }
  1653. break;
  1654. case DRIVES_ICOL_FILESYSTEM:
  1655. {
  1656. CMountPoint* pMtPt = CMountPoint::GetMountPoint(DRIVEID(pidd->cName));
  1657. if (pMtPt)
  1658. {
  1659. WCHAR szFileSysName[MAX_FILESYSNAME];
  1660. // GetFileSystemName hits the disk for floppies so disable it.
  1661. // since this is a perf win for defview but disables some functionality, it means
  1662. // do NOT rely on the namespace for getting filesystem information, go direct to
  1663. // the mountpoint instead. if filefldr ever supports SCID_FILESYSTEM like
  1664. // SCID_FREESPACE then this should be munged around.
  1665. if (!pMtPt->IsFloppy() && pMtPt->GetFileSystemName(szFileSysName, ARRAYSIZE(szFileSysName)))
  1666. {
  1667. StringCchCopy(szTemp, min(ARRAYSIZE(szTemp), ARRAYSIZE(szFileSysName)), szFileSysName);
  1668. }
  1669. pMtPt->Release();
  1670. }
  1671. }
  1672. break;
  1673. }
  1674. return StringToStrRet(szTemp, &pDetails->str);
  1675. }
  1676. HRESULT CDrivesFolder::MapColumnToSCID(UINT iColumn, SHCOLUMNID* pscid)
  1677. {
  1678. return MapColumnToSCIDImpl(c_drives_cols, ARRAYSIZE(c_drives_cols), iColumn, pscid);
  1679. }
  1680. STDMETHODIMP CDrivesFolder::GetClassID(CLSID* pCLSID)
  1681. {
  1682. *pCLSID = CLSID_MyComputer;
  1683. return S_OK;
  1684. }
  1685. STDMETHODIMP CDrivesFolder::Initialize(LPCITEMIDLIST pidl)
  1686. {
  1687. // Only allow the Drives root on the desktop
  1688. ASSERT(AssertIsIDListInNameSpace(pidl, &CLSID_MyComputer) && ILIsEmpty(_ILNext(pidl)));
  1689. return S_OK;
  1690. }
  1691. STDMETHODIMP CDrivesFolder::GetCurFolder(LPITEMIDLIST *ppidl)
  1692. {
  1693. return GetCurFolderImpl(IDLIST_DRIVES, ppidl);
  1694. }
  1695. STDMETHODIMP CDrivesFolder::_GetIconOverlayInfo(LPCIDDRIVE pidd, int *pIndex, DWORD dwFlags)
  1696. {
  1697. IShellIconOverlayManager *psiom;
  1698. HRESULT hr = GetIconOverlayManager(&psiom);
  1699. if (SUCCEEDED(hr))
  1700. {
  1701. WCHAR wszDrive[10];
  1702. SHAnsiToUnicode(pidd->cName, wszDrive, ARRAYSIZE(wszDrive));
  1703. if (IsShared(wszDrive, FALSE))
  1704. {
  1705. hr = psiom->GetReservedOverlayInfo(wszDrive, 0, pIndex, SIOM_OVERLAYINDEX, SIOM_RESERVED_SHARED);
  1706. }
  1707. else
  1708. {
  1709. hr = psiom->GetFileOverlayInfo(wszDrive, 0, pIndex, dwFlags);
  1710. }
  1711. psiom->Release();
  1712. }
  1713. return hr;
  1714. }
  1715. STDMETHODIMP CDrivesFolder::GetOverlayIndex(LPCITEMIDLIST pidl, int *pIndex)
  1716. {
  1717. HRESULT hr = E_FAIL;
  1718. LPCIDDRIVE pidd = _IsValidID(pidl);
  1719. if (pidd)
  1720. {
  1721. hr = _GetIconOverlayInfo(pidd, pIndex, SIOM_OVERLAYINDEX);
  1722. }
  1723. return hr;
  1724. }
  1725. STDMETHODIMP CDrivesFolder::GetOverlayIconIndex(LPCITEMIDLIST pidl, int *pIndex)
  1726. {
  1727. HRESULT hr = E_FAIL;
  1728. LPCIDDRIVE pidd = _IsValidID(pidl);
  1729. if (pidd)
  1730. {
  1731. hr = _GetIconOverlayInfo(pidd, pIndex, SIOM_ICONINDEX);
  1732. }
  1733. return hr;
  1734. }
  1735. STDMETHODIMP CDrivesFolder::CompareItemIDs(LPCIDDRIVE pidd1, LPCIDDRIVE pidd2)
  1736. {
  1737. // Compare the drive letter for sorting purpose.
  1738. int iRes = StrCmpICA(pidd1->cName, pidd2->cName); // don't need local goo
  1739. // then, compare pidl sizes
  1740. if (iRes == 0)
  1741. {
  1742. iRes = pidd1->cb - pidd2->cb;
  1743. }
  1744. // still equal, compare clsids if both pidls are big and have them
  1745. if ((iRes == 0) && (pidd1->cb >= sizeof(IDDRIVE)))
  1746. {
  1747. iRes = memcmp(&pidd1->clsid, &pidd2->clsid, sizeof(CLSID));
  1748. }
  1749. // still equal, compare on bFlags
  1750. if (iRes == 0)
  1751. {
  1752. iRes = pidd1->bFlags - pidd2->bFlags;
  1753. }
  1754. return ResultFromShort(iRes);
  1755. }
  1756. HRESULT CDrivesFolder::_OnChangeNotify(LPARAM lNotification, LPCITEMIDLIST *ppidl)
  1757. {
  1758. // Get to the last part of this id list...
  1759. if ((lNotification != SHCNE_DRIVEADD) || (ppidl == NULL) || (*ppidl == NULL))
  1760. return S_OK;
  1761. DWORD dwRestricted = SHRestricted(REST_NODRIVES);
  1762. if (dwRestricted == 0)
  1763. return S_OK; // no drives restricted... (majority case)
  1764. LPCIDDRIVE pidd = (LPCIDDRIVE)ILFindLastID(*ppidl);
  1765. if (((1 << DRIVEID(pidd->cName)) & dwRestricted))
  1766. {
  1767. TraceMsg(DM_TRACE, "Drive not added due to restrictions or Drivespace says it should be hidden");
  1768. return S_FALSE;
  1769. }
  1770. return S_OK;
  1771. }
  1772. CDrivesBackgroundMenuCB::CDrivesBackgroundMenuCB(LPITEMIDLIST pidlFolder) : _cRef(1)
  1773. {
  1774. _pidlFolder = ILClone(pidlFolder);
  1775. }
  1776. CDrivesBackgroundMenuCB::~CDrivesBackgroundMenuCB()
  1777. {
  1778. ILFree(_pidlFolder);
  1779. }
  1780. STDMETHODIMP CDrivesBackgroundMenuCB::QueryInterface(REFIID riid, void **ppvObj)
  1781. {
  1782. static const QITAB qit[] = {
  1783. QITABENT(CDrivesBackgroundMenuCB, IContextMenuCB),
  1784. { 0 },
  1785. };
  1786. return QISearch(this, qit, riid, ppvObj);
  1787. }
  1788. STDMETHODIMP_(ULONG) CDrivesBackgroundMenuCB::AddRef()
  1789. {
  1790. return InterlockedIncrement(&_cRef);
  1791. }
  1792. STDMETHODIMP_(ULONG) CDrivesBackgroundMenuCB::Release()
  1793. {
  1794. ASSERT( 0 != _cRef );
  1795. ULONG cRef = InterlockedDecrement(&_cRef);
  1796. if ( 0 == cRef )
  1797. {
  1798. delete this;
  1799. }
  1800. return cRef;
  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. }