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.

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