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.

1181 lines
36 KiB

  1. #include "priv.h"
  2. #include "browsext.h"
  3. #include "tbext.h"
  4. #include <winreg.h> // For the registry walking
  5. #include "dochost.h"
  6. #include "resource.h"
  7. #include <mluisupp.h>
  8. #include <tb_ids.h>
  9. // {DFEED31E-78ED-11d2-86BA-00C04F8EEA99}
  10. EXTERN_C const IID IID_IToolbarExt =
  11. { 0xdfeed31e, 0x78ed, 0x11d2, { 0x86, 0xba, 0x0, 0xc0, 0x4f, 0x8e, 0xea, 0x99 } };
  12. // {D82B85D0-78F4-11d2-86BA-00C04F8EEA99}
  13. EXTERN_C const CLSID CLSID_PrivBrowsExtCommands =
  14. { 0xd82b85d0, 0x78f4, 0x11d2, { 0x86, 0xba, 0x0, 0xc0, 0x4f, 0x8e, 0xea, 0x99 } };
  15. const TCHAR c_szHelpMenu[] = TEXT("help");
  16. //+-------------------------------------------------------------------------
  17. // Creates and instance of CBrowserExtension
  18. //--------------------------------------------------------------------------
  19. HRESULT CBrowserExtension_CreateInstance(IUnknown* pUnkOuter, IUnknown** ppunk, LPCOBJECTINFO poi)
  20. {
  21. *ppunk = NULL;
  22. CBrowserExtension* p = new CBrowserExtension();
  23. if (p)
  24. {
  25. *ppunk = SAFECAST(p, IToolbarExt*);
  26. return S_OK;
  27. }
  28. return E_OUTOFMEMORY;
  29. }
  30. CBrowserExtension::CBrowserExtension()
  31. : _cRef(1),
  32. _uStringIndex((UINT)-1),
  33. _uiImageIndex((UINT)-1)
  34. {
  35. ASSERT(_pISB == NULL);
  36. ASSERT(_hdpa == NULL);
  37. ASSERT(_nExtButtons == 0);
  38. ASSERT(_fStringInit == FALSE);
  39. ASSERT(_fImageInit == FALSE);
  40. }
  41. CBrowserExtension::~CBrowserExtension(void)
  42. {
  43. if (_pISB)
  44. _pISB->Release();
  45. if (_hdpa)
  46. {
  47. _FreeItems();
  48. DPA_Destroy(_hdpa);
  49. _hdpa = NULL;
  50. }
  51. _ReleaseImageLists(_uiImageIndex);
  52. }
  53. // *** IUnknown methods ***
  54. HRESULT CBrowserExtension::QueryInterface(REFIID riid, void ** ppvObj)
  55. {
  56. static const QITAB qit[] = {
  57. QITABENT(CBrowserExtension, IToolbarExt),
  58. QITABENT(CBrowserExtension, IObjectWithSite),
  59. QITABENT(CBrowserExtension, IOleCommandTarget),
  60. { 0 },
  61. };
  62. return QISearch(this, qit, riid, ppvObj);
  63. }
  64. STDMETHODIMP_(ULONG) CBrowserExtension::AddRef()
  65. {
  66. return InterlockedIncrement(&_cRef);
  67. }
  68. STDMETHODIMP_(ULONG) CBrowserExtension::Release()
  69. {
  70. if (InterlockedDecrement(&_cRef) == 0)
  71. {
  72. delete this;
  73. return 0;
  74. }
  75. return _cRef;
  76. }
  77. // IToolbarExt interface functions
  78. HRESULT CBrowserExtension::SetSite(IUnknown* pUnkSite)
  79. {
  80. HRESULT hr = S_OK;
  81. ATOMICRELEASE(_pISB);
  82. if (pUnkSite)
  83. {
  84. hr = pUnkSite->QueryInterface(IID_IShellBrowser, (LPVOID*)&_pISB);
  85. }
  86. // See if we need to init ourselves
  87. if (NULL == _hdpa)
  88. {
  89. // Real construction happens here
  90. HRESULT hr2 = Update();
  91. ASSERT(SUCCEEDED(hr2));
  92. }
  93. else
  94. {
  95. // Update the site for each button/menu extension
  96. for (int i = 0; i < DPA_GetPtrCount(_hdpa); i++)
  97. {
  98. ExtensionItem* pItem = (ExtensionItem*)DPA_GetPtr(_hdpa, i);
  99. IUnknown_SetSite(pItem->pIBE, _pISB);
  100. }
  101. }
  102. return hr;
  103. }
  104. STDMETHODIMP CBrowserExtension::GetSite(REFIID riid, void ** ppvSite)
  105. {
  106. HRESULT hr = S_OK;
  107. *ppvSite = NULL;
  108. if (_pISB)
  109. {
  110. hr = _pISB->QueryInterface(riid, ppvSite);
  111. }
  112. return hr;
  113. }
  114. HRESULT CBrowserExtension::GetNumButtons(UINT* pButtons)
  115. {
  116. ASSERT(pButtons);
  117. *pButtons = _nExtButtons;
  118. return S_OK;
  119. }
  120. HRESULT CBrowserExtension::InitButtons(IExplorerToolbar* pxtb, UINT* puStringIndex, const GUID* pguidCommandGroup)
  121. {
  122. ASSERT(pxtb);
  123. UINT uiSize;
  124. pxtb->GetBitmapSize(&uiSize);
  125. int cx = LOWORD(uiSize);
  126. // Get the image lists for the current button size and screen resolution
  127. CImageList* pimlDef;
  128. CImageList* pimlHot;
  129. UINT uiImageIndexOld = _uiImageIndex;
  130. _uiImageIndex = _GetImageLists(&pimlDef, &pimlHot, cx < 20);
  131. pxtb->SetImageList(pguidCommandGroup, *pimlDef, *pimlHot, NULL);
  132. // Free the previously used image list
  133. _ReleaseImageLists(uiImageIndexOld);
  134. // Add the button text to the toolbar
  135. if (_uStringIndex == (UINT)-1)
  136. {
  137. LRESULT iAddResult = 0; // result of adding the string buffer to the toolbar string list
  138. HRESULT hr = pxtb->AddString(pguidCommandGroup, MLGetHinst(), IDS_BROWSER_TB_LABELS, &iAddResult);
  139. _uStringIndex = (UINT)iAddResult;
  140. _AddCustomStringsToBuffer(pxtb, pguidCommandGroup);
  141. }
  142. *puStringIndex = _uStringIndex;
  143. return S_OK;
  144. }
  145. CBrowserExtension::ExtensionItem* CBrowserExtension::_FindItem(REFGUID rguid)
  146. {
  147. ExtensionItem* pFound = NULL;
  148. if (NULL != _hdpa)
  149. {
  150. for (int i = 0; i < DPA_GetPtrCount(_hdpa); i++)
  151. {
  152. ExtensionItem* pItem = (ExtensionItem*)DPA_GetPtr(_hdpa, i);
  153. if (pItem && IsEqualGUID(pItem->guid, rguid))
  154. {
  155. pFound = pItem;
  156. break;
  157. }
  158. }
  159. }
  160. return pFound;
  161. }
  162. void CBrowserExtension::_AddItem(HKEY hkeyExtensions, LPCWSTR pszGuidItem, REFGUID rguidItem)
  163. {
  164. // Create the dpa used to store our items
  165. if (NULL == _hdpa)
  166. {
  167. _hdpa = DPA_Create(5);
  168. if (NULL == _hdpa)
  169. {
  170. return;
  171. }
  172. }
  173. HKEY hkeyThisExtension;
  174. if (RegOpenKeyEx(hkeyExtensions, pszGuidItem, 0, KEY_READ, &hkeyThisExtension) == ERROR_SUCCESS)
  175. {
  176. // Get the clsid of the object
  177. WCHAR szCLSID[64];
  178. ULONG cbCLSID = SIZEOF(szCLSID);
  179. CLSID clsidCustomButton;
  180. if (SUCCEEDED(RegQueryValueEx(hkeyThisExtension, TEXT("clsid"), NULL, NULL, (unsigned char *)&szCLSID, &cbCLSID)) &&
  181. SUCCEEDED(CLSIDFromString(szCLSID, &clsidCustomButton)))
  182. {
  183. IBrowserExtension * pibeTemp;
  184. // Check for our internal object. Note that our CoCreateInctance wrapper
  185. // compares to the address of the global clsid, so we want to use the global
  186. // guid.
  187. const CLSID* pclsid = &clsidCustomButton;
  188. if (IsEqualGUID(clsidCustomButton, CLSID_ToolbarExtExec))
  189. {
  190. pclsid = &CLSID_ToolbarExtExec;
  191. }
  192. else if (IsEqualGUID(clsidCustomButton, CLSID_ToolbarExtBand))
  193. {
  194. pclsid = &CLSID_ToolbarExtBand;
  195. }
  196. // Create the extension object
  197. if (SUCCEEDED(CoCreateInstance(*pclsid, NULL, CLSCTX_INPROC_SERVER,
  198. IID_IBrowserExtension, (void **)&pibeTemp)))
  199. {
  200. if (SUCCEEDED(pibeTemp->Init(rguidItem)))
  201. {
  202. // Add this item to our array
  203. ExtensionItem* pItem = new ExtensionItem;
  204. if (pItem)
  205. {
  206. if (DPA_AppendPtr(_hdpa, pItem) != -1)
  207. {
  208. VARIANTARG varArg;
  209. pItem->idCmd = _GetCmdIdFromClsid(pszGuidItem);
  210. pItem->pIBE = pibeTemp;
  211. pItem->guid = rguidItem;
  212. pibeTemp->AddRef();
  213. // See if it's a button
  214. if (SUCCEEDED(pibeTemp->GetProperty(TBEX_BUTTONTEXT, NULL)))
  215. {
  216. _nExtButtons++;
  217. pItem->fButton = TRUE;
  218. // See if the button default to visible on the toolbar
  219. if (SUCCEEDED(pibeTemp->GetProperty(TBEX_DEFAULTVISIBLE, &varArg)))
  220. {
  221. ASSERT(varArg.vt == VT_BOOL);
  222. pItem->fVisible = (varArg.boolVal == VARIANT_TRUE);
  223. }
  224. }
  225. // set the target menu
  226. pItem->idmMenu = 0;
  227. if (SUCCEEDED(pibeTemp->GetProperty(TMEX_MENUTEXT, NULL)))
  228. {
  229. if (SUCCEEDED(pibeTemp->GetProperty(TMEX_CUSTOM_MENU, &varArg)))
  230. {
  231. ASSERT(varArg.vt == VT_BSTR);
  232. ASSERT(IS_VALID_STRING_PTR(varArg.bstrVal, -1));
  233. if (!StrCmpNI(varArg.bstrVal, c_szHelpMenu, ARRAYSIZE(c_szHelpMenu)))
  234. {
  235. pItem->idmMenu = FCIDM_MENU_HELP;
  236. }
  237. VariantClear(&varArg);
  238. }
  239. if (pItem->idmMenu == 0)
  240. {
  241. pItem->idmMenu = FCIDM_MENU_TOOLS;
  242. }
  243. }
  244. // Pass the site to the object
  245. IUnknown_SetSite(pibeTemp, _pISB);
  246. }
  247. else
  248. {
  249. delete pItem;
  250. }
  251. }
  252. }
  253. // This will free pibeTemp if we didn't store it away
  254. pibeTemp->Release();
  255. }
  256. }
  257. RegCloseKey(hkeyThisExtension);
  258. }
  259. }
  260. //
  261. // All real construction happens here. In theory this function can be called upon a SysINIChange to update our
  262. // custom toolbar cached information. This has not been tested. This opens the Extensions section of the registry
  263. // enumerates all of the subkeys. Attempts to CoCreate each one. Upon successful CoCreation it calls
  264. // IObjectWithSite::SetSite(IShellBrowser), if it is implemented. Next IBrowserExtension::Init is called. Finally,
  265. // IBrowserExtension::GetProperty(TBEX_BUTTONTEXT, NULL) is called looking for a S_OK to insure that the control in
  266. // question is a Toolbar Button (as opposed to a tools menu item, or...)
  267. //
  268. HRESULT CBrowserExtension::Update()
  269. {
  270. WCHAR szItemGuid[64]; // sufficient for {clsid}
  271. DWORD cbItemGuid;
  272. GUID guidItem;
  273. HRESULT hr = S_OK;
  274. // Free previous items
  275. _nExtButtons = 0;
  276. _nExtToolsMenuItems = 0;
  277. _FreeItems();
  278. // First add extensions from HKCU
  279. HKEY hkeyExtensions;
  280. if (RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Internet Explorer\\Extensions"), 0,
  281. KEY_READ, &hkeyExtensions) == ERROR_SUCCESS)
  282. {
  283. cbItemGuid = sizeof(szItemGuid);
  284. for (int iKey = 0;
  285. RegEnumKeyEx(hkeyExtensions, iKey, szItemGuid, &cbItemGuid, NULL, NULL, NULL, NULL) != ERROR_NO_MORE_ITEMS;
  286. iKey++)
  287. {
  288. if (SUCCEEDED(CLSIDFromString(szItemGuid, &guidItem)))
  289. {
  290. _AddItem(hkeyExtensions, szItemGuid, guidItem);
  291. }
  292. cbItemGuid = sizeof(szItemGuid);
  293. }
  294. RegCloseKey(hkeyExtensions);
  295. }
  296. // Next add any unique items from HKLM
  297. if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("Software\\Microsoft\\Internet Explorer\\Extensions"), 0,
  298. KEY_READ, &hkeyExtensions) == ERROR_SUCCESS)
  299. {
  300. cbItemGuid = sizeof(szItemGuid);
  301. for (int iKey = 0;
  302. RegEnumKeyEx(hkeyExtensions, iKey, szItemGuid, &cbItemGuid, NULL, NULL, NULL, NULL) != ERROR_NO_MORE_ITEMS;
  303. iKey++)
  304. {
  305. if (SUCCEEDED(CLSIDFromString(szItemGuid, &guidItem)))
  306. {
  307. if (_FindItem(guidItem) == NULL)
  308. {
  309. _AddItem(hkeyExtensions, szItemGuid, guidItem);
  310. }
  311. }
  312. cbItemGuid = sizeof(szItemGuid);
  313. }
  314. RegCloseKey(hkeyExtensions);
  315. }
  316. return hr;
  317. }
  318. //
  319. // This takes a TBBUTTON[] and fills in the Custom Buttons. A couple of usage points:
  320. // (1) The caller should allocate a TBBUTTON[] big enough for NUM_STD_BUTTONS + GetNumExtButtons()
  321. // Then they should copy the standard buttons into the array, and pass the pointer to the remainder
  322. // of the array here.
  323. // (2) This function should *by design* never be called before AddCustomImagesToImageList and
  324. // AddCustomStringsToBuffer have both been called. An attempt to do so in DEBUG mode will hit
  325. // a break point.
  326. //
  327. HRESULT CBrowserExtension::GetButtons(TBBUTTON * tbArr, int nNumButtons, BOOL fInit)
  328. {
  329. ASSERT(_fStringInit && _fImageInit);
  330. if (_hdpa)
  331. {
  332. ASSERT(nNumButtons == _nExtButtons);
  333. ASSERT(tbArr != NULL)
  334. int iBtn = 0;
  335. for (int i = 0; i < DPA_GetPtrCount(_hdpa); i++)
  336. {
  337. ExtensionItem* pItem = (ExtensionItem*)DPA_GetPtr(_hdpa, i);
  338. if (!pItem->fButton)
  339. continue;
  340. // We use the MAKELONG(n, 1) to insure that we are using the alternate image list.
  341. tbArr[iBtn].iBitmap = MAKELONG(pItem->iImageID, 1);
  342. tbArr[iBtn].idCommand = pItem->idCmd;
  343. tbArr[iBtn].fsState = TBSTATE_ENABLED;
  344. tbArr[iBtn].fsStyle = BTNS_BUTTON;
  345. tbArr[iBtn].dwData = 0;
  346. tbArr[iBtn].iString = pItem->iStringID;
  347. //
  348. // Default to hidden during initialization so that it defaults to the left well
  349. // of the the customize dialog (defaults off the toolbar)
  350. //
  351. if (fInit && !pItem->fVisible)
  352. {
  353. tbArr[iBtn].fsState = TBSTATE_HIDDEN;
  354. }
  355. ++iBtn;
  356. }
  357. }
  358. return S_OK;
  359. }
  360. //
  361. // This function takes the ImageLists for hot and normal icons and adds the appropriate icon to each
  362. // list for each custom toolbar button. The resultant ImageID is then stored in our _rgExtensionItem struct
  363. // so that the IDs can be placed in a TBBUTTON[] when AddExtButtonsTBArray is called.
  364. //
  365. HRESULT CBrowserExtension::_AddCustomImagesToImageList(CImageList& rimlNormal, CImageList& rimlHot, BOOL fSmallIcons)
  366. {
  367. #ifdef DEBUG
  368. _fImageInit = TRUE;
  369. #endif DEBUG
  370. if (rimlNormal.HasImages() && rimlHot.HasImages() && NULL != _hdpa)
  371. {
  372. for (int i = 0; i < DPA_GetPtrCount(_hdpa); i++)
  373. {
  374. ExtensionItem* pItem = (ExtensionItem*)DPA_GetPtr(_hdpa, i);
  375. if (!pItem->fButton)
  376. continue;
  377. VARIANTARG varArg;
  378. pItem->iImageID = rimlNormal.GetImageIndex(pItem->guid);
  379. if (-1 == pItem->iImageID &&
  380. SUCCEEDED(pItem->pIBE->GetProperty((fSmallIcons ? TBEX_GRAYICONSM : TBEX_GRAYICON), &varArg)))
  381. {
  382. if (varArg.vt == VT_BYREF)
  383. {
  384. pItem->iImageID = rimlNormal.AddIcon((HICON)varArg.byref, pItem->guid);
  385. }
  386. else if (varArg.vt == VT_I4)
  387. {
  388. // It's one of our built-in images
  389. pItem->iImageID = varArg.lVal;
  390. }
  391. else
  392. {
  393. TraceMsg(TF_ALWAYS, "Button doesn't have an image associated");
  394. }
  395. }
  396. int iHot = rimlHot.GetImageIndex(pItem->guid);
  397. if (-1 == iHot &&
  398. SUCCEEDED(pItem->pIBE->GetProperty((fSmallIcons ? TBEX_HOTICONSM : TBEX_HOTICON), &varArg)))
  399. {
  400. if (varArg.vt == VT_BYREF)
  401. {
  402. iHot = rimlHot.AddIcon((HICON)varArg.byref, pItem->guid);
  403. }
  404. else if (varArg.vt == VT_I4)
  405. {
  406. // It's one of our built-in images
  407. iHot = varArg.lVal;
  408. }
  409. else
  410. {
  411. TraceMsg(TF_ALWAYS, "Button doesn't have an image associated");
  412. }
  413. }
  414. if (iHot!=pItem->iImageID)
  415. {
  416. TraceMsg(TF_ALWAYS, "ButtonExtension: iHot doesn't match iImageID");
  417. }
  418. }
  419. }
  420. return S_OK;
  421. }
  422. //
  423. // This function takes the StringList and adds the caption (ToolbarText) for each of the custom toolbar buttons
  424. // to it. The resultant StringID is then stored in our _rgExtensionItem struct so that the ID can be placed in
  425. // a TBBUTTON[] when AddExtButtonsTBArray is called.
  426. //
  427. HRESULT CBrowserExtension::_AddCustomStringsToBuffer(IExplorerToolbar * pxtb, const GUID* pguidCommandGroup)
  428. {
  429. #ifdef DEBUG
  430. _fStringInit = TRUE;
  431. #endif DEBUG
  432. if (NULL != _hdpa)
  433. {
  434. for (int i = 0; i < DPA_GetPtrCount(_hdpa); i++)
  435. {
  436. ExtensionItem* pItem = (ExtensionItem*)DPA_GetPtr(_hdpa, i);
  437. if (!pItem->fButton)
  438. continue;
  439. VARIANTARG varArg;
  440. if (SUCCEEDED(pItem->pIBE->GetProperty(TBEX_BUTTONTEXT, &varArg)))
  441. {
  442. // We need to double-null terminate the string!
  443. WCHAR szBuf[70]; // should be ample for button text!
  444. ZeroInit(szBuf, sizeof(szBuf));
  445. StrNCpy(szBuf, varArg.bstrVal, ARRAYSIZE(szBuf) - 2);
  446. LRESULT iResult;
  447. if (SUCCEEDED(pxtb->AddString(pguidCommandGroup, 0, (LPARAM)szBuf, &iResult)))
  448. {
  449. pItem->iStringID = (SHORT)iResult;
  450. }
  451. VariantClear(&varArg);
  452. }
  453. }
  454. }
  455. return S_OK;
  456. }
  457. int CBrowserExtension::_GetCmdIdFromClsid(LPCWSTR pszGuid)
  458. {
  459. DWORD dwDisposition;
  460. HRESULT hr = S_OK;
  461. int nReturn = DVIDM_MENUEXT_FIRST;
  462. HKEY hkeyExtensionMapping;
  463. if (RegCreateKeyEx(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Internet Explorer\\Extensions\\CmdMapping"), 0, NULL, 0,
  464. KEY_READ | KEY_WRITE, NULL, &hkeyExtensionMapping, &dwDisposition) == ERROR_SUCCESS)
  465. {
  466. DWORD dwType = REG_DWORD, dwData, cbData = sizeof(dwData);
  467. if ( (SHQueryValueEx(hkeyExtensionMapping, pszGuid, NULL, &dwType, &dwData, &cbData) == ERROR_SUCCESS) &&
  468. (dwType == REG_DWORD) )
  469. {
  470. //the item has a mapping
  471. nReturn = dwData;
  472. }
  473. else
  474. {
  475. //it's a new item, get and store the next available id in the default value of the Mapping key
  476. if ( (SHQueryValueEx(hkeyExtensionMapping, L"NextId", NULL, &dwType, &dwData, &cbData) != ERROR_SUCCESS) ||
  477. (dwType != REG_DWORD) )
  478. {
  479. dwData = DVIDM_MENUEXT_FIRST;
  480. }
  481. nReturn = dwData;
  482. dwType = REG_DWORD;
  483. cbData = sizeof(dwData);
  484. EVAL(SHSetValueW(hkeyExtensionMapping, NULL, pszGuid, dwType, &dwData, cbData) == ERROR_SUCCESS);
  485. dwData++;
  486. ASSERT(dwData < DVIDM_MENUEXT_LAST); //ugh, we've used up our whole range. we need to look for holes.
  487. EVAL(SHSetValueW(hkeyExtensionMapping, NULL, L"NextId", dwType, &dwData, cbData) == ERROR_SUCCESS);
  488. }
  489. RegCloseKey(hkeyExtensionMapping);
  490. }
  491. return nReturn;
  492. }
  493. int CBrowserExtension::_GetIdpaFromCmdId(int nCmdId)
  494. {
  495. if (NULL != _hdpa)
  496. {
  497. for (int i = 0; i < DPA_GetPtrCount(_hdpa); i++)
  498. {
  499. ExtensionItem* pItem = (ExtensionItem*)DPA_GetPtr(_hdpa, i);
  500. if (pItem->idCmd == nCmdId)
  501. return i;
  502. }
  503. }
  504. return -1;
  505. }
  506. // *** IOleCommandTarget methods ***
  507. HRESULT CBrowserExtension::Exec(const GUID *pguidCmdGroup, DWORD nCmdID,
  508. DWORD nCmdexecopt, VARIANTARG *pvarargIn, VARIANTARG *pvarargOut)
  509. {
  510. if (!pguidCmdGroup)
  511. return E_INVALIDARG;
  512. if (IsEqualGUID(*pguidCmdGroup, CLSID_ToolbarExtButtons))
  513. {
  514. int iCmd = _GetIdpaFromCmdId(nCmdID);
  515. if (iCmd >= 0 && iCmd < DPA_GetPtrCount(_hdpa))
  516. {
  517. ExtensionItem* pItem = (ExtensionItem*)DPA_GetPtr(_hdpa, iCmd);
  518. if (pItem)
  519. return IUnknown_Exec(pItem->pIBE, NULL, 0, 0, NULL, NULL);
  520. }
  521. }
  522. else if (IsEqualGUID(*pguidCmdGroup, CLSID_PrivBrowsExtCommands))
  523. {
  524. switch (nCmdID)
  525. {
  526. case PBEC_GETSTRINGINDEX:
  527. if (pvarargIn && pvarargIn->vt == VT_I4)
  528. {
  529. pvarargIn->lVal = _uStringIndex;
  530. return S_OK;
  531. }
  532. break;
  533. }
  534. }
  535. return E_FAIL;
  536. }
  537. HRESULT CBrowserExtension::QueryStatus(const GUID *pguidCmdGroup,
  538. ULONG cCmds, OLECMD rgCmds[], OLECMDTEXT *pcmdtext)
  539. {
  540. if (!pguidCmdGroup)
  541. return E_INVALIDARG;
  542. if (IsEqualGUID(*pguidCmdGroup, CLSID_ToolbarExtButtons))
  543. {
  544. for (ULONG i = 0; i < cCmds; i++)
  545. {
  546. int iCmd = _GetIdpaFromCmdId(rgCmds[i].cmdID);
  547. if (iCmd >= 0 && iCmd < DPA_GetPtrCount(_hdpa))
  548. {
  549. ExtensionItem* pItem = (ExtensionItem*)DPA_GetPtr(_hdpa, iCmd);
  550. if (pItem)
  551. {
  552. // I don't think this has ever worked. The command id
  553. // isn't the same as the one we use in Exec.
  554. IUnknown_QueryStatus(pItem->pIBE, pguidCmdGroup, 1, &rgCmds[i], pcmdtext);
  555. }
  556. }
  557. }
  558. return S_OK;
  559. }
  560. return E_FAIL;
  561. }
  562. //
  563. // This function is a helper for the destructor. It is also called by Update() so that if we are ever asked
  564. // to Update() we first kill all of our cached information and then we go to the registry...
  565. //
  566. void CBrowserExtension::_FreeItems(void)
  567. {
  568. if (_hdpa)
  569. {
  570. for (int i = DPA_GetPtrCount(_hdpa) - 1; i >= 0; --i)
  571. {
  572. ExtensionItem* pItem = (ExtensionItem*)DPA_DeletePtr(_hdpa, i);
  573. IUnknown_SetSite(pItem->pIBE, NULL);
  574. pItem->pIBE->Release();
  575. delete pItem;
  576. }
  577. }
  578. }
  579. // this help function is used to isolate the menu-specific
  580. // processing. after using this helper to fill out the BROWSEXT_MENU_INFO
  581. // struct, the OnCustomizableMenuPopup is able to do menu-inspecific
  582. // processing.
  583. HRESULT
  584. CBrowserExtension::_GetCustomMenuInfo(HMENU hMenuParent, HMENU hMenu, BROWSEXT_MENU_INFO * pMI)
  585. {
  586. HRESULT hr;
  587. RIP(IS_VALID_HANDLE(hMenuParent, MENU));
  588. RIP(IS_VALID_HANDLE(hMenu, MENU));
  589. RIP(IS_VALID_WRITE_PTR(pMI, BROWSEXT_MENU_INFO *));
  590. hr = E_FAIL;
  591. pMI->idmMenu = 0;
  592. // set idmMenu, idmPlaceholder, and idmModMarker to values
  593. // reflecting whichever menu's popup we're currently handling
  594. if (GetMenuFromID(hMenuParent, FCIDM_MENU_HELP) == hMenu)
  595. {
  596. pMI->idmMenu = FCIDM_MENU_HELP;
  597. pMI->idmPlaceholder = FCIDM_HELP_EXT_PLACEHOLDER;
  598. pMI->idmModMarker = FCIDM_HELP_EXT_MOD_MARKER;
  599. }
  600. else if (GetMenuFromID(hMenuParent, FCIDM_MENU_TOOLS) == hMenu)
  601. {
  602. pMI->idmMenu = FCIDM_MENU_TOOLS;
  603. pMI->idmPlaceholder = FCIDM_TOOLS_EXT_PLACEHOLDER;
  604. pMI->idmModMarker = FCIDM_TOOLS_EXT_MOD_MARKER;
  605. }
  606. // set iInsert. using a constant insertion index
  607. // instead of always inserting by command at
  608. // the placeholder makes it easier later when
  609. // we have to stick in the final separator to
  610. // isolate the custom item group.
  611. if (pMI->idmMenu != 0)
  612. {
  613. int i;
  614. int cItems;
  615. cItems = GetMenuItemCount(hMenu);
  616. for (i = 0; i < cItems; i++)
  617. {
  618. MENUITEMINFO mii;
  619. BOOL f;
  620. mii.cbSize = sizeof(mii);
  621. mii.fMask = MIIM_ID;
  622. f = GetMenuItemInfo(hMenu, i, TRUE, &mii);
  623. if (f && mii.wID == pMI->idmPlaceholder)
  624. {
  625. pMI->iInsert = i;
  626. hr = S_OK;
  627. break;
  628. }
  629. }
  630. }
  631. return hr;
  632. }
  633. // note, this popup handler can't easily tell whether an item
  634. // has been removed from the DPA. if you remove any items from the
  635. // DPA it is your responsibility to delete them from the menu
  636. // also, if they live on a menu
  637. HRESULT CBrowserExtension::OnCustomizableMenuPopup(HMENU hMenuParent, HMENU hMenu)
  638. {
  639. HRESULT hr;
  640. BROWSEXT_MENU_INFO menuInfo;
  641. RIP(IS_VALID_HANDLE(hMenu, MENU));
  642. hr = _GetCustomMenuInfo(hMenuParent, hMenu, &menuInfo);
  643. if (SUCCEEDED(hr) && _hdpa != NULL)
  644. {
  645. BOOL fItemInserted;
  646. UINT cItems;
  647. UINT i;
  648. ASSERT(IS_VALID_HANDLE(_hdpa, DPA));
  649. fItemInserted = FALSE;
  650. // check each extension object we currently have
  651. // to see whether any of them should go into this
  652. // menu
  653. cItems = (UINT)DPA_GetPtrCount(_hdpa);
  654. for (i = 0; i < cItems; i++)
  655. {
  656. ExtensionItem * pItem;
  657. pItem = (ExtensionItem *)DPA_GetPtr(_hdpa, i);
  658. ASSERT(IS_VALID_READ_PTR(pItem, ExtensionItem));
  659. // does this item go into the menu we're currently
  660. // customizing?
  661. if (pItem->idmMenu == menuInfo.idmMenu)
  662. {
  663. MENUITEMINFO mii;
  664. IOleCommandTarget * pOCT;
  665. mii.fMask = MIIM_ID;
  666. mii.wID = pItem->idCmd;
  667. mii.cbSize = sizeof(mii);
  668. // set the MENUITEMINFO's state information, if applicable
  669. ASSERT(IS_VALID_CODE_PTR(pItem->pIBE, IBrowserExtension));
  670. hr = pItem->pIBE->QueryInterface(IID_IOleCommandTarget, (void **)&pOCT);
  671. if (SUCCEEDED(hr))
  672. {
  673. OLECMD oleCmd = {OLECMDID_OPEN,};
  674. ASSERT(IS_VALID_CODE_PTR(pOCT, IOleCommandTarget));
  675. hr = pOCT->QueryStatus(NULL, 1, &oleCmd, NULL);
  676. if (SUCCEEDED(hr))
  677. {
  678. mii.fMask |= MIIM_STATE;
  679. mii.fState = 0;
  680. // enabled state
  681. if (oleCmd.cmdf & OLECMDF_ENABLED)
  682. {
  683. mii.fState |= MFS_ENABLED;
  684. }
  685. else
  686. {
  687. mii.fState |= MFS_DISABLED;
  688. }
  689. // checked state
  690. if (oleCmd.cmdf & OLECMDF_LATCHED)
  691. {
  692. mii.fState |= MFS_CHECKED;
  693. }
  694. else
  695. {
  696. mii.fState |= MFS_UNCHECKED;
  697. }
  698. }
  699. pOCT->Release();
  700. }
  701. // get the menu text.
  702. // this changing is an unlikely scenario, but if we're truly
  703. // supporting dynamic customization, then we need to allow for
  704. // this possibility.
  705. VARIANTARG varArg;
  706. hr = pItem->pIBE->GetProperty(TMEX_MENUTEXT, &varArg);
  707. if (SUCCEEDED(hr))
  708. {
  709. BOOL fItemExists;
  710. ASSERT(varArg.vt == VT_BSTR);
  711. ASSERT(IS_VALID_STRING_PTR(varArg.bstrVal, -1));
  712. fItemExists = GetMenuItemInfo(hMenu, mii.wID, FALSE, &mii);
  713. mii.fMask |= MIIM_TYPE;
  714. mii.fType = MFT_STRING;
  715. mii.cch = SysStringLen(varArg.bstrVal);
  716. mii.dwTypeData = varArg.bstrVal;
  717. if (fItemExists)
  718. {
  719. // update the old item using current info
  720. SetMenuItemInfo(hMenu, mii.wID, FALSE, &mii);
  721. }
  722. else
  723. {
  724. // create a new item using current info
  725. if (InsertMenuItem(hMenu, menuInfo.iInsert, TRUE, &mii))
  726. {
  727. fItemInserted = TRUE;
  728. }
  729. }
  730. VariantClear(&varArg);
  731. }
  732. }
  733. }
  734. if (fItemInserted)
  735. {
  736. MENUITEMINFO mii;
  737. BOOL fModMarkerExists;
  738. // since we made an insertion, we need to insert
  739. // a separator, but only if we didn't do it already
  740. mii.cbSize = sizeof(mii);
  741. mii.fMask = 0;
  742. fModMarkerExists = GetMenuItemInfo(hMenu, menuInfo.idmModMarker, FALSE, &mii);
  743. if (!fModMarkerExists)
  744. {
  745. mii.fMask = MIIM_ID | MIIM_TYPE;
  746. mii.wID = menuInfo.idmModMarker;
  747. mii.fType = MFT_SEPARATOR;
  748. InsertMenuItem(hMenu, menuInfo.iInsert, TRUE, &mii);
  749. }
  750. }
  751. // the only thing that is guaranteed to be a complete failure
  752. // if if we failed to get the info for the menu doing the popup.
  753. // otherwise, despite the possibility that any particular insertion
  754. // attempt might have failed, there are potentially many custom
  755. // items. though some might fail, some might succeed. in either
  756. // we'll return overall success, because we successfully did the
  757. // best we could with the items that were present.
  758. // at least we didn't crash :)
  759. hr = S_OK;
  760. }
  761. return hr;
  762. }
  763. HRESULT CBrowserExtension::OnMenuSelect(UINT nCmdID)
  764. {
  765. VARIANT varArg;
  766. HRESULT hr = E_FAIL;
  767. // We better have stored our menu extensions if we are at this point
  768. ASSERT(_hdpa != NULL);
  769. int i = _GetIdpaFromCmdId(nCmdID);
  770. if (i >= 0 && i < DPA_GetPtrCount(_hdpa))
  771. {
  772. ExtensionItem* pItem = (ExtensionItem*)DPA_GetPtr(_hdpa, i);
  773. ASSERT(pItem->idmMenu != 0);
  774. hr = pItem->pIBE->GetProperty(TMEX_STATUSBARTEXT, &varArg);
  775. if (SUCCEEDED(hr))
  776. {
  777. if (varArg.vt == VT_BSTR)
  778. {
  779. // Set the Status Bar Text
  780. if (_pISB)
  781. {
  782. _pISB->SetStatusTextSB(varArg.bstrVal);
  783. }
  784. }
  785. VariantClear(&varArg);
  786. hr = S_OK;
  787. }
  788. }
  789. return hr;
  790. }
  791. // Create an image list for the Cut/Copy/Paste buttons
  792. CBrowserExtension::CImageCache CBrowserExtension::_rgImages[3];
  793. //
  794. // Get the image list for the toolbar. These image lists are shared between instances so
  795. // the caller must call _ReturnImageLists when finished with them. The index returned from this
  796. // functions is passed to _ReturnImageLists.
  797. //
  798. UINT CBrowserExtension::_GetImageLists(CImageList** ppimlDef, CImageList** ppimlHot, BOOL fSmall)
  799. {
  800. COLORREF crMask = RGB( 255, 0, 255 );
  801. BOOL bUseNewIcons = !SHUseClassicToolbarGlyphs();
  802. //
  803. // Get the index into our image cache
  804. // 16 color 16x16 (small)
  805. // 16 color 20x20
  806. // 256 color 20x20
  807. //
  808. int i = fSmall ? 0 : 1;
  809. if (!fSmall && SHGetCurColorRes() > 8)
  810. ++i;
  811. int cx = fSmall ? 16 : 20;
  812. if (!fSmall && bUseNewIcons)
  813. {
  814. cx = 24;
  815. }
  816. //
  817. // Create the images if necessary
  818. //
  819. ENTERCRITICAL;
  820. if (_rgImages[0].uiResDef == 0)
  821. {
  822. _rgImages[1].uiResDef = IDB_CLASSIC_IETOOLBAR;
  823. _rgImages[1].uiResHot = IDB_CLASSIC_IETOOLBARHOT;
  824. _rgImages[1].bShell32 = FALSE;
  825. if (bUseNewIcons)
  826. {
  827. _rgImages[0].uiResDef = IDB_TB_EXT_DEF_16;
  828. _rgImages[0].uiResHot = IDB_TB_EXT_HOT_16;
  829. _rgImages[0].bShell32 = TRUE;
  830. _rgImages[2].uiResDef = IDB_TB_EXT_DEF_24;
  831. _rgImages[2].uiResHot = IDB_TB_EXT_HOT_24;
  832. _rgImages[2].bShell32 = TRUE;
  833. }
  834. else
  835. {
  836. _rgImages[0].uiResDef = IDB_CLASSIC_IETOOLBAR16;
  837. _rgImages[0].uiResHot = IDB_CLASSIC_IETOOLBARHOT16;
  838. _rgImages[0].bShell32 = FALSE;
  839. _rgImages[2].uiResDef = IDB_CLASSIC_IETOOLBARHICOLOR;
  840. _rgImages[2].uiResHot = IDB_CLASSIC_IETOOLBARHOTHICOLOR;
  841. _rgImages[2].bShell32 = FALSE;
  842. }
  843. }
  844. if (!_rgImages[i].imlDef.HasImages())
  845. {
  846. _rgImages[i].imlDef = ImageList_LoadImage(_rgImages[i].bShell32 ? GetModuleHandle(TEXT("shell32.dll")) : HINST_THISDLL,
  847. MAKEINTRESOURCE(_rgImages[i].uiResDef),
  848. cx, 0, crMask,
  849. IMAGE_BITMAP, LR_CREATEDIBSECTION);
  850. }
  851. if (!_rgImages[i].imlHot.HasImages())
  852. {
  853. _rgImages[i].imlHot = ImageList_LoadImage(_rgImages[i].bShell32 ? GetModuleHandle(TEXT("shell32.dll")) : HINST_THISDLL,
  854. MAKEINTRESOURCE(_rgImages[i].uiResHot),
  855. cx, 0, crMask,
  856. IMAGE_BITMAP, LR_CREATEDIBSECTION);
  857. }
  858. //
  859. // Add the custom buttons to our image lists
  860. //
  861. _AddCustomImagesToImageList(_rgImages[i].imlDef, _rgImages[i].imlHot, fSmall);
  862. ++_rgImages[i].cUsage;
  863. *ppimlDef = &_rgImages[i].imlDef;
  864. *ppimlHot = &_rgImages[i].imlHot;
  865. LEAVECRITICAL;
  866. return i;
  867. }
  868. //
  869. // Called when the imagelist indicated by uiIndex is not longer used by this instance
  870. //
  871. void CBrowserExtension::_ReleaseImageLists(UINT uiIndex)
  872. {
  873. if (uiIndex >= ARRAYSIZE(_rgImages))
  874. {
  875. return;
  876. }
  877. ENTERCRITICAL;
  878. ASSERT(_rgImages[uiIndex].cUsage >= 1);
  879. // If the image lists are no longer used, we can free them
  880. if (--_rgImages[uiIndex].cUsage == 0)
  881. {
  882. _rgImages[uiIndex].imlDef.FreeImages();
  883. _rgImages[uiIndex].imlHot.FreeImages();
  884. }
  885. LEAVECRITICAL;
  886. }
  887. //+-------------------------------------------------------------------------
  888. // Constructor
  889. //--------------------------------------------------------------------------
  890. CImageList::CImageList(HIMAGELIST himl)
  891. : _himl(himl)
  892. {
  893. ASSERT(_hdpa == NULL);
  894. }
  895. //+-------------------------------------------------------------------------
  896. // Destructor
  897. //--------------------------------------------------------------------------
  898. CImageList::~CImageList()
  899. {
  900. FreeImages();
  901. }
  902. //+-------------------------------------------------------------------------
  903. // Frees an association item from our dpa
  904. //--------------------------------------------------------------------------
  905. int CImageList::_DPADestroyCallback(LPVOID p, LPVOID d)
  906. {
  907. delete (ImageAssoc*)p;
  908. return 1;
  909. }
  910. //+-------------------------------------------------------------------------
  911. // Frees our image list and inex associations
  912. //--------------------------------------------------------------------------
  913. void CImageList::FreeImages()
  914. {
  915. if (_hdpa)
  916. {
  917. DPA_DestroyCallback(_hdpa, _DPADestroyCallback, 0);
  918. _hdpa = NULL;
  919. }
  920. if (_himl)
  921. {
  922. ImageList_Destroy(_himl);
  923. _himl = NULL;
  924. }
  925. }
  926. //+-------------------------------------------------------------------------
  927. // Updates the image list
  928. //--------------------------------------------------------------------------
  929. CImageList& CImageList::operator=(HIMAGELIST himl)
  930. {
  931. if (himl != _himl)
  932. {
  933. FreeImages();
  934. _himl = himl;
  935. }
  936. return *this;
  937. }
  938. //+-------------------------------------------------------------------------
  939. // Returns the index of the images associated with rguid. Returns -1 if not
  940. // found.
  941. //--------------------------------------------------------------------------
  942. int CImageList::GetImageIndex(REFGUID rguid)
  943. {
  944. int iIndex = -1;
  945. if (_hdpa)
  946. {
  947. ASSERT(_himl);
  948. for (int i=0; i < DPA_GetPtrCount(_hdpa); ++i)
  949. {
  950. ImageAssoc* pAssoc = (ImageAssoc*)DPA_GetPtr(_hdpa, i);
  951. if (IsEqualGUID(pAssoc->guid, rguid))
  952. {
  953. return pAssoc->iImage;
  954. }
  955. }
  956. }
  957. return iIndex;
  958. }
  959. //+-------------------------------------------------------------------------
  960. // Adds the icon to the image list and returns the index. If the image is
  961. // already present, the existing index is returned. Returns -1 on failure.
  962. //--------------------------------------------------------------------------
  963. int CImageList::AddIcon(HICON hicon, REFGUID rguid)
  964. {
  965. ASSERT(hicon != NULL);
  966. // First see is we have already added this image
  967. int iIndex = GetImageIndex(rguid);
  968. if (iIndex == -1)
  969. {
  970. // Make sure we have a dpa to store our items
  971. if (NULL == _hdpa)
  972. {
  973. _hdpa = DPA_Create(5);
  974. }
  975. if (_hdpa && _himl)
  976. {
  977. // Add the icon to our image list
  978. iIndex = ImageList_AddIcon(_himl, hicon);
  979. if (iIndex != -1)
  980. {
  981. ImageAssoc* pAssoc = new ImageAssoc;
  982. if (pAssoc)
  983. {
  984. pAssoc->guid = rguid;
  985. pAssoc->iImage = iIndex;
  986. DPA_AppendPtr(_hdpa, pAssoc);
  987. }
  988. }
  989. }
  990. }
  991. return iIndex;
  992. }