|
|
#include "priv.h"
#include "browsext.h"
#include "tbext.h"
#include <winreg.h> // For the registry walking
#include "dochost.h"
#include "resource.h"
#include <mluisupp.h>
#include <tb_ids.h>
// {DFEED31E-78ED-11d2-86BA-00C04F8EEA99}
EXTERN_C const IID IID_IToolbarExt = { 0xdfeed31e, 0x78ed, 0x11d2, { 0x86, 0xba, 0x0, 0xc0, 0x4f, 0x8e, 0xea, 0x99 } };
// {D82B85D0-78F4-11d2-86BA-00C04F8EEA99}
EXTERN_C const CLSID CLSID_PrivBrowsExtCommands = { 0xd82b85d0, 0x78f4, 0x11d2, { 0x86, 0xba, 0x0, 0xc0, 0x4f, 0x8e, 0xea, 0x99 } };
const TCHAR c_szHelpMenu[] = TEXT("help");
//+-------------------------------------------------------------------------
// Creates and instance of CBrowserExtension
//--------------------------------------------------------------------------
HRESULT CBrowserExtension_CreateInstance(IUnknown* pUnkOuter, IUnknown** ppunk, LPCOBJECTINFO poi) { *ppunk = NULL; CBrowserExtension* p = new CBrowserExtension(); if (p) { *ppunk = SAFECAST(p, IToolbarExt*); return S_OK; }
return E_OUTOFMEMORY; }
CBrowserExtension::CBrowserExtension() : _cRef(1), _uStringIndex((UINT)-1), _uiImageIndex((UINT)-1) { ASSERT(_pISB == NULL); ASSERT(_hdpa == NULL); ASSERT(_nExtButtons == 0); ASSERT(_fStringInit == FALSE); ASSERT(_fImageInit == FALSE); }
CBrowserExtension::~CBrowserExtension(void) { if (_pISB) _pISB->Release();
if (_hdpa) { _FreeItems(); DPA_Destroy(_hdpa); _hdpa = NULL; }
_ReleaseImageLists(_uiImageIndex); }
// *** IUnknown methods ***
HRESULT CBrowserExtension::QueryInterface(REFIID riid, void ** ppvObj) { static const QITAB qit[] = { QITABENT(CBrowserExtension, IToolbarExt), QITABENT(CBrowserExtension, IObjectWithSite), QITABENT(CBrowserExtension, IOleCommandTarget), { 0 }, };
return QISearch(this, qit, riid, ppvObj); }
STDMETHODIMP_(ULONG) CBrowserExtension::AddRef() { return InterlockedIncrement(&_cRef); }
STDMETHODIMP_(ULONG) CBrowserExtension::Release() { ASSERT( 0 != _cRef ); ULONG cRef = InterlockedDecrement(&_cRef); if ( 0 == cRef ) { delete this; } return cRef; }
// IToolbarExt interface functions
HRESULT CBrowserExtension::SetSite(IUnknown* pUnkSite) { HRESULT hr = S_OK;
ATOMICRELEASE(_pISB);
if (pUnkSite) { hr = pUnkSite->QueryInterface(IID_IShellBrowser, (LPVOID*)&_pISB); }
// See if we need to init ourselves
if (NULL == _hdpa) { // Real construction happens here
HRESULT hr2 = Update();
ASSERT(SUCCEEDED(hr2)); } else { // Update the site for each button/menu extension
for (int i = 0; i < DPA_GetPtrCount(_hdpa); i++) { ExtensionItem* pItem = (ExtensionItem*)DPA_GetPtr(_hdpa, i); IUnknown_SetSite(pItem->pIBE, _pISB); } } return hr; }
STDMETHODIMP CBrowserExtension::GetSite(REFIID riid, void ** ppvSite) { HRESULT hr = S_OK; *ppvSite = NULL;
if (_pISB) { hr = _pISB->QueryInterface(riid, ppvSite); } return hr; }
HRESULT CBrowserExtension::GetNumButtons(UINT* pButtons) { ASSERT(pButtons); *pButtons = _nExtButtons; return S_OK; }
HRESULT CBrowserExtension::InitButtons(IExplorerToolbar* pxtb, UINT* puStringIndex, const GUID* pguidCommandGroup) { ASSERT(pxtb);
UINT uiSize; pxtb->GetBitmapSize(&uiSize); int cx = LOWORD(uiSize);
// Get the image lists for the current button size and screen resolution
CImageList* pimlDef; CImageList* pimlHot; UINT uiImageIndexOld = _uiImageIndex; _uiImageIndex = _GetImageLists(&pimlDef, &pimlHot, cx < 20); pxtb->SetImageList(pguidCommandGroup, *pimlDef, *pimlHot, NULL);
// Free the previously used image list
_ReleaseImageLists(uiImageIndexOld);
// Add the button text to the toolbar
if (_uStringIndex == (UINT)-1) { LRESULT iAddResult = 0; // result of adding the string buffer to the toolbar string list
HRESULT hr = pxtb->AddString(pguidCommandGroup, MLGetHinst(), IDS_BROWSER_TB_LABELS, &iAddResult); _uStringIndex = (UINT)iAddResult; _AddCustomStringsToBuffer(pxtb, pguidCommandGroup); }
*puStringIndex = _uStringIndex; return S_OK; }
CBrowserExtension::ExtensionItem* CBrowserExtension::_FindItem(REFGUID rguid) { ExtensionItem* pFound = NULL; if (NULL != _hdpa) { for (int i = 0; i < DPA_GetPtrCount(_hdpa); i++) { ExtensionItem* pItem = (ExtensionItem*)DPA_GetPtr(_hdpa, i);
if (pItem && IsEqualGUID(pItem->guid, rguid)) { pFound = pItem; break; } } } return pFound; }
void CBrowserExtension::_AddItem(HKEY hkeyExtensions, LPCWSTR pszGuidItem, REFGUID rguidItem) { // Create the dpa used to store our items
if (NULL == _hdpa) { _hdpa = DPA_Create(5); if (NULL == _hdpa) { return; } }
HKEY hkeyThisExtension;
if (RegOpenKeyEx(hkeyExtensions, pszGuidItem, 0, KEY_READ, &hkeyThisExtension) == ERROR_SUCCESS) { // Get the clsid of the object
WCHAR szCLSID[64]; ULONG cbCLSID = SIZEOF(szCLSID); CLSID clsidCustomButton;
if (SUCCEEDED(RegQueryValueEx(hkeyThisExtension, TEXT("clsid"), NULL, NULL, (unsigned char *)&szCLSID, &cbCLSID)) && SUCCEEDED(CLSIDFromString(szCLSID, &clsidCustomButton))) { IBrowserExtension * pibeTemp;
// Check for our internal object. Note that our CoCreateInctance wrapper
// compares to the address of the global clsid, so we want to use the global
// guid.
const CLSID* pclsid = &clsidCustomButton; if (IsEqualGUID(clsidCustomButton, CLSID_ToolbarExtExec)) { pclsid = &CLSID_ToolbarExtExec; } else if (IsEqualGUID(clsidCustomButton, CLSID_ToolbarExtBand)) { pclsid = &CLSID_ToolbarExtBand; }
// Create the extension object
if (SUCCEEDED(CoCreateInstance(*pclsid, NULL, CLSCTX_INPROC_SERVER, IID_IBrowserExtension, (void **)&pibeTemp))) { if (SUCCEEDED(pibeTemp->Init(rguidItem))) { // Add this item to our array
ExtensionItem* pItem = new ExtensionItem; if (pItem) { if (DPA_AppendPtr(_hdpa, pItem) != -1) { VARIANTARG varArg;
pItem->idCmd = _GetCmdIdFromClsid(pszGuidItem); pItem->pIBE = pibeTemp; pItem->guid = rguidItem; pibeTemp->AddRef();
// See if it's a button
if (SUCCEEDED(pibeTemp->GetProperty(TBEX_BUTTONTEXT, NULL))) { _nExtButtons++; pItem->fButton = TRUE;
// See if the button default to visible on the toolbar
if (SUCCEEDED(pibeTemp->GetProperty(TBEX_DEFAULTVISIBLE, &varArg))) { ASSERT(varArg.vt == VT_BOOL); pItem->fVisible = (varArg.boolVal == VARIANT_TRUE); } }
// set the target menu
pItem->idmMenu = 0; if (SUCCEEDED(pibeTemp->GetProperty(TMEX_MENUTEXT, NULL))) { if (SUCCEEDED(pibeTemp->GetProperty(TMEX_CUSTOM_MENU, &varArg))) { ASSERT(varArg.vt == VT_BSTR); ASSERT(IS_VALID_STRING_PTR(varArg.bstrVal, -1)); if (!StrCmpNI(varArg.bstrVal, c_szHelpMenu, ARRAYSIZE(c_szHelpMenu))) { pItem->idmMenu = FCIDM_MENU_HELP; } VariantClear(&varArg); }
if (pItem->idmMenu == 0) { pItem->idmMenu = FCIDM_MENU_TOOLS; } }
// Pass the site to the object
IUnknown_SetSite(pibeTemp, _pISB); } else { delete pItem; } } }
// This will free pibeTemp if we didn't store it away
pibeTemp->Release(); } } RegCloseKey(hkeyThisExtension); } }
//
// All real construction happens here. In theory this function can be called upon a SysINIChange to update our
// custom toolbar cached information. This has not been tested. This opens the Extensions section of the registry
// enumerates all of the subkeys. Attempts to CoCreate each one. Upon successful CoCreation it calls
// IObjectWithSite::SetSite(IShellBrowser), if it is implemented. Next IBrowserExtension::Init is called. Finally,
// IBrowserExtension::GetProperty(TBEX_BUTTONTEXT, NULL) is called looking for a S_OK to insure that the control in
// question is a Toolbar Button (as opposed to a tools menu item, or...)
//
HRESULT CBrowserExtension::Update() { WCHAR szItemGuid[64]; // sufficient for {clsid}
DWORD cbItemGuid; GUID guidItem; HRESULT hr = S_OK;
// Free previous items
_nExtButtons = 0; _nExtToolsMenuItems = 0; _FreeItems();
// First add extensions from HKCU
HKEY hkeyExtensions; if (RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Internet Explorer\\Extensions"), 0, KEY_READ, &hkeyExtensions) == ERROR_SUCCESS) { cbItemGuid = sizeof(szItemGuid); for (int iKey = 0; RegEnumKeyEx(hkeyExtensions, iKey, szItemGuid, &cbItemGuid, NULL, NULL, NULL, NULL) != ERROR_NO_MORE_ITEMS; iKey++) { if (SUCCEEDED(CLSIDFromString(szItemGuid, &guidItem))) { _AddItem(hkeyExtensions, szItemGuid, guidItem); } cbItemGuid = sizeof(szItemGuid); }
RegCloseKey(hkeyExtensions); }
// Next add any unique items from HKLM
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("Software\\Microsoft\\Internet Explorer\\Extensions"), 0, KEY_READ, &hkeyExtensions) == ERROR_SUCCESS) { cbItemGuid = sizeof(szItemGuid); for (int iKey = 0; RegEnumKeyEx(hkeyExtensions, iKey, szItemGuid, &cbItemGuid, NULL, NULL, NULL, NULL) != ERROR_NO_MORE_ITEMS; iKey++) { if (SUCCEEDED(CLSIDFromString(szItemGuid, &guidItem))) { if (_FindItem(guidItem) == NULL) { _AddItem(hkeyExtensions, szItemGuid, guidItem); } } cbItemGuid = sizeof(szItemGuid); }
RegCloseKey(hkeyExtensions); }
return hr; }
//
// This takes a TBBUTTON[] and fills in the Custom Buttons. A couple of usage points:
// (1) The caller should allocate a TBBUTTON[] big enough for NUM_STD_BUTTONS + GetNumExtButtons()
// Then they should copy the standard buttons into the array, and pass the pointer to the remainder
// of the array here.
// (2) This function should *by design* never be called before AddCustomImagesToImageList and
// AddCustomStringsToBuffer have both been called. An attempt to do so in DEBUG mode will hit
// a break point.
//
HRESULT CBrowserExtension::GetButtons(TBBUTTON * tbArr, int nNumButtons, BOOL fInit) { ASSERT(_fStringInit && _fImageInit);
if (_hdpa) { ASSERT(nNumButtons == _nExtButtons); ASSERT(tbArr != NULL) int iBtn = 0;
for (int i = 0; i < DPA_GetPtrCount(_hdpa); i++) { ExtensionItem* pItem = (ExtensionItem*)DPA_GetPtr(_hdpa, i); if (!pItem->fButton) continue;
// We use the MAKELONG(n, 1) to insure that we are using the alternate image list.
tbArr[iBtn].iBitmap = MAKELONG(pItem->iImageID, 1); tbArr[iBtn].idCommand = pItem->idCmd; tbArr[iBtn].fsState = TBSTATE_ENABLED; tbArr[iBtn].fsStyle = BTNS_BUTTON; tbArr[iBtn].dwData = 0; tbArr[iBtn].iString = pItem->iStringID;
//
// Default to hidden during initialization so that it defaults to the left well
// of the the customize dialog (defaults off the toolbar)
//
if (fInit && !pItem->fVisible) { tbArr[iBtn].fsState = TBSTATE_HIDDEN; }
++iBtn; } } return S_OK; }
//
// This function takes the ImageLists for hot and normal icons and adds the appropriate icon to each
// list for each custom toolbar button. The resultant ImageID is then stored in our _rgExtensionItem struct
// so that the IDs can be placed in a TBBUTTON[] when AddExtButtonsTBArray is called.
//
HRESULT CBrowserExtension::_AddCustomImagesToImageList(CImageList& rimlNormal, CImageList& rimlHot, BOOL fSmallIcons) { #ifdef DEBUG
_fImageInit = TRUE; #endif DEBUG
if (rimlNormal.HasImages() && rimlHot.HasImages() && NULL != _hdpa) { for (int i = 0; i < DPA_GetPtrCount(_hdpa); i++) { ExtensionItem* pItem = (ExtensionItem*)DPA_GetPtr(_hdpa, i); if (!pItem->fButton) continue;
VARIANTARG varArg;
pItem->iImageID = rimlNormal.GetImageIndex(pItem->guid); if (-1 == pItem->iImageID && SUCCEEDED(pItem->pIBE->GetProperty((fSmallIcons ? TBEX_GRAYICONSM : TBEX_GRAYICON), &varArg))) { if (varArg.vt == VT_BYREF) { pItem->iImageID = rimlNormal.AddIcon((HICON)varArg.byref, pItem->guid); } else if (varArg.vt == VT_I4) { // It's one of our built-in images
pItem->iImageID = varArg.lVal; } else { TraceMsg(TF_ALWAYS, "Button doesn't have an image associated"); } }
int iHot = rimlHot.GetImageIndex(pItem->guid); if (-1 == iHot && SUCCEEDED(pItem->pIBE->GetProperty((fSmallIcons ? TBEX_HOTICONSM : TBEX_HOTICON), &varArg))) { if (varArg.vt == VT_BYREF) { iHot = rimlHot.AddIcon((HICON)varArg.byref, pItem->guid); } else if (varArg.vt == VT_I4) { // It's one of our built-in images
iHot = varArg.lVal; } else { TraceMsg(TF_ALWAYS, "Button doesn't have an image associated"); } }
if (iHot!=pItem->iImageID) { TraceMsg(TF_ALWAYS, "ButtonExtension: iHot doesn't match iImageID"); } } }
return S_OK; }
//
// This function takes the StringList and adds the caption (ToolbarText) for each of the custom toolbar buttons
// to it. The resultant StringID is then stored in our _rgExtensionItem struct so that the ID can be placed in
// a TBBUTTON[] when AddExtButtonsTBArray is called.
//
HRESULT CBrowserExtension::_AddCustomStringsToBuffer(IExplorerToolbar * pxtb, const GUID* pguidCommandGroup) { #ifdef DEBUG
_fStringInit = TRUE; #endif DEBUG
if (NULL != _hdpa) { for (int i = 0; i < DPA_GetPtrCount(_hdpa); i++) { ExtensionItem* pItem = (ExtensionItem*)DPA_GetPtr(_hdpa, i); if (!pItem->fButton) continue;
VARIANTARG varArg;
if (SUCCEEDED(pItem->pIBE->GetProperty(TBEX_BUTTONTEXT, &varArg))) { // We need to double-null terminate the string!
WCHAR szBuf[70]; // should be ample for button text!
ZeroMemory(szBuf, sizeof(szBuf)); StringCchCopy(szBuf, ARRAYSIZE(szBuf) - 2, varArg.bstrVal); LRESULT iResult;
if (SUCCEEDED(pxtb->AddString(pguidCommandGroup, 0, (LPARAM)szBuf, &iResult))) { pItem->iStringID = (SHORT)iResult; }
VariantClear(&varArg); } } }
return S_OK; }
int CBrowserExtension::_GetCmdIdFromClsid(LPCWSTR pszGuid) { DWORD dwDisposition; HRESULT hr = S_OK; int nReturn = DVIDM_MENUEXT_FIRST;
HKEY hkeyExtensionMapping; if (RegCreateKeyEx(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Internet Explorer\\Extensions\\CmdMapping"), 0, NULL, 0, KEY_READ | KEY_WRITE, NULL, &hkeyExtensionMapping, &dwDisposition) == ERROR_SUCCESS) { DWORD dwType = REG_DWORD, dwData, cbData = sizeof(dwData); if ( (SHQueryValueEx(hkeyExtensionMapping, pszGuid, NULL, &dwType, &dwData, &cbData) == ERROR_SUCCESS) && (dwType == REG_DWORD) ) { //the item has a mapping
nReturn = dwData; } else { //it's a new item, get and store the next available id in the default value of the Mapping key
if ( (SHQueryValueEx(hkeyExtensionMapping, L"NextId", NULL, &dwType, &dwData, &cbData) != ERROR_SUCCESS) || (dwType != REG_DWORD) ) { dwData = DVIDM_MENUEXT_FIRST; } nReturn = dwData;
dwType = REG_DWORD; cbData = sizeof(dwData); EVAL(SHSetValueW(hkeyExtensionMapping, NULL, pszGuid, dwType, &dwData, cbData) == ERROR_SUCCESS);
dwData++; ASSERT(dwData < DVIDM_MENUEXT_LAST); //ugh, we've used up our whole range. we need to look for holes.
EVAL(SHSetValueW(hkeyExtensionMapping, NULL, L"NextId", dwType, &dwData, cbData) == ERROR_SUCCESS); } RegCloseKey(hkeyExtensionMapping); }
return nReturn; }
int CBrowserExtension::_GetIdpaFromCmdId(int nCmdId) { if (NULL != _hdpa) { for (int i = 0; i < DPA_GetPtrCount(_hdpa); i++) { ExtensionItem* pItem = (ExtensionItem*)DPA_GetPtr(_hdpa, i); if (pItem->idCmd == nCmdId) return i; } } return -1; }
// *** IOleCommandTarget methods ***
HRESULT CBrowserExtension::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG *pvarargIn, VARIANTARG *pvarargOut) { if (!pguidCmdGroup) return E_INVALIDARG;
if (IsEqualGUID(*pguidCmdGroup, CLSID_ToolbarExtButtons)) { int iCmd = _GetIdpaFromCmdId(nCmdID);
if (iCmd >= 0 && iCmd < DPA_GetPtrCount(_hdpa)) { ExtensionItem* pItem = (ExtensionItem*)DPA_GetPtr(_hdpa, iCmd); if (pItem) return IUnknown_Exec(pItem->pIBE, NULL, 0, 0, NULL, NULL); } } else if (IsEqualGUID(*pguidCmdGroup, CLSID_PrivBrowsExtCommands)) { switch (nCmdID) { case PBEC_GETSTRINGINDEX: if (pvarargIn && pvarargIn->vt == VT_I4) { pvarargIn->lVal = _uStringIndex; return S_OK; } break; } }
return E_FAIL; }
HRESULT CBrowserExtension::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD rgCmds[], OLECMDTEXT *pcmdtext) { if (!pguidCmdGroup) return E_INVALIDARG;
if (IsEqualGUID(*pguidCmdGroup, CLSID_ToolbarExtButtons)) { for (ULONG i = 0; i < cCmds; i++) { int iCmd = _GetIdpaFromCmdId(rgCmds[i].cmdID);
if (iCmd >= 0 && iCmd < DPA_GetPtrCount(_hdpa)) { ExtensionItem* pItem = (ExtensionItem*)DPA_GetPtr(_hdpa, iCmd); if (pItem) { // I don't think this has ever worked. The command id
// isn't the same as the one we use in Exec.
IUnknown_QueryStatus(pItem->pIBE, pguidCmdGroup, 1, &rgCmds[i], pcmdtext); } } }
return S_OK; }
return E_FAIL; }
//
// This function is a helper for the destructor. It is also called by Update() so that if we are ever asked
// to Update() we first kill all of our cached information and then we go to the registry...
//
void CBrowserExtension::_FreeItems(void) { if (_hdpa) { for (int i = DPA_GetPtrCount(_hdpa) - 1; i >= 0; --i) { ExtensionItem* pItem = (ExtensionItem*)DPA_DeletePtr(_hdpa, i);
IUnknown_SetSite(pItem->pIBE, NULL); pItem->pIBE->Release(); delete pItem; } } }
// this help function is used to isolate the menu-specific
// processing. after using this helper to fill out the BROWSEXT_MENU_INFO
// struct, the OnCustomizableMenuPopup is able to do menu-inspecific
// processing.
HRESULT CBrowserExtension::_GetCustomMenuInfo(HMENU hMenuParent, HMENU hMenu, BROWSEXT_MENU_INFO * pMI) { HRESULT hr;
RIP(IS_VALID_HANDLE(hMenuParent, MENU)); RIP(IS_VALID_HANDLE(hMenu, MENU)); RIP(IS_VALID_WRITE_PTR(pMI, BROWSEXT_MENU_INFO *));
hr = E_FAIL; pMI->idmMenu = 0;
// set idmMenu, idmPlaceholder, and idmModMarker to values
// reflecting whichever menu's popup we're currently handling
if (GetMenuFromID(hMenuParent, FCIDM_MENU_HELP) == hMenu) { pMI->idmMenu = FCIDM_MENU_HELP; pMI->idmPlaceholder = FCIDM_HELP_EXT_PLACEHOLDER; pMI->idmModMarker = FCIDM_HELP_EXT_MOD_MARKER; } else if (GetMenuFromID(hMenuParent, FCIDM_MENU_TOOLS) == hMenu) { pMI->idmMenu = FCIDM_MENU_TOOLS; pMI->idmPlaceholder = FCIDM_TOOLS_EXT_PLACEHOLDER; pMI->idmModMarker = FCIDM_TOOLS_EXT_MOD_MARKER; }
// set iInsert. using a constant insertion index
// instead of always inserting by command at
// the placeholder makes it easier later when
// we have to stick in the final separator to
// isolate the custom item group.
if (pMI->idmMenu != 0) { int i; int cItems;
cItems = GetMenuItemCount(hMenu);
for (i = 0; i < cItems; i++) { MENUITEMINFO mii; BOOL f;
mii.cbSize = sizeof(mii); mii.fMask = MIIM_ID;
f = GetMenuItemInfo(hMenu, i, TRUE, &mii);
if (f && mii.wID == pMI->idmPlaceholder) { pMI->iInsert = i; hr = S_OK; break; } } }
return hr; }
// note, this popup handler can't easily tell whether an item
// has been removed from the DPA. if you remove any items from the
// DPA it is your responsibility to delete them from the menu
// also, if they live on a menu
HRESULT CBrowserExtension::OnCustomizableMenuPopup(HMENU hMenuParent, HMENU hMenu) { HRESULT hr; BROWSEXT_MENU_INFO menuInfo;
RIP(IS_VALID_HANDLE(hMenu, MENU));
hr = _GetCustomMenuInfo(hMenuParent, hMenu, &menuInfo); if (SUCCEEDED(hr) && _hdpa != NULL) { BOOL fItemInserted; UINT cItems; UINT i;
ASSERT(IS_VALID_HANDLE(_hdpa, DPA));
fItemInserted = FALSE;
// check each extension object we currently have
// to see whether any of them should go into this
// menu
cItems = (UINT)DPA_GetPtrCount(_hdpa);
for (i = 0; i < cItems; i++) { ExtensionItem * pItem;
pItem = (ExtensionItem *)DPA_GetPtr(_hdpa, i); ASSERT(IS_VALID_READ_PTR(pItem, ExtensionItem));
// does this item go into the menu we're currently
// customizing?
if (pItem->idmMenu == menuInfo.idmMenu) { MENUITEMINFO mii; IOleCommandTarget * pOCT;
mii.fMask = MIIM_ID; mii.wID = pItem->idCmd; mii.cbSize = sizeof(mii);
// set the MENUITEMINFO's state information, if applicable
ASSERT(IS_VALID_CODE_PTR(pItem->pIBE, IBrowserExtension));
hr = pItem->pIBE->QueryInterface(IID_IOleCommandTarget, (void **)&pOCT); if (SUCCEEDED(hr)) { OLECMD oleCmd = {OLECMDID_OPEN,};
ASSERT(IS_VALID_CODE_PTR(pOCT, IOleCommandTarget));
hr = pOCT->QueryStatus(NULL, 1, &oleCmd, NULL); if (SUCCEEDED(hr)) { mii.fMask |= MIIM_STATE; mii.fState = 0;
// enabled state
if (oleCmd.cmdf & OLECMDF_ENABLED) { mii.fState |= MFS_ENABLED; } else { mii.fState |= MFS_DISABLED; }
// checked state
if (oleCmd.cmdf & OLECMDF_LATCHED) { mii.fState |= MFS_CHECKED; } else { mii.fState |= MFS_UNCHECKED; } }
pOCT->Release(); }
// get the menu text.
// this changing is an unlikely scenario, but if we're truly
// supporting dynamic customization, then we need to allow for
// this possibility.
VARIANTARG varArg;
hr = pItem->pIBE->GetProperty(TMEX_MENUTEXT, &varArg); if (SUCCEEDED(hr)) { BOOL fItemExists;
ASSERT(varArg.vt == VT_BSTR); ASSERT(IS_VALID_STRING_PTR(varArg.bstrVal, -1));
fItemExists = GetMenuItemInfo(hMenu, mii.wID, FALSE, &mii);
mii.fMask |= MIIM_TYPE; mii.fType = MFT_STRING; mii.cch = SysStringLen(varArg.bstrVal); mii.dwTypeData = varArg.bstrVal;
if (fItemExists) { // update the old item using current info
SetMenuItemInfo(hMenu, mii.wID, FALSE, &mii); } else { // create a new item using current info
if (InsertMenuItem(hMenu, menuInfo.iInsert, TRUE, &mii)) { fItemInserted = TRUE; } }
VariantClear(&varArg); }
} }
if (fItemInserted) { MENUITEMINFO mii; BOOL fModMarkerExists;
// since we made an insertion, we need to insert
// a separator, but only if we didn't do it already
mii.cbSize = sizeof(mii); mii.fMask = 0;
fModMarkerExists = GetMenuItemInfo(hMenu, menuInfo.idmModMarker, FALSE, &mii);
if (!fModMarkerExists) { mii.fMask = MIIM_ID | MIIM_TYPE; mii.wID = menuInfo.idmModMarker; mii.fType = MFT_SEPARATOR;
InsertMenuItem(hMenu, menuInfo.iInsert, TRUE, &mii); } }
// the only thing that is guaranteed to be a complete failure
// if if we failed to get the info for the menu doing the popup.
// otherwise, despite the possibility that any particular insertion
// attempt might have failed, there are potentially many custom
// items. though some might fail, some might succeed. in either
// we'll return overall success, because we successfully did the
// best we could with the items that were present.
// at least we didn't crash :)
hr = S_OK; }
return hr; }
HRESULT CBrowserExtension::OnMenuSelect(UINT nCmdID) { VARIANT varArg; HRESULT hr = E_FAIL;
// We better have stored our menu extensions if we are at this point
ASSERT(_hdpa != NULL); int i = _GetIdpaFromCmdId(nCmdID); if (i >= 0 && i < DPA_GetPtrCount(_hdpa)) { ExtensionItem* pItem = (ExtensionItem*)DPA_GetPtr(_hdpa, i); ASSERT(pItem->idmMenu != 0);
hr = pItem->pIBE->GetProperty(TMEX_STATUSBARTEXT, &varArg); if (SUCCEEDED(hr)) { if (varArg.vt == VT_BSTR) { // Set the Status Bar Text
if (_pISB) { _pISB->SetStatusTextSB(varArg.bstrVal); } }
VariantClear(&varArg); hr = S_OK; } } return hr; }
// Create an image list for the Cut/Copy/Paste buttons
CBrowserExtension::CImageCache CBrowserExtension::_rgImages[3];
//
// Get the image list for the toolbar. These image lists are shared between instances so
// the caller must call _ReturnImageLists when finished with them. The index returned from this
// functions is passed to _ReturnImageLists.
//
UINT CBrowserExtension::_GetImageLists(CImageList** ppimlDef, CImageList** ppimlHot, BOOL fSmall) { COLORREF crMask = RGB( 255, 0, 255 ); BOOL bUseNewIcons = !SHUseClassicToolbarGlyphs();
//
// Get the index into our image cache
// 16 color 16x16 (small)
// 16 color 20x20
// 256 color 20x20
//
int i = fSmall ? 0 : 1; if (!fSmall && SHGetCurColorRes() > 8) ++i;
int cx = fSmall ? 16 : 20;
if (!fSmall && bUseNewIcons) { cx = 24; }
//
// Create the images if necessary
//
ENTERCRITICAL;
if (_rgImages[0].uiResDef == 0) {
_rgImages[1].uiResDef = IDB_CLASSIC_IETOOLBAR; _rgImages[1].uiResHot = IDB_CLASSIC_IETOOLBARHOT; _rgImages[1].bShell32 = FALSE;
if (bUseNewIcons) { _rgImages[0].uiResDef = IDB_TB_EXT_DEF_16; _rgImages[0].uiResHot = IDB_TB_EXT_HOT_16; _rgImages[0].bShell32 = TRUE; _rgImages[2].uiResDef = IDB_TB_EXT_DEF_24; _rgImages[2].uiResHot = IDB_TB_EXT_HOT_24; _rgImages[2].bShell32 = TRUE; } else { _rgImages[0].uiResDef = IDB_CLASSIC_IETOOLBAR16; _rgImages[0].uiResHot = IDB_CLASSIC_IETOOLBARHOT16; _rgImages[0].bShell32 = FALSE; _rgImages[2].uiResDef = IDB_CLASSIC_IETOOLBARHICOLOR; _rgImages[2].uiResHot = IDB_CLASSIC_IETOOLBARHOTHICOLOR; _rgImages[2].bShell32 = FALSE; } }
if (!_rgImages[i].imlDef.HasImages()) { _rgImages[i].imlDef = ImageList_LoadImage(_rgImages[i].bShell32 ? GetModuleHandle(TEXT("shell32.dll")) : HINST_THISDLL, MAKEINTRESOURCE(_rgImages[i].uiResDef), cx, 0, crMask, IMAGE_BITMAP, LR_CREATEDIBSECTION); }
if (!_rgImages[i].imlHot.HasImages()) { _rgImages[i].imlHot = ImageList_LoadImage(_rgImages[i].bShell32 ? GetModuleHandle(TEXT("shell32.dll")) : HINST_THISDLL, MAKEINTRESOURCE(_rgImages[i].uiResHot), cx, 0, crMask, IMAGE_BITMAP, LR_CREATEDIBSECTION); }
//
// Add the custom buttons to our image lists
//
_AddCustomImagesToImageList(_rgImages[i].imlDef, _rgImages[i].imlHot, fSmall);
++_rgImages[i].cUsage;
*ppimlDef = &_rgImages[i].imlDef; *ppimlHot = &_rgImages[i].imlHot; LEAVECRITICAL;
return i; }
//
// Called when the imagelist indicated by uiIndex is not longer used by this instance
//
void CBrowserExtension::_ReleaseImageLists(UINT uiIndex) { if (uiIndex >= ARRAYSIZE(_rgImages)) { return; }
ENTERCRITICAL;
ASSERT(_rgImages[uiIndex].cUsage >= 1);
// If the image lists are no longer used, we can free them
if (--_rgImages[uiIndex].cUsage == 0) { _rgImages[uiIndex].imlDef.FreeImages(); _rgImages[uiIndex].imlHot.FreeImages(); } LEAVECRITICAL; }
//+-------------------------------------------------------------------------
// Constructor
//--------------------------------------------------------------------------
CImageList::CImageList(HIMAGELIST himl) : _himl(himl) { ASSERT(_hdpa == NULL); }
//+-------------------------------------------------------------------------
// Destructor
//--------------------------------------------------------------------------
CImageList::~CImageList() { FreeImages(); }
//+-------------------------------------------------------------------------
// Frees an association item from our dpa
//--------------------------------------------------------------------------
int CImageList::_DPADestroyCallback(LPVOID p, LPVOID d) { delete (ImageAssoc*)p; return 1; }
//+-------------------------------------------------------------------------
// Frees our image list and inex associations
//--------------------------------------------------------------------------
void CImageList::FreeImages() { if (_hdpa) { DPA_DestroyCallback(_hdpa, _DPADestroyCallback, 0); _hdpa = NULL; } if (_himl) { ImageList_Destroy(_himl); _himl = NULL; } }
//+-------------------------------------------------------------------------
// Updates the image list
//--------------------------------------------------------------------------
CImageList& CImageList::operator=(HIMAGELIST himl) { if (himl != _himl) { FreeImages(); _himl = himl; } return *this; }
//+-------------------------------------------------------------------------
// Returns the index of the images associated with rguid. Returns -1 if not
// found.
//--------------------------------------------------------------------------
int CImageList::GetImageIndex(REFGUID rguid) { int iIndex = -1;
if (_hdpa) { ASSERT(_himl);
for (int i=0; i < DPA_GetPtrCount(_hdpa); ++i) { ImageAssoc* pAssoc = (ImageAssoc*)DPA_GetPtr(_hdpa, i); if (IsEqualGUID(pAssoc->guid, rguid)) { return pAssoc->iImage; } } } return iIndex; }
//+-------------------------------------------------------------------------
// Adds the icon to the image list and returns the index. If the image is
// already present, the existing index is returned. Returns -1 on failure.
//--------------------------------------------------------------------------
int CImageList::AddIcon(HICON hicon, REFGUID rguid) { ASSERT(hicon != NULL);
// First see is we have already added this image
int iIndex = GetImageIndex(rguid); if (iIndex == -1) { // Make sure we have a dpa to store our items
if (NULL == _hdpa) { _hdpa = DPA_Create(5); }
if (_hdpa && _himl) { // Add the icon to our image list
iIndex = ImageList_AddIcon(_himl, hicon); if (iIndex != -1) { ImageAssoc* pAssoc = new ImageAssoc; if (pAssoc) { pAssoc->guid = rguid; pAssoc->iImage = iIndex; DPA_AppendPtr(_hdpa, pAssoc); } } } } return iIndex; }
|