857 lines
23 KiB
C++
857 lines
23 KiB
C++
////////////////////////////////////////////////////////////////////////////
|
|
// File: TBExt.cpp (toolbar extension classes)
|
|
// Author: Karim Farouki
|
|
//
|
|
// We define here three classes:
|
|
// (1) CToolbarExt a base class that takes care of the
|
|
// button work for our custom extensions
|
|
// (2) CToolbarExtBand the object which deals with custom
|
|
// buttons that plug into bands
|
|
// (3) CToolbarExtExec the object which deals with custom
|
|
// buttons (or tools menu items) that exec stuff.
|
|
//
|
|
// The latter two are derived from the former
|
|
#include "priv.h"
|
|
#include <mshtmcid.h>
|
|
#include "tbext.h"
|
|
|
|
|
|
//////////////////////////////
|
|
// Class CToolbarExt
|
|
//
|
|
// This is the base class from which CToolbarExtBand and CToolbarExtExec
|
|
// both inherit. It takes care of all the ToolbarButton specific stuff
|
|
// like lazy loading the appropriate icons, and keeping track of the button
|
|
// text.
|
|
|
|
// Constructor / Destructor
|
|
//
|
|
CToolbarExt::CToolbarExt() : _cRef(1)
|
|
{
|
|
ASSERT(_hIcon == NULL);
|
|
ASSERT(_hIconSm == NULL);
|
|
ASSERT(_hHotIcon == NULL);
|
|
ASSERT(_hHotIconSm == NULL);
|
|
ASSERT(_bstrButtonText == NULL);
|
|
ASSERT(_bstrToolTip == NULL);
|
|
ASSERT(_hkeyThisExtension == NULL);
|
|
ASSERT(_hkeyCurrentLang == NULL);
|
|
ASSERT(_pisb == NULL);
|
|
|
|
DllAddRef();
|
|
}
|
|
|
|
// Destructor
|
|
//
|
|
CToolbarExt::~CToolbarExt()
|
|
{
|
|
if (_pisb)
|
|
_pisb->Release();
|
|
|
|
if (_bstrButtonText)
|
|
SysFreeString(_bstrButtonText);
|
|
|
|
if (_bstrToolTip)
|
|
SysFreeString(_bstrToolTip);
|
|
|
|
if (_hIcon)
|
|
DestroyIcon(_hIcon);
|
|
|
|
if (_hIconSm)
|
|
DestroyIcon(_hIconSm);
|
|
|
|
if (_hHotIcon)
|
|
DestroyIcon(_hHotIcon);
|
|
|
|
if (_hHotIconSm)
|
|
DestroyIcon(_hHotIconSm);
|
|
|
|
if (_hkeyThisExtension)
|
|
RegCloseKey(_hkeyThisExtension);
|
|
|
|
if (_hkeyCurrentLang)
|
|
RegCloseKey(_hkeyCurrentLang);
|
|
|
|
DllRelease();
|
|
}
|
|
|
|
|
|
// IUnknown implementation
|
|
//
|
|
STDMETHODIMP CToolbarExt::QueryInterface(const IID& iid, void** ppv)
|
|
{
|
|
if (iid == IID_IUnknown)
|
|
*ppv = static_cast<IBrowserExtension*>(this);
|
|
else if (iid == IID_IBrowserExtension)
|
|
*ppv = static_cast<IBrowserExtension*>(this);
|
|
else if (iid == IID_IOleCommandTarget)
|
|
*ppv = static_cast<IOleCommandTarget*>(this);
|
|
else if (iid == IID_IObjectWithSite)
|
|
*ppv = static_cast<IObjectWithSite*>(this);
|
|
else
|
|
{
|
|
*ppv = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
reinterpret_cast<IUnknown*>(*ppv)->AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CToolbarExt::AddRef()
|
|
{
|
|
return InterlockedIncrement(&_cRef);
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CToolbarExt::Release()
|
|
{
|
|
if (InterlockedDecrement(&_cRef) == 0)
|
|
{
|
|
delete this;
|
|
return 0;
|
|
}
|
|
return _cRef;
|
|
}
|
|
|
|
// IBrowserExtension::Init Implementation. We'll read the ButtonText here but wait on the icons until
|
|
// a specific variant of the icon is requested.
|
|
STDMETHODIMP CToolbarExt::Init(REFGUID rguid)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
LPOLESTR pszGUID;
|
|
|
|
if (SUCCEEDED(StringFromCLSID(rguid, &pszGUID)))
|
|
{
|
|
//Open the extension reg key associated with this guid
|
|
WCHAR szKey[MAX_PATH];
|
|
StrCpyN(szKey, TEXT("Software\\Microsoft\\Internet Explorer\\Extensions\\"), ARRAYSIZE(szKey));
|
|
StrCatBuff(szKey, pszGUID, ARRAYSIZE(szKey));
|
|
|
|
// We will keep _hkeyThisExtension around... it will be closed in the destructor!
|
|
if (RegOpenKeyEx(HKEY_CURRENT_USER, szKey, 0, KEY_READ, &_hkeyThisExtension) == ERROR_SUCCESS ||
|
|
RegOpenKeyEx(HKEY_LOCAL_MACHINE, szKey, 0, KEY_READ, &_hkeyThisExtension) == ERROR_SUCCESS)
|
|
{
|
|
// See if there is a subkey for the current language
|
|
LANGID langid = MLGetUILanguage();
|
|
WCHAR szBuff[MAX_PATH];
|
|
wnsprintf(szBuff, ARRAYSIZE(szBuff), L"Lang%04x", langid);
|
|
RegOpenKeyEx(_hkeyThisExtension, szBuff, 0, KEY_READ, &_hkeyCurrentLang);
|
|
|
|
// Now get the button text
|
|
_RegReadString(_hkeyThisExtension, TEXT("ButtonText"), &_bstrButtonText);
|
|
}
|
|
|
|
CoTaskMemFree(pszGUID);
|
|
}
|
|
|
|
if (!_bstrButtonText)
|
|
hr = E_FAIL;
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Gets the icon closest to the desired size from an .ico file or from the
|
|
// resource in a .dll of .exe file
|
|
//
|
|
HICON CToolbarExt::_ExtractIcon
|
|
(
|
|
LPWSTR pszPath, // file to get icon from
|
|
int resid, // resource id (0 if unused)
|
|
int cx, // desired icon width
|
|
int cy // desired icon height
|
|
)
|
|
{
|
|
HICON hIcon = NULL;
|
|
|
|
WCHAR szPath[MAX_PATH];
|
|
SHExpandEnvironmentStrings(pszPath, szPath, ARRAYSIZE(szPath));
|
|
|
|
// If no resource id, assume it's an ico file
|
|
if (resid == 0)
|
|
{
|
|
hIcon = (HICON)LoadImage(0, szPath, IMAGE_ICON, cx, cy, LR_LOADFROMFILE);
|
|
}
|
|
|
|
// Otherwise, see if it's a resouce
|
|
if (hIcon == NULL)
|
|
{
|
|
HINSTANCE hInst = LoadLibraryEx(szPath, NULL, LOAD_LIBRARY_AS_DATAFILE);
|
|
if (hInst)
|
|
{
|
|
hIcon = (HICON)LoadImage(hInst, MAKEINTRESOURCE(resid), IMAGE_ICON, cx, cy, LR_DEFAULTCOLOR);
|
|
FreeLibrary(hInst);
|
|
}
|
|
}
|
|
|
|
return hIcon;
|
|
}
|
|
|
|
//
|
|
// Returns the desired icon in pvarProperty
|
|
//
|
|
HRESULT CToolbarExt::_GetIcon
|
|
(
|
|
LPCWSTR pszIcon, // Name of icon value in registry
|
|
int nWidth, // icon width
|
|
int nHeight, // icon height
|
|
HICON& rhIcon, // location to cached icon
|
|
VARIANTARG * pvarProperty // used for return icon
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
if (pvarProperty)
|
|
{
|
|
if (rhIcon == NULL)
|
|
{
|
|
BSTR bstrIconName;
|
|
if (_RegReadString(_hkeyThisExtension, pszIcon, &bstrIconName, TRUE))
|
|
{
|
|
// Parse entry such as "file.ext,1" to get the icon index
|
|
int nIconIndex = PathParseIconLocation(bstrIconName);
|
|
|
|
// If the entry was ",#" then it's an index into our built-in button bitmap
|
|
if (*bstrIconName == L'\0')
|
|
{
|
|
pvarProperty->vt = VT_I4;
|
|
pvarProperty->lVal = nIconIndex;
|
|
SysFreeString(bstrIconName);
|
|
return hr;
|
|
}
|
|
else
|
|
{
|
|
rhIcon = _ExtractIcon(bstrIconName, nIconIndex, nWidth, nHeight);
|
|
}
|
|
SysFreeString(bstrIconName);
|
|
}
|
|
}
|
|
|
|
if (rhIcon)
|
|
{
|
|
pvarProperty->vt = VT_BYREF;
|
|
pvarProperty->byref = rhIcon;
|
|
}
|
|
else
|
|
{
|
|
VariantInit(pvarProperty);
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Implementation of IBrowserExtension::GetProperty(). There are two important points here:
|
|
// (1) We are lazy loading the appropriate icons. This way if the user never goes into small icon
|
|
// mode we never create the images...
|
|
// (2) If we are called with a NULL pvarProperty then we must still return S_OK if the iPropID
|
|
// is for a property that we support and E_NOTIMPL if we do not. This is why the if (pvarProperty)
|
|
// check is done for each case rather tan outside the case block. This behavior is important
|
|
// for CBrowserExtension::Update() who passes in a NULL pvarProperty but still is trying to determine
|
|
// what kind of extension this is!
|
|
//
|
|
STDMETHODIMP CToolbarExt::GetProperty(SHORT iPropID, VARIANTARG * pvarProperty)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (pvarProperty)
|
|
VariantInit(pvarProperty); // in case of failure
|
|
|
|
switch (iPropID)
|
|
{
|
|
case TBEX_BUTTONTEXT:
|
|
if (pvarProperty)
|
|
{
|
|
pvarProperty->bstrVal = SysAllocString(_bstrButtonText);
|
|
if (pvarProperty->bstrVal)
|
|
{
|
|
pvarProperty->vt = VT_BSTR;
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case TBEX_GRAYICON:
|
|
|
|
// For Whistler, we now use a 24 x 24 icons
|
|
if (SHUseClassicToolbarGlyphs())
|
|
{
|
|
hr = _GetIcon(TEXT("Icon"), 20, 20, _hIcon, pvarProperty);
|
|
}
|
|
else
|
|
{
|
|
hr = _GetIcon(TEXT("Icon"), 24, 24, _hIcon, pvarProperty);
|
|
}
|
|
break;
|
|
|
|
case TBEX_GRAYICONSM:
|
|
hr = _GetIcon(TEXT("Icon"), 16, 16, _hIconSm, pvarProperty);
|
|
break;
|
|
|
|
case TBEX_HOTICON:
|
|
// For Whistler, we now use a 24 x 24 icons
|
|
if (SHUseClassicToolbarGlyphs())
|
|
{
|
|
hr = _GetIcon(TEXT("HotIcon"), 20, 20, _hHotIcon, pvarProperty);
|
|
}
|
|
else
|
|
{
|
|
hr = _GetIcon(TEXT("HotIcon"), 24, 24, _hHotIcon, pvarProperty);
|
|
}
|
|
break;
|
|
|
|
case TBEX_HOTICONSM:
|
|
hr = _GetIcon(TEXT("HotIcon"), 16, 16, _hHotIconSm, pvarProperty);
|
|
break;
|
|
|
|
case TBEX_DEFAULTVISIBLE:
|
|
if (pvarProperty)
|
|
{
|
|
BOOL fVisible = _RegGetBoolValue(L"Default Visible", FALSE);
|
|
pvarProperty->vt = VT_BOOL;
|
|
pvarProperty->boolVal = fVisible ? VARIANT_TRUE : VARIANT_FALSE;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
hr = E_NOTIMPL;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// IOleCommandTarget Implementation
|
|
//
|
|
STDMETHODIMP CToolbarExt::QueryStatus(const GUID* pguidCmdGroup, ULONG cCmds, OLECMD rgCmds[], OLECMDTEXT* pCmdText)
|
|
{
|
|
HRESULT hr = OLECMDERR_E_UNKNOWNGROUP;
|
|
if (pguidCmdGroup && IsEqualGUID(*pguidCmdGroup, CLSID_ToolbarExtButtons))
|
|
{
|
|
// Default to all commands enabled
|
|
for (ULONG i = 0; i < cCmds; i++)
|
|
{
|
|
// if (prgCmds[i].cmdID == 1)
|
|
// Execing this object is supported and can be done at this point
|
|
rgCmds[i].cmdf = OLECMDF_ENABLED | OLECMDF_SUPPORTED;
|
|
// else
|
|
// prgCmds[i].cmdf = 0;
|
|
}
|
|
hr = S_OK;
|
|
}
|
|
|
|
// Return an empty pCmdText
|
|
if (pCmdText != NULL)
|
|
{
|
|
pCmdText->cwActual = 0;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// IObjectWithSite Implementation
|
|
//
|
|
STDMETHODIMP CToolbarExt::SetSite(IUnknown* pUnkSite)
|
|
{
|
|
if (_pisb != NULL)
|
|
{
|
|
_pisb->Release();
|
|
_pisb = NULL;
|
|
}
|
|
|
|
if (pUnkSite)
|
|
pUnkSite->QueryInterface(IID_IShellBrowser, (void **)&_pisb);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CToolbarExt::GetSite(REFIID riid, void ** ppvSite)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
BOOL CToolbarExt::_RegGetBoolValue
|
|
(
|
|
LPCWSTR pszPropName,
|
|
BOOL fDefault
|
|
)
|
|
{
|
|
WCHAR szData[MAX_PATH];
|
|
DWORD cbData = SIZEOF(szData);
|
|
|
|
if ((_hkeyCurrentLang && RegQueryValueEx(_hkeyCurrentLang, pszPropName, NULL, NULL, (unsigned char *)szData, &cbData) == ERROR_SUCCESS) ||
|
|
(_hkeyThisExtension && RegQueryValueEx(_hkeyThisExtension, pszPropName, NULL, NULL, (unsigned char *)szData, &cbData) == ERROR_SUCCESS))
|
|
{
|
|
if ((0 == StrCmpI(L"TRUE", szData)) ||
|
|
(0 == StrCmpI(L"YES", szData)))
|
|
{
|
|
fDefault = TRUE; // We read TRUE from the registry.
|
|
}
|
|
else if ((0 == StrCmpI(L"FALSE", szData)) ||
|
|
(0 == StrCmpI(L"NO", szData)))
|
|
{
|
|
fDefault = FALSE; // We read TRUE from the registry.
|
|
}
|
|
}
|
|
|
|
return fDefault;
|
|
}
|
|
|
|
|
|
|
|
// Private Helper Functions
|
|
//
|
|
// shlwapi has some similar function; however, they all insist on reopening and closing the key in question
|
|
// with each read. It is explicitly suggested that we use our own helper if we are caching the key...
|
|
BOOL CToolbarExt::_RegReadString
|
|
(
|
|
HKEY hkeyThisExtension,
|
|
LPCWSTR pszPropName,
|
|
BSTR * pbstrProp,
|
|
BOOL fExpand // = FALSE, Expand Environment strings
|
|
)
|
|
{
|
|
WCHAR szData[MAX_PATH];
|
|
*pbstrProp = NULL;
|
|
BOOL fSuccess = FALSE;
|
|
|
|
// First try the optional location for localized content
|
|
if (_hkeyCurrentLang)
|
|
{
|
|
if (SUCCEEDED(SHLoadRegUIString(_hkeyCurrentLang, pszPropName, szData, ARRAYSIZE(szData))))
|
|
{
|
|
fSuccess = TRUE;
|
|
}
|
|
}
|
|
|
|
// Next try default location
|
|
if (!fSuccess && _hkeyThisExtension)
|
|
{
|
|
if (SUCCEEDED(SHLoadRegUIString(hkeyThisExtension, pszPropName, szData, ARRAYSIZE(szData))))
|
|
{
|
|
fSuccess = TRUE;
|
|
}
|
|
}
|
|
|
|
if (fSuccess)
|
|
{
|
|
LPWSTR psz = szData;
|
|
WCHAR szExpand[MAX_PATH];
|
|
if (fExpand)
|
|
{
|
|
SHExpandEnvironmentStrings(szData, szExpand, ARRAYSIZE(szExpand));
|
|
psz = szExpand;
|
|
}
|
|
*pbstrProp = SysAllocString(psz);
|
|
}
|
|
return (NULL != *pbstrProp);
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// Class CToolbarExtBand
|
|
//
|
|
// This class adds to the base functionality of CToolbarExt
|
|
// by storing the CLSID for a registered band, and displaying that
|
|
// band upon execution of IOleCommandTarget::Exec
|
|
//
|
|
//
|
|
STDAPI CToolbarExtBand_CreateInstance(
|
|
IUnknown * punkOuter,
|
|
IUnknown ** ppunk,
|
|
LPCOBJECTINFO poi
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
*ppunk = NULL;
|
|
|
|
CToolbarExtBand * lpTEB = new CToolbarExtBand();
|
|
|
|
if (lpTEB == NULL)
|
|
hr = E_OUTOFMEMORY;
|
|
else
|
|
*ppunk = SAFECAST(lpTEB, IBrowserExtension *);
|
|
|
|
return hr;
|
|
}
|
|
|
|
// Constructor / Destructor
|
|
//
|
|
CToolbarExtBand::CToolbarExtBand()
|
|
{
|
|
ASSERT(_cRef == 1);
|
|
ASSERT(_bBandState == FALSE);
|
|
ASSERT(_bstrBandCLSID == NULL);
|
|
}
|
|
|
|
// Destructor
|
|
//
|
|
CToolbarExtBand::~CToolbarExtBand()
|
|
{
|
|
if (_bstrBandCLSID)
|
|
SysFreeString(_bstrBandCLSID);
|
|
}
|
|
|
|
// IBrowserExtension::Init() We pass the majroity of the work on to the base class, then we load
|
|
// the BandCLSID and cache it.
|
|
STDMETHODIMP CToolbarExtBand::Init(REFGUID rguid)
|
|
{
|
|
HRESULT hr = CToolbarExt::Init(rguid);
|
|
|
|
_RegReadString(_hkeyThisExtension, TEXT("BandCLSID"), &_bstrBandCLSID);
|
|
|
|
if (!(_bstrButtonText && _bstrBandCLSID))
|
|
hr = E_FAIL;
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CToolbarExtBand::QueryStatus
|
|
(
|
|
const GUID * pguidCmdGroup,
|
|
ULONG cCmds,
|
|
OLECMD prgCmds[],
|
|
OLECMDTEXT * pCmdText
|
|
)
|
|
{
|
|
HRESULT hr = OLECMDERR_E_UNKNOWNGROUP;
|
|
if (pguidCmdGroup && IsEqualGUID(*pguidCmdGroup, CLSID_ToolbarExtButtons))
|
|
{
|
|
VARIANT varClsid;
|
|
|
|
// Default to all commands enabled
|
|
for (ULONG i = 0; i < cCmds; i++)
|
|
{
|
|
varClsid.vt = VT_BSTR;
|
|
varClsid.bstrVal = _bstrBandCLSID;
|
|
prgCmds[i].cmdf = OLECMDF_ENABLED | OLECMDF_SUPPORTED;
|
|
|
|
hr = IUnknown_Exec(_pisb, &CGID_ShellDocView, SHDVID_ISBROWSERBARVISIBLE, 0, &varClsid, NULL);
|
|
if (S_OK == hr)
|
|
{
|
|
prgCmds[i].cmdf |= OLECMDF_LATCHED;
|
|
}
|
|
}
|
|
hr = S_OK;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
// Take the pIShellBrowser (obtained from IObjectWithSite::SetSite()) and disply the band
|
|
STDMETHODIMP CToolbarExtBand::Exec(
|
|
const GUID * pguidCmdGroup,
|
|
DWORD nCmdID,
|
|
DWORD nCmdexecopt,
|
|
VARIANT * pvaIn,
|
|
VARIANT * pvaOut
|
|
)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
if (_pisb)
|
|
{
|
|
VARIANT varClsid;
|
|
varClsid.vt = VT_BSTR;
|
|
varClsid.bstrVal = _bstrBandCLSID;
|
|
|
|
_bBandState = !_bBandState;
|
|
IUnknown_Exec(_pisb, &CGID_ShellDocView, SHDVID_SHOWBROWSERBAR, _bBandState, &varClsid, NULL);
|
|
|
|
hr = S_OK;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
// Class CToolbarExtExec
|
|
//
|
|
// Expands on the base class by adding support for tools menu plug-ins.
|
|
// An instance of this class can be a button OR a menu OR BOTH. It also
|
|
// keeps track of a BSTR which it ShellExecutes in its IOleCommandTarget::Exec()
|
|
//
|
|
STDAPI CToolbarExtExec_CreateInstance(
|
|
IUnknown * punkOuter,
|
|
IUnknown ** ppunk,
|
|
LPCOBJECTINFO poi
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
*ppunk = NULL;
|
|
|
|
CToolbarExtExec * lpTEE = new CToolbarExtExec();
|
|
|
|
if (lpTEE == NULL)
|
|
hr = E_OUTOFMEMORY;
|
|
else
|
|
*ppunk = SAFECAST(lpTEE, IBrowserExtension *);
|
|
|
|
return hr;
|
|
}
|
|
|
|
CToolbarExtExec::CToolbarExtExec()
|
|
{
|
|
ASSERT(_cRef == 1);
|
|
ASSERT(_bstrToolTip == NULL);
|
|
ASSERT(_bstrExec == NULL);
|
|
ASSERT(_bstrScript == NULL);
|
|
ASSERT(_bstrMenuText == NULL);
|
|
ASSERT(_bstrMenuCustomize == NULL);
|
|
ASSERT(_bstrMenuStatusBar == NULL);
|
|
ASSERT(_punkExt == NULL);
|
|
}
|
|
|
|
CToolbarExtExec::~CToolbarExtExec()
|
|
{
|
|
if (_bstrToolTip)
|
|
SysFreeString(_bstrToolTip);
|
|
|
|
if (_bstrExec)
|
|
SysFreeString(_bstrExec);
|
|
|
|
if (_bstrScript)
|
|
SysFreeString(_bstrScript);
|
|
|
|
if (_bstrMenuText)
|
|
SysFreeString(_bstrMenuText);
|
|
|
|
if (_bstrMenuCustomize)
|
|
SysFreeString(_bstrMenuCustomize);
|
|
|
|
if (_bstrMenuStatusBar)
|
|
SysFreeString(_bstrMenuStatusBar);
|
|
|
|
if (_punkExt)
|
|
_punkExt->Release();
|
|
}
|
|
|
|
// Pass on the work for the toolbar button intiaztion to the base class then determine the object
|
|
// type and initialize the menu information if necessary...
|
|
STDMETHODIMP CToolbarExtExec::Init(REFGUID rguid)
|
|
{
|
|
HRESULT hr = CToolbarExt::Init(rguid);
|
|
|
|
// If the baseclass initialization went OK, then we have a working button
|
|
if (hr == S_OK)
|
|
_bButton = TRUE;
|
|
|
|
// Get app and/or script to execute (optional)
|
|
_RegReadString(_hkeyThisExtension, TEXT("Exec"), &_bstrExec, TRUE);
|
|
_RegReadString(_hkeyThisExtension, TEXT("Script"), &_bstrScript, TRUE);
|
|
|
|
|
|
// See if we have a menu item
|
|
if (_RegReadString(_hkeyThisExtension, TEXT("MenuText"), &_bstrMenuText))
|
|
{
|
|
_RegReadString(_hkeyThisExtension, TEXT("MenuCustomize"), &_bstrMenuCustomize);
|
|
_RegReadString(_hkeyThisExtension, TEXT("MenuStatusBar"), &_bstrMenuStatusBar);
|
|
_bMenuItem = TRUE;
|
|
}
|
|
|
|
if (_bMenuItem || _bButton)
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// It we're a button try passing the work on to the base class, if that doesn't cut it we'll
|
|
// check the menu stuff...
|
|
STDMETHODIMP CToolbarExtExec::GetProperty(SHORT iPropID, VARIANTARG * pvarProperty)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BOOL fImple = FALSE;
|
|
|
|
if (_bButton)
|
|
{
|
|
// If The generic button's getproperty returns S_OK then our job here is done
|
|
if (CToolbarExt::GetProperty(iPropID, pvarProperty) == S_OK)
|
|
fImple = TRUE;
|
|
}
|
|
|
|
if (_bMenuItem && !fImple)
|
|
{
|
|
fImple = TRUE;
|
|
|
|
if (pvarProperty)
|
|
VariantInit(pvarProperty);
|
|
|
|
switch (iPropID)
|
|
{
|
|
case TMEX_CUSTOM_MENU:
|
|
{
|
|
if (pvarProperty)
|
|
{
|
|
pvarProperty->bstrVal = SysAllocString(_bstrMenuCustomize);
|
|
if (pvarProperty->bstrVal)
|
|
{
|
|
pvarProperty->vt = VT_BSTR;
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case TMEX_MENUTEXT:
|
|
if (pvarProperty)
|
|
{
|
|
pvarProperty->bstrVal = SysAllocString(_bstrMenuText);
|
|
if (pvarProperty->bstrVal)
|
|
{
|
|
pvarProperty->vt = VT_BSTR;
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case TMEX_STATUSBARTEXT:
|
|
if (pvarProperty)
|
|
{
|
|
pvarProperty->bstrVal = SysAllocString(_bstrMenuStatusBar);
|
|
if (pvarProperty->bstrVal)
|
|
{
|
|
pvarProperty->vt = VT_BSTR;
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
fImple = FALSE;
|
|
}
|
|
}
|
|
|
|
if (!fImple)
|
|
hr = E_NOTIMPL;
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CToolbarExtExec::SetSite(IUnknown* punkSite)
|
|
{
|
|
// Give the external object our site
|
|
IUnknown_SetSite(_punkExt, punkSite);
|
|
|
|
// Call base class
|
|
return CToolbarExt::SetSite(punkSite);
|
|
}
|
|
|
|
STDMETHODIMP CToolbarExtExec::QueryStatus(const GUID * pguidCmdGroup, ULONG cCmds, OLECMD rgCmds[], OLECMDTEXT * pCmdText)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
// Pass query to external object if it exists
|
|
IOleCommandTarget* pCmd;
|
|
if (_punkExt && SUCCEEDED(_punkExt->QueryInterface(IID_IOleCommandTarget, (LPVOID*)&pCmd)))
|
|
{
|
|
hr = pCmd->QueryStatus(pguidCmdGroup, cCmds, rgCmds, pCmdText);
|
|
pCmd->Release();
|
|
}
|
|
else
|
|
{
|
|
// Let base class handle this
|
|
hr = CToolbarExt::QueryStatus(pguidCmdGroup, cCmds, rgCmds, pCmdText);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// Shell execute the _bstrExec
|
|
STDMETHODIMP CToolbarExtExec::Exec(
|
|
const GUID * pguidCmdGroup,
|
|
DWORD nCmdId,
|
|
DWORD nCmdexecopt,
|
|
VARIANT * pvaIn,
|
|
VARIANT * pvaOut
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
//
|
|
// The first time this is called, we lazy instantiate an external object if
|
|
// one is registered.. This object can JIT in components and provide a
|
|
// command target.
|
|
//
|
|
if (!_bExecCalled)
|
|
{
|
|
// We only do this once
|
|
_bExecCalled = TRUE;
|
|
|
|
BSTR bstrExtCLSID;
|
|
if (_RegReadString(_hkeyThisExtension, TEXT("clsidExtension"), &bstrExtCLSID))
|
|
{
|
|
// We have an extension clsid, so create the object. This gives the object an oportunity
|
|
// to jit in code when its button or menu is invoked.
|
|
CLSID clsidExt;
|
|
|
|
if (CLSIDFromString(bstrExtCLSID, &clsidExt) == S_OK)
|
|
{
|
|
if (SUCCEEDED(CoCreateInstance(clsidExt, NULL, CLSCTX_INPROC_SERVER,
|
|
IID_IUnknown, (void **)&_punkExt)))
|
|
{
|
|
// Give the object our site (optional)
|
|
IUnknown_SetSite(_punkExt, _pisb);
|
|
}
|
|
}
|
|
SysFreeString(bstrExtCLSID);
|
|
}
|
|
}
|
|
|
|
// Pass command to external object if it exists
|
|
IOleCommandTarget* pCmd;
|
|
if (_punkExt && SUCCEEDED(_punkExt->QueryInterface(IID_IOleCommandTarget, (LPVOID*)&pCmd)))
|
|
{
|
|
hr = pCmd->Exec(pguidCmdGroup, nCmdId, nCmdexecopt, pvaIn, pvaOut);
|
|
pCmd->Release();
|
|
}
|
|
|
|
// Run a script if one was specified
|
|
if(_bstrScript && _pisb)
|
|
{
|
|
IOleCommandTarget *poct = NULL;
|
|
VARIANT varArg;
|
|
varArg.vt = VT_BSTR;
|
|
varArg.bstrVal = _bstrScript;
|
|
hr = _pisb->QueryInterface(IID_IOleCommandTarget, (LPVOID *)&poct);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Tell MSHTML to execute the script
|
|
hr = poct->Exec(&CGID_MSHTML, IDM_RUNURLSCRIPT, 0, &varArg, NULL);
|
|
poct->Release();
|
|
}
|
|
}
|
|
|
|
// Launch executable if one was specified
|
|
if (_bstrExec)
|
|
{
|
|
SHELLEXECUTEINFO sei = { 0 };
|
|
|
|
sei.cbSize = sizeof(sei);
|
|
sei.lpFile = _bstrExec;
|
|
sei.nShow = SW_SHOWNORMAL;
|
|
|
|
// We are using ShellExecuteEx over ShellExecute because the Unicode version of ShellExecute
|
|
// is bogus on 95/98
|
|
if (ShellExecuteExW(&sei) == FALSE)
|
|
hr = E_FAIL;
|
|
}
|
|
|
|
return hr;
|
|
}
|