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.

566 lines
16 KiB

  1. #include "precomp.h"
  2. #pragma hdrstop
  3. #include "stdenum.h"
  4. #define DM_SDFOLDER 0
  5. class CShellFolderView : public IShellFolderViewDual2,
  6. public IShellService,
  7. public IServiceProvider,
  8. public CObjectSafety,
  9. public CObjectWithSite,
  10. protected CImpIConnectionPointContainer,
  11. protected CImpIExpDispSupport,
  12. protected CImpIDispatch
  13. {
  14. public:
  15. CShellFolderView(void);
  16. // IUnknown
  17. STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
  18. STDMETHODIMP_(ULONG) AddRef(void);
  19. STDMETHODIMP_(ULONG) Release(void);
  20. // IDispatch
  21. STDMETHODIMP GetTypeInfoCount(UINT * pctinfo)
  22. { return CImpIDispatch::GetTypeInfoCount(pctinfo); }
  23. STDMETHODIMP GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo **pptinfo)
  24. { return CImpIDispatch::GetTypeInfo(itinfo, lcid, pptinfo); }
  25. STDMETHODIMP GetIDsOfNames(REFIID riid, OLECHAR **rgszNames, UINT cNames, LCID lcid, DISPID * rgdispid)
  26. { return CImpIDispatch::GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid); }
  27. STDMETHODIMP Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS * pdispparams, VARIANT * pvarResult, EXCEPINFO * pexcepinfo, UINT * puArgErr);
  28. // IServiceProvider
  29. STDMETHODIMP QueryService(REFGUID guidService, REFIID riid, void **ppvObj);
  30. // IShellFolderViewDual2
  31. STDMETHODIMP get_Application(IDispatch **ppid);
  32. STDMETHODIMP get_Parent(IDispatch **ppid);
  33. STDMETHODIMP get_Folder(Folder **ppid);
  34. STDMETHODIMP SelectedItems(FolderItems **ppid);
  35. STDMETHODIMP get_FocusedItem(FolderItem **ppid);
  36. STDMETHODIMP SelectItem(VARIANT *pvfi, int dwFlags);
  37. STDMETHODIMP PopupItemMenu(FolderItem * pfi, VARIANT vx, VARIANT vy, BSTR * pbs);
  38. STDMETHODIMP get_Script(IDispatch **ppid);
  39. STDMETHODIMP get_ViewOptions(long *plSetting);
  40. STDMETHODIMP get_CurrentViewMode(UINT *pViewMode);
  41. STDMETHODIMP put_CurrentViewMode(UINT ViewMode);
  42. STDMETHODIMP SelectItemRelative(int iRelative);
  43. // IShellService
  44. STDMETHODIMP SetOwner(IUnknown* punkOwner);
  45. // CImpIConnectionPoint
  46. STDMETHODIMP EnumConnectionPoints(LPENUMCONNECTIONPOINTS * ppEnum);
  47. // CObjectWithSite overriding
  48. STDMETHODIMP SetSite(IUnknown *punkSite);
  49. private:
  50. HRESULT _IsSafe()
  51. {
  52. return (0 == _dwSafetyOptions) ? S_OK : IsSafePage(_punkSite);
  53. }
  54. ~CShellFolderView(void);
  55. HRESULT _GetFolder();
  56. // CImpIExpDispSupport
  57. virtual CConnectionPoint* _FindCConnectionPointNoRef(BOOL fdisp, REFIID iid);
  58. LONG _cRef;
  59. CFolder *_psdf; // The shell folder we talk to ...
  60. IUnknown *_punkOwner; // back link to defview (owner object of us...)
  61. IShellFolderView *_psfvOwner; // The owners Shell folder view...
  62. HWND _hwnd; // the hwnd for the window...
  63. // Embed our Connection Point object - implmentation in cnctnpt.cpp
  64. CConnectionPoint _cpEvents;
  65. };
  66. // implementation of CLSID_ShellFolderView, class factory is in shell32 (and was in mars, but that project is dead)
  67. STDAPI CShellFolderView_CreateInstance(IUnknown* punkOuter, REFIID riid, void **ppvOut)
  68. {
  69. *ppvOut = NULL;
  70. HRESULT hr = E_OUTOFMEMORY;
  71. CShellFolderView* psfv = new CShellFolderView();
  72. if (psfv)
  73. {
  74. hr = psfv->QueryInterface(riid, ppvOut);
  75. psfv->Release();
  76. }
  77. return hr;
  78. }
  79. CShellFolderView::CShellFolderView(void) :
  80. CImpIDispatch(SDSPATCH_TYPELIB, IID_IShellFolderViewDual2), _cRef(1), _psdf(NULL)
  81. {
  82. DllAddRef();
  83. _cpEvents.SetOwner((IUnknown*)SAFECAST(this, IShellFolderViewDual2*), &DIID_DShellFolderViewEvents);
  84. }
  85. CShellFolderView::~CShellFolderView(void)
  86. {
  87. // if we ever grabbed a shell folder for this window release it also
  88. if (_psdf)
  89. {
  90. _psdf->SetSite(NULL);
  91. _psdf->SetOwner(NULL);
  92. _psdf->Release();
  93. }
  94. ASSERT(_punkOwner == NULL);
  95. ASSERT(_psfvOwner == NULL);
  96. ASSERT(_punkSite == NULL);
  97. DllRelease();
  98. }
  99. STDMETHODIMP CShellFolderView::QueryInterface(REFIID riid, void **ppv)
  100. {
  101. static const QITAB qit[] = {
  102. QITABENT(CShellFolderView, IShellFolderViewDual),
  103. QITABENT(CShellFolderView, IShellFolderViewDual2),
  104. QITABENTMULTI(CShellFolderView, IDispatch, IShellFolderViewDual),
  105. QITABENTMULTI(CShellFolderView, IDispatch, IShellFolderViewDual2),
  106. QITABENT(CShellFolderView, IShellService),
  107. QITABENT(CShellFolderView, IConnectionPointContainer),
  108. QITABENT(CShellFolderView, IExpDispSupport),
  109. QITABENT(CShellFolderView, IObjectSafety),
  110. QITABENT(CShellFolderView, IObjectWithSite),
  111. QITABENT(CShellFolderView, IServiceProvider),
  112. { 0 },
  113. };
  114. return QISearch(this, qit, riid, ppv);
  115. }
  116. STDMETHODIMP_(ULONG) CShellFolderView::AddRef(void)
  117. {
  118. return InterlockedIncrement(&_cRef);
  119. }
  120. STDMETHODIMP_(ULONG) CShellFolderView::Release(void)
  121. {
  122. if (InterlockedDecrement(&_cRef))
  123. return _cRef;
  124. delete this;
  125. return 0;
  126. }
  127. //The ShellWindow implementation
  128. // let folder we have handle this. Probably won't work for webviews as this object
  129. // is not secure...
  130. STDMETHODIMP CShellFolderView::get_Application(IDispatch **ppid)
  131. {
  132. if (S_OK != _IsSafe())
  133. return E_ACCESSDENIED;
  134. HRESULT hres = _GetFolder();
  135. if (SUCCEEDED(hres))
  136. hres = _psdf->get_Application(ppid);
  137. return hres;
  138. }
  139. STDMETHODIMP CShellFolderView::get_Parent(IDispatch **ppid)
  140. {
  141. *ppid = NULL;
  142. return E_FAIL;
  143. }
  144. HRESULT CShellFolderView::_GetFolder()
  145. {
  146. if (_psdf)
  147. return S_OK;
  148. HRESULT hr;
  149. LPITEMIDLIST pidl = NULL;
  150. IShellFolder *psf = NULL;
  151. if (_psfvOwner)
  152. {
  153. IFolderView *pfv;
  154. if (SUCCEEDED(_psfvOwner->QueryInterface(IID_PPV_ARG(IFolderView, &pfv))))
  155. {
  156. if (SUCCEEDED(pfv->GetFolder(IID_PPV_ARG(IShellFolder, &psf))))
  157. {
  158. if (SHGetIDListFromUnk(psf, &pidl) != S_OK)
  159. {
  160. psf->Release();
  161. psf = NULL;
  162. }
  163. }
  164. pfv->Release();
  165. }
  166. if (!pidl)
  167. {
  168. LPCITEMIDLIST pidlT;
  169. // dsheldon - the -42 is a hack that says "give me the folder pidl"
  170. hr = GetObjectSafely(_psfvOwner, &pidlT, (UINT)-42);
  171. if (SUCCEEDED(hr))
  172. {
  173. pidl = ILClone(pidlT);
  174. }
  175. }
  176. }
  177. if (pidl)
  178. {
  179. hr = CFolder_Create2(_hwnd, pidl, psf, &_psdf);
  180. if (SUCCEEDED(hr))
  181. {
  182. _psdf->SetSite(_punkSite);
  183. _psdf->SetOwner(_punkOwner);
  184. if (_dwSafetyOptions)
  185. hr = MakeSafeForScripting((IUnknown**)&_psdf);
  186. }
  187. if (psf)
  188. psf->Release();
  189. ILFree(pidl);
  190. }
  191. else
  192. hr = E_FAIL;
  193. return hr;
  194. }
  195. STDMETHODIMP CShellFolderView::SetSite(IUnknown *punkSite)
  196. {
  197. if (_psdf)
  198. _psdf->SetSite(punkSite);
  199. return CObjectWithSite::SetSite(punkSite);
  200. }
  201. STDMETHODIMP CShellFolderView::get_Folder(Folder **ppid)
  202. {
  203. if (S_OK != _IsSafe())
  204. return E_ACCESSDENIED;
  205. *ppid = NULL;
  206. HRESULT hr = _GetFolder();
  207. if (SUCCEEDED(hr))
  208. hr = _psdf->QueryInterface(IID_PPV_ARG(Folder, ppid));
  209. return hr;
  210. }
  211. STDMETHODIMP CShellFolderView::SelectedItems(FolderItems **ppid)
  212. {
  213. if (S_OK != _IsSafe())
  214. return E_ACCESSDENIED;
  215. HRESULT hr = _GetFolder();
  216. if (SUCCEEDED(hr))
  217. {
  218. hr = CFolderItems_Create(_psdf, TRUE, ppid);
  219. if (SUCCEEDED(hr))
  220. {
  221. IUnknown_SetSite(*ppid, _punkSite);
  222. if (_dwSafetyOptions)
  223. {
  224. hr = MakeSafeForScripting((IUnknown**)ppid);
  225. }
  226. }
  227. }
  228. return hr;
  229. }
  230. // NOTE: this returns an alias pointer, it is not allocated
  231. HRESULT GetObjectSafely(IShellFolderView *psfv, LPCITEMIDLIST *ppidl, UINT iType)
  232. {
  233. // cast needed because GetObject() returns an alias!
  234. HRESULT hr = psfv->GetObject((LPITEMIDLIST *)ppidl, iType);
  235. if (SUCCEEDED(hr))
  236. {
  237. // On the off chance this is coppied across process boundries...
  238. __try
  239. {
  240. // force a full deref this PIDL to generate a fault if cross process
  241. if (ILGetSize(*ppidl) > 0)
  242. hr = S_OK;
  243. // Don't free it as it was not cloned...
  244. }
  245. __except(SetErrorMode(SEM_NOGPFAULTERRORBOX), UnhandledExceptionFilter(GetExceptionInformation()))
  246. {
  247. *ppidl = NULL;
  248. hr = E_FAIL;
  249. }
  250. }
  251. return hr;
  252. }
  253. STDMETHODIMP CShellFolderView::get_FocusedItem(FolderItem **ppid)
  254. {
  255. if (S_OK != _IsSafe())
  256. return E_ACCESSDENIED;
  257. HRESULT hr = _GetFolder();
  258. if (SUCCEEDED(hr))
  259. {
  260. *ppid = NULL;
  261. hr = S_FALSE;
  262. if (_psfvOwner)
  263. {
  264. // Warning:
  265. // It is common for the following function to fail (which means no item has the focus).
  266. // So, do not save the return code from GetObjectSafely() into "hr" that will ruin the
  267. // S_FALSE value already stored there and result in script errors. (Bug #301306)
  268. //
  269. LPCITEMIDLIST pidl;
  270. // dsheldon - the -2 is a hack that says "give me the selected pidl"
  271. if (SUCCEEDED(GetObjectSafely(_psfvOwner, &pidl, (UINT)-2)))
  272. {
  273. hr = CFolderItem_Create(_psdf, pidl, ppid);
  274. if (_dwSafetyOptions && SUCCEEDED(hr))
  275. hr = MakeSafeForScripting((IUnknown**)ppid);
  276. }
  277. }
  278. else
  279. hr = E_FAIL;
  280. }
  281. return hr;
  282. }
  283. STDMETHODIMP CShellFolderView::get_CurrentViewMode(UINT *pViewMode)
  284. {
  285. if (S_OK != _IsSafe())
  286. return E_ACCESSDENIED;
  287. HRESULT hr = S_FALSE;
  288. IFolderView *pfv;
  289. if (_punkOwner && SUCCEEDED(_punkOwner->QueryInterface(IID_PPV_ARG(IFolderView, &pfv))))
  290. {
  291. hr = pfv->GetCurrentViewMode(pViewMode);
  292. pfv->Release();
  293. }
  294. return hr;
  295. }
  296. STDMETHODIMP CShellFolderView::put_CurrentViewMode(UINT ViewMode)
  297. {
  298. if (S_OK != _IsSafe())
  299. return E_ACCESSDENIED;
  300. HRESULT hr = S_FALSE;
  301. IFolderView *pfv;
  302. if (_punkOwner && SUCCEEDED(_punkOwner->QueryInterface(IID_PPV_ARG(IFolderView, &pfv))))
  303. {
  304. hr = pfv->SetCurrentViewMode(ViewMode);
  305. pfv->Release();
  306. }
  307. return hr;
  308. }
  309. STDMETHODIMP CShellFolderView::SelectItemRelative(int iRelative)
  310. {
  311. if (S_OK != _IsSafe())
  312. return E_ACCESSDENIED;
  313. HRESULT hr = S_FALSE;
  314. IFolderView *pfv;
  315. if (_punkOwner && SUCCEEDED(_punkOwner->QueryInterface(IID_PPV_ARG(IFolderView, &pfv))))
  316. {
  317. int iCurrent, cItems;
  318. if (SUCCEEDED(pfv->ItemCount(SVGIO_ALLVIEW, &cItems)) && (cItems > 1) &&
  319. SUCCEEDED(pfv->GetSelectionMarkedItem(&iCurrent)))
  320. {
  321. int iToSelect = iCurrent + iRelative;
  322. if (iToSelect < 0)
  323. iToSelect = 0;
  324. else if (iToSelect > cItems - 1)
  325. iToSelect = cItems - 1;
  326. hr = pfv->SelectItem(iToSelect, SVSI_SELECTIONMARK | SVSI_SELECT | SVSI_ENSUREVISIBLE | SVSI_DESELECTOTHERS);
  327. }
  328. pfv->Release();
  329. }
  330. return hr;
  331. }
  332. // pvfi should be a "FolderItem" IDispatch
  333. STDMETHODIMP CShellFolderView::SelectItem(VARIANT *pvfi, int dwFlags)
  334. {
  335. if (S_OK != _IsSafe())
  336. return E_ACCESSDENIED;
  337. HRESULT hr = E_FAIL;
  338. LPITEMIDLIST pidl = VariantToIDList(pvfi);
  339. // if pvfi is NULL that means all items in the folder...
  340. if (pidl || (pvfi && pvfi->vt == VT_NULL))
  341. {
  342. IShellView *psv; // use this to select the item...
  343. if (_punkOwner && SUCCEEDED(_punkOwner->QueryInterface(IID_PPV_ARG(IShellView, &psv))))
  344. {
  345. // ILFindLastID checks for NULL...
  346. hr = psv->SelectItem(ILFindLastID(pidl), dwFlags);
  347. psv->Release();
  348. }
  349. ILFree(pidl);
  350. }
  351. return hr;
  352. }
  353. STDMETHODIMP CShellFolderView::PopupItemMenu(FolderItem *pfi, VARIANT vx, VARIANT vy, BSTR * pbs)
  354. {
  355. return E_NOTIMPL;
  356. }
  357. STDMETHODIMP CShellFolderView::get_Script(IDispatch **ppid)
  358. {
  359. *ppid = NULL; // Say that we got nothing...
  360. if (!_punkOwner)
  361. return S_FALSE;
  362. IShellView *psv;
  363. HRESULT hres = _punkOwner->QueryInterface(IID_PPV_ARG(IShellView, &psv));
  364. if (SUCCEEDED(hres))
  365. {
  366. // lets see if there is a IHTMLDocument that is below us now...
  367. IHTMLDocument *phtmld;
  368. hres = psv->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARG(IHTMLDocument, &phtmld));
  369. if (SUCCEEDED(hres))
  370. {
  371. if (_dwSafetyOptions)
  372. hres = MakeSafeForScripting((IUnknown **)&phtmld);
  373. if (SUCCEEDED(hres))
  374. {
  375. hres = phtmld->get_Script(ppid);
  376. }
  377. phtmld->Release();
  378. }
  379. psv->Release();
  380. }
  381. return hres;
  382. }
  383. // Helper function that can be used to get the current view options...
  384. // (ripped out of dvoc.cpp)
  385. DWORD GetViewOptionsForDispatch()
  386. {
  387. SHELLSTATE ss = {0};
  388. DWORD dwSetting = 0;
  389. // Get the view options to return...
  390. SHGetSetSettings(&ss,
  391. SSF_SHOWALLOBJECTS|SSF_SHOWEXTENSIONS|SSF_SHOWCOMPCOLOR|
  392. SSF_SHOWSYSFILES|SSF_DOUBLECLICKINWEBVIEW|SSF_DESKTOPHTML|SSF_WIN95CLASSIC,
  393. FALSE);
  394. // Aarg: mnuch the Bool:1 fields into a dword...
  395. if (ss.fShowAllObjects) dwSetting |= SFVVO_SHOWALLOBJECTS;
  396. if (ss.fShowExtensions) dwSetting |= SFVVO_SHOWEXTENSIONS;
  397. if (ss.fShowCompColor) dwSetting |= SFVVO_SHOWCOMPCOLOR;
  398. if (ss.fShowSysFiles) dwSetting |= SFVVO_SHOWSYSFILES;
  399. if (ss.fDoubleClickInWebView) dwSetting |= SFVVO_DOUBLECLICKINWEBVIEW;
  400. if (ss.fDesktopHTML) dwSetting |= SFVVO_DESKTOPHTML;
  401. if (ss.fWin95Classic) dwSetting |= SFVVO_WIN95CLASSIC;
  402. return dwSetting;
  403. }
  404. STDMETHODIMP CShellFolderView::get_ViewOptions(long *plSetting)
  405. {
  406. if (S_OK != _IsSafe())
  407. return E_ACCESSDENIED;
  408. *plSetting = (LONG)GetViewOptionsForDispatch();
  409. return S_OK;
  410. }
  411. STDMETHODIMP CShellFolderView::SetOwner(IUnknown* punkOwner)
  412. {
  413. if (_psdf)
  414. _psdf->SetOwner(punkOwner);
  415. IUnknown_Set((IUnknown **)&_psfvOwner, NULL); // free cached interface
  416. IUnknown_Set(&_punkOwner, punkOwner); // hold this guy
  417. if (_punkOwner)
  418. {
  419. _punkOwner->QueryInterface(IID_PPV_ARG(IShellFolderView, &_psfvOwner));
  420. if (!_hwnd)
  421. {
  422. IShellView *psv;
  423. // this is gross, until we can merge the two models, create one of our
  424. // Window objects.
  425. if (SUCCEEDED(_punkOwner->QueryInterface(IID_PPV_ARG(IShellView, &psv))))
  426. {
  427. HWND hwndFldr;
  428. psv->GetWindow(&hwndFldr);
  429. // Really gross, but assume parent HWND is the HWND we are after...
  430. _hwnd = GetParent(hwndFldr);
  431. psv->Release();
  432. }
  433. }
  434. }
  435. return S_OK;
  436. }
  437. STDMETHODIMP CShellFolderView::EnumConnectionPoints(IEnumConnectionPoints **ppEnum)
  438. {
  439. return CreateInstance_IEnumConnectionPoints(ppEnum, 1, _cpEvents.CastToIConnectionPoint());
  440. }
  441. CConnectionPoint* CShellFolderView::_FindCConnectionPointNoRef(BOOL fdisp, REFIID iid)
  442. {
  443. if (IsEqualIID(iid, DIID_DShellFolderViewEvents) || (fdisp && IsEqualIID(iid, IID_IDispatch)))
  444. return &_cpEvents;
  445. return NULL;
  446. }
  447. HRESULT CShellFolderView::Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS * pdispparams, VARIANT * pvarResult, EXCEPINFO * pexcepinfo, UINT * puArgErr)
  448. {
  449. HRESULT hr;
  450. if (dispidMember == DISPID_READYSTATE)
  451. return DISP_E_MEMBERNOTFOUND; // perf: what typeinfo would return.
  452. if (dispidMember == DISPID_WINDOWOBJECT)
  453. {
  454. IDispatch *pdisp;
  455. if (SUCCEEDED(get_Script(&pdisp)))
  456. {
  457. hr = pdisp->Invoke(dispidMember, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr);
  458. pdisp->Release();
  459. }
  460. else
  461. {
  462. hr = DISP_E_MEMBERNOTFOUND;
  463. }
  464. }
  465. else
  466. hr = CImpIDispatch::Invoke(dispidMember, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr);
  467. return hr;
  468. }
  469. HRESULT CShellFolderView::QueryService(REFGUID guidService, REFIID riid, void **ppv)
  470. {
  471. HRESULT hr = IUnknown_QueryService(_punkOwner, guidService, riid, ppv);
  472. if (FAILED(hr))
  473. hr = IUnknown_QueryService(_punkSite, guidService, riid, ppv);
  474. return hr;
  475. }