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.

543 lines
16 KiB

  1. #include "shellprv.h"
  2. #include "defcm.h"
  3. #include "datautil.h"
  4. /////////////////////////////////////////////////////////////////////////////
  5. // CClientExtractIcon
  6. class CClientExtractIconCB;
  7. class ATL_NO_VTABLE CClientExtractIcon
  8. : public IShellExtInit
  9. , public IExtractIconW
  10. , public IExtractIconA
  11. , public IContextMenu
  12. , public IPersistPropertyBag
  13. , public IServiceProvider
  14. , public CComObjectRootEx<CComSingleThreadModel>
  15. , public CComCoClass<CClientExtractIcon, &CLSID_ClientExtractIcon>
  16. {
  17. public:
  18. BEGIN_COM_MAP(CClientExtractIcon)
  19. COM_INTERFACE_ENTRY(IShellExtInit)
  20. // Need to use COM_INTERFACE_ENTRY_IID for the interfaces
  21. // that don't have an idl
  22. COM_INTERFACE_ENTRY_IID(IID_IExtractIconA, IExtractIconA)
  23. COM_INTERFACE_ENTRY_IID(IID_IExtractIconW, IExtractIconW)
  24. COM_INTERFACE_ENTRY_IID(IID_IContextMenu, IContextMenu)
  25. COM_INTERFACE_ENTRY(IPersist)
  26. COM_INTERFACE_ENTRY(IPersistPropertyBag)
  27. COM_INTERFACE_ENTRY(IServiceProvider)
  28. END_COM_MAP()
  29. DECLARE_NO_REGISTRY()
  30. DECLARE_NOT_AGGREGATABLE(CClientExtractIcon)
  31. public:
  32. // *** IShellExtInit ***
  33. STDMETHODIMP Initialize(LPCITEMIDLIST pidlFolder,
  34. IDataObject *pdto,
  35. HKEY hkProgID);
  36. // *** IExtractIconA ***
  37. STDMETHODIMP GetIconLocation(UINT uFlags,
  38. LPSTR szIconFile,
  39. UINT cchMax,
  40. int *piIndex,
  41. UINT *pwFlags);
  42. STDMETHODIMP Extract(LPCSTR pszFile,
  43. UINT nIconIndex,
  44. HICON *phiconLarge,
  45. HICON *phiconSmall,
  46. UINT nIconSize);
  47. // *** IExtractIconW ***
  48. STDMETHODIMP GetIconLocation(UINT uFlags,
  49. LPWSTR szIconFile,
  50. UINT cchMax,
  51. int *piIndex,
  52. UINT *pwFlags);
  53. STDMETHODIMP Extract(LPCWSTR pszFile,
  54. UINT nIconIndex,
  55. HICON *phiconLarge,
  56. HICON *phiconSmall,
  57. UINT nIconSize);
  58. // *** IContextMenu methods ***
  59. STDMETHOD(QueryContextMenu)(HMENU hmenu,
  60. UINT indexMenu,
  61. UINT idCmdFirst,
  62. UINT idCmdLast,
  63. UINT uFlags);
  64. STDMETHOD(InvokeCommand)(LPCMINVOKECOMMANDINFO pici);
  65. STDMETHOD(GetCommandString)(UINT_PTR idCmd,
  66. UINT uType,
  67. UINT * pwReserved,
  68. LPSTR pszName,
  69. UINT cchMax);
  70. // *** IPersist methods ***
  71. STDMETHOD(GetClassID)(CLSID *pclsid)
  72. { *pclsid = CLSID_ClientExtractIcon; return S_OK; }
  73. // *** IPersistPropertyBag methods ***
  74. STDMETHOD(InitNew)();
  75. STDMETHOD(Load)(IPropertyBag *pbg,
  76. IErrorLog *plog);
  77. STDMETHOD(Save)(IPropertyBag *pbg,
  78. BOOL fClearDirty,
  79. BOOL FSaveAllProperties) { return E_NOTIMPL; }
  80. // *** IServiceProvider methods ***
  81. STDMETHODIMP QueryService(REFGUID guidService, REFIID riid, void **ppvObj);
  82. public:
  83. CClientExtractIcon() : _idmProperties(-1) { }
  84. ~CClientExtractIcon();
  85. // *** IContextMenuCB forwarded back to us ***
  86. HRESULT CallBack(IShellFolder *psf, HWND hwnd, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam);
  87. public:
  88. IContextMenu * _pcmInner;
  89. IAssociationElement * _pae;
  90. IPropertyBag * _pbag;
  91. LPITEMIDLIST _pidlObject;
  92. CComObject<CClientExtractIconCB> * _pcb;
  93. HKEY _hkClass;
  94. UINT _idmProperties;
  95. UINT _idCmdFirst;
  96. };
  97. /////////////////////////////////////////////////////////////////////////////
  98. // CClientExtractIconCB - DefCM callback
  99. //
  100. class ATL_NO_VTABLE CClientExtractIconCB
  101. : public IContextMenuCB
  102. , public CComObjectRootEx<CComSingleThreadModel>
  103. , public CComCoClass<CClientExtractIconCB>
  104. {
  105. public:
  106. BEGIN_COM_MAP(CClientExtractIconCB)
  107. // Need to use COM_INTERFACE_ENTRY_IID for the interfaces
  108. // that don't have an idl
  109. COM_INTERFACE_ENTRY_IID(IID_IContextMenuCB, IContextMenuCB)
  110. END_COM_MAP()
  111. DECLARE_NO_REGISTRY()
  112. DECLARE_NOT_AGGREGATABLE(CClientExtractIconCB)
  113. void Attach(CClientExtractIcon *pcxi) { _pcxi = pcxi; }
  114. public:
  115. // *** IContextMenuCB ***
  116. STDMETHODIMP CallBack(IShellFolder *psf, HWND hwnd, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
  117. {
  118. if (_pcxi)
  119. return _pcxi->CallBack(psf, hwnd, pdtobj, uMsg, wParam, lParam);
  120. return E_FAIL;
  121. }
  122. public:
  123. CClientExtractIcon *_pcxi;
  124. };
  125. /////////////////////////////////////////////////////////////////////////////
  126. STDAPI CClientExtractIcon_CreateInstance(IUnknown* punkOuter, REFIID riid, void** ppunk)
  127. {
  128. return CClientExtractIcon::_CreatorClass::CreateInstance(punkOuter, riid, ppunk);
  129. }
  130. CClientExtractIcon::~CClientExtractIcon()
  131. {
  132. InitNew();
  133. }
  134. // *** IPersistPropertyBag::InitNew ***
  135. HRESULT CClientExtractIcon::InitNew()
  136. {
  137. ATOMICRELEASE(_pcmInner);
  138. ATOMICRELEASE(_pae);
  139. ATOMICRELEASE(_pbag);
  140. ILFree(_pidlObject);
  141. if (_hkClass) RegCloseKey(_hkClass);
  142. if (_pcb)
  143. {
  144. _pcb->Attach(NULL); // break circular reference
  145. _pcb->Release();
  146. _pcb = NULL;
  147. }
  148. return S_OK;
  149. }
  150. //
  151. // Property bag items:
  152. //
  153. // Element = CLSID_Assoc* element to create
  154. // InitString = string to IPersistString2::SetString with
  155. // opentext = display name for "open" command (if not overridden)
  156. //
  157. // If the client did not customize a "properties" command, then we
  158. // also use these values:
  159. //
  160. // properties = program to execute for "properties"
  161. // propertiestext = name for the "properties" command
  162. //
  163. // *** IPersistPropertyBag::Load ***
  164. HRESULT CClientExtractIcon::Load(IPropertyBag *pbag, IErrorLog *plog)
  165. {
  166. HRESULT hr;
  167. // Erase any old state
  168. InitNew();
  169. // Save the property bag so we can mess with him later
  170. _pbag = pbag;
  171. if (_pbag)
  172. {
  173. _pbag->AddRef();
  174. }
  175. // Get the CLSID we are dispatching through and initialize it
  176. // with the InitString.
  177. CLSID clsid;
  178. hr = SHPropertyBag_ReadGUID(pbag, L"Element", &clsid);
  179. if (SUCCEEDED(hr))
  180. {
  181. BSTR bs;
  182. hr = SHPropertyBag_ReadBSTR(pbag, L"InitString", &bs);
  183. if (SUCCEEDED(hr))
  184. {
  185. hr = THR(AssocElemCreateForClass(&clsid, bs, &_pae));
  186. ::SysFreeString(bs);
  187. // Ignore failure of AssocElemCreateForClass
  188. // It can fail if the user's default client got uninstalled
  189. hr = S_OK;
  190. }
  191. }
  192. return hr;
  193. }
  194. // *** IServiceProvider::QueryService ***
  195. //
  196. // We cheat and use ISericeProvider::QueryService as a sort of
  197. // "QueryInterface that's allowed to break COM identity rules".
  198. //
  199. HRESULT CClientExtractIcon::QueryService(REFGUID guidService, REFIID riid, void **ppvObj)
  200. {
  201. if (guidService == IID_IAssociationElement && _pae)
  202. {
  203. return _pae->QueryInterface(riid, ppvObj);
  204. }
  205. *ppvObj = NULL;
  206. return E_FAIL;
  207. }
  208. // *** IShellExtInit::Initialize
  209. //
  210. // Only if the HKEY specifies a ClientType.
  211. //
  212. HRESULT CClientExtractIcon::Initialize(LPCITEMIDLIST, IDataObject *pdto, HKEY hkClass)
  213. {
  214. ILFree(_pidlObject);
  215. HRESULT hr = PidlFromDataObject(pdto, &_pidlObject);
  216. if (_hkClass)
  217. {
  218. RegCloseKey(_hkClass);
  219. }
  220. if (hkClass)
  221. {
  222. _hkClass = SHRegDuplicateHKey(hkClass);
  223. }
  224. return hr;
  225. }
  226. // *** IExtractIconA::GetIconLocation
  227. HRESULT CClientExtractIcon::GetIconLocation(
  228. UINT uFlags, LPSTR szIconFile, UINT cchMax,
  229. int *piIndex, UINT *pwFlags)
  230. {
  231. WCHAR wszPath[MAX_PATH];
  232. HRESULT hr = GetIconLocation(uFlags, wszPath, ARRAYSIZE(wszPath), piIndex, pwFlags);
  233. if (SUCCEEDED(hr))
  234. {
  235. SHUnicodeToAnsi(wszPath, szIconFile, cchMax);
  236. }
  237. return hr;
  238. }
  239. // *** IExtractIconA::Extract
  240. HRESULT CClientExtractIcon::Extract(
  241. LPCSTR pszFile, UINT nIconIndex,
  242. HICON *phiconLarge, HICON *phiconSmall,
  243. UINT nIconSize)
  244. {
  245. return S_FALSE;
  246. }
  247. // *** IExtractIconW::GetIconLocation
  248. HRESULT CClientExtractIcon::GetIconLocation(
  249. UINT uFlags, LPWSTR szIconFile, UINT cchMax,
  250. int *piIndex, UINT *pwFlags)
  251. {
  252. szIconFile[0] = L'\0';
  253. if (_pae)
  254. {
  255. LPWSTR pszIcon;
  256. HRESULT hr = _pae->QueryString(AQS_DEFAULTICON, NULL, &pszIcon);
  257. if (SUCCEEDED(hr))
  258. {
  259. lstrcpynW(szIconFile, pszIcon, cchMax);
  260. SHFree(pszIcon);
  261. }
  262. }
  263. if (!szIconFile[0] && _hkClass)
  264. {
  265. LONG cb = cchMax * sizeof(TCHAR);
  266. RegQueryValueW(_hkClass, L"DefaultIcon", szIconFile, &cb);
  267. }
  268. if (szIconFile[0])
  269. {
  270. *pwFlags = GIL_PERCLASS;
  271. *piIndex = PathParseIconLocationW(szIconFile);
  272. return S_OK;
  273. }
  274. else
  275. {
  276. return E_FAIL;
  277. }
  278. }
  279. // *** IExtractIconW::Extract
  280. HRESULT CClientExtractIcon::Extract(
  281. LPCWSTR pszFile, UINT nIconIndex,
  282. HICON *phiconLarge, HICON *phiconSmall,
  283. UINT nIconSize)
  284. {
  285. return S_FALSE;
  286. }
  287. // *** IContextMenuCB forwarded back to us ***
  288. HRESULT CClientExtractIcon::CallBack(IShellFolder *psf, HWND hwnd, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
  289. {
  290. WCHAR wszBuf[MAX_PATH];
  291. switch (uMsg)
  292. {
  293. case DFM_MERGECONTEXTMENU:
  294. {
  295. QCMINFO *pqcm = (QCMINFO *)lParam;
  296. // Add a "Properties" command in case we end up needing one.
  297. // (And if not, we'll remove it afterwards.)
  298. if (SUCCEEDED(SHPropertyBag_ReadStr(_pbag, L"propertiestext", wszBuf, ARRAYSIZE(wszBuf))))
  299. {
  300. SHLoadIndirectString(wszBuf, wszBuf, ARRAYSIZE(wszBuf), NULL);
  301. InsertMenuW(pqcm->hmenu, pqcm->indexMenu, MF_BYPOSITION | MF_STRING, pqcm->idCmdFirst, wszBuf);
  302. _idmProperties = pqcm->idCmdFirst++;
  303. }
  304. return S_OK; // Please merge the HKEY\shell stuff
  305. }
  306. case DFM_MERGECONTEXTMENU_TOP:
  307. // The app has added its menu items; see what needs to be cleaned up
  308. {
  309. QCMINFO *pqcm = (QCMINFO *)lParam;
  310. // See if they added a "Properties" item.
  311. // If so, then delete ours.
  312. if (GetMenuIndexForCanonicalVerb(pqcm->hmenu, _pcmInner, _idCmdFirst, L"properties") != 0xFFFFFFFF)
  313. {
  314. // Yes they do, so delete our bogus one
  315. DeleteMenu(pqcm->hmenu, _idmProperties, MF_BYCOMMAND);
  316. _idmProperties = -1;
  317. }
  318. // Now see if their "open" command uses the default name.
  319. // If so, then we replace it with a friendlier name.
  320. BOOL fCustomOpenString = FALSE;
  321. IAssociationElement *paeOpen;
  322. if (_pae && SUCCEEDED(_pae->QueryObject(AQVO_SHELLVERB_DELEGATE, L"open", IID_PPV_ARG(IAssociationElement, &paeOpen))))
  323. {
  324. if (SUCCEEDED(paeOpen->QueryExists(AQN_NAMED_VALUE, L"MUIVerb")) ||
  325. SUCCEEDED(paeOpen->QueryExists(AQN_NAMED_VALUE, NULL)))
  326. {
  327. fCustomOpenString = TRUE;
  328. }
  329. paeOpen->Release();
  330. }
  331. if (!fCustomOpenString)
  332. {
  333. UINT idm = GetMenuIndexForCanonicalVerb(pqcm->hmenu, _pcmInner, _idCmdFirst, L"open");
  334. if (idm != 0xFFFFFFFF)
  335. {
  336. if (SUCCEEDED(SHPropertyBag_ReadStr(_pbag, L"opentext", wszBuf, ARRAYSIZE(wszBuf))) &&
  337. wszBuf[0])
  338. {
  339. SHLoadIndirectString(wszBuf, wszBuf, ARRAYSIZE(wszBuf), NULL);
  340. MENUITEMINFO mii;
  341. mii.cbSize = sizeof(mii);
  342. mii.fMask = MIIM_STRING;
  343. mii.dwTypeData = wszBuf;
  344. SetMenuItemInfo(pqcm->hmenu, idm, TRUE, &mii);
  345. }
  346. }
  347. }
  348. }
  349. return S_OK;
  350. case DFM_INVOKECOMMANDEX:
  351. switch (wParam)
  352. {
  353. case 0: // Properties
  354. if (SUCCEEDED(SHPropertyBag_ReadStr(_pbag, L"properties", wszBuf, ARRAYSIZE(wszBuf))))
  355. {
  356. DFMICS *pdfmics = (DFMICS *)lParam;
  357. if (ShellExecCmdLine(pdfmics->pici->hwnd, wszBuf, NULL, SW_SHOWNORMAL, NULL, 0))
  358. {
  359. return S_OK;
  360. }
  361. else
  362. {
  363. return E_FAIL;
  364. }
  365. }
  366. break;
  367. default:
  368. ASSERT(!"Unexpected DFM_INVOKECOMMAND");
  369. break;
  370. }
  371. return E_FAIL;
  372. default:
  373. return E_NOTIMPL;
  374. }
  375. }
  376. // *** IContextMenu::QueryContextMenu ***
  377. HRESULT CClientExtractIcon::QueryContextMenu(
  378. HMENU hmenu,
  379. UINT indexMenu,
  380. UINT idCmdFirst,
  381. UINT idCmdLast,
  382. UINT uFlags)
  383. {
  384. HRESULT hr;
  385. if (!_pcmInner)
  386. {
  387. HKEY hk = NULL;
  388. if (_pae)
  389. {
  390. // If this fails, we continue with a NULL key
  391. AssocKeyFromElement(_pae, &hk);
  392. }
  393. if (!_pcb)
  394. {
  395. hr = CComObject<CClientExtractIconCB>::CreateInstance(&_pcb);
  396. if (SUCCEEDED(hr))
  397. {
  398. _pcb->Attach(this);
  399. _pcb->AddRef();
  400. }
  401. }
  402. else
  403. {
  404. hr = S_OK;
  405. }
  406. if (SUCCEEDED(hr))
  407. {
  408. IShellFolder *psf;
  409. hr = SHGetDesktopFolder(&psf);
  410. if (SUCCEEDED(hr))
  411. {
  412. DEFCONTEXTMENU dcm = {
  413. NULL, // hwnd
  414. _pcb, // pcmcb
  415. NULL, // pidlFolder
  416. psf, // IShellFolder
  417. 1, // cidl
  418. (LPCITEMIDLIST*)&_pidlObject, // apidl
  419. NULL, // paa
  420. 1, // cKeys
  421. &hk, // aKeys
  422. };
  423. hr = CreateDefaultContextMenu(&dcm, &_pcmInner);
  424. psf->Release();
  425. }
  426. }
  427. if (hk)
  428. {
  429. RegCloseKey(hk);
  430. }
  431. if (!_pcmInner)
  432. {
  433. return E_FAIL;
  434. }
  435. }
  436. if (_pcmInner)
  437. {
  438. _idCmdFirst = idCmdFirst;
  439. uFlags |= CMF_VERBSONLY; // Don't do cut/copy/paste/link
  440. hr = _pcmInner->QueryContextMenu(hmenu, indexMenu, idCmdFirst, idCmdLast, uFlags);
  441. }
  442. else
  443. {
  444. hr = E_FAIL;
  445. }
  446. return hr;
  447. }
  448. // *** IContextMenu::InvokeCommand ***
  449. HRESULT CClientExtractIcon::InvokeCommand(LPCMINVOKECOMMANDINFO pici)
  450. {
  451. HRESULT hr = E_FAIL;
  452. if (_pcmInner)
  453. {
  454. hr = _pcmInner->InvokeCommand(pici);
  455. }
  456. return hr;
  457. }
  458. // *** IContextMenu::GetCommandString ***
  459. HRESULT CClientExtractIcon::GetCommandString(
  460. UINT_PTR idCmd,
  461. UINT uType,
  462. UINT * pwReserved,
  463. LPSTR pszName,
  464. UINT cchMax)
  465. {
  466. if (!_pcmInner) return E_FAIL;
  467. return _pcmInner->GetCommandString(idCmd, uType, pwReserved, pszName, cchMax);
  468. }