|
|
#include "shellprv.h"
#include "defcm.h"
#include "datautil.h"
/////////////////////////////////////////////////////////////////////////////
// CClientExtractIcon
class CClientExtractIconCB;
class ATL_NO_VTABLE CClientExtractIcon : public IShellExtInit , public IExtractIconW , public IExtractIconA , public IContextMenu , public IPersistPropertyBag , public IServiceProvider , public CComObjectRootEx<CComSingleThreadModel> , public CComCoClass<CClientExtractIcon, &CLSID_ClientExtractIcon> { public:
BEGIN_COM_MAP(CClientExtractIcon) COM_INTERFACE_ENTRY(IShellExtInit) // Need to use COM_INTERFACE_ENTRY_IID for the interfaces
// that don't have an idl
COM_INTERFACE_ENTRY_IID(IID_IExtractIconA, IExtractIconA) COM_INTERFACE_ENTRY_IID(IID_IExtractIconW, IExtractIconW) COM_INTERFACE_ENTRY_IID(IID_IContextMenu, IContextMenu) COM_INTERFACE_ENTRY(IPersist) COM_INTERFACE_ENTRY(IPersistPropertyBag) COM_INTERFACE_ENTRY(IServiceProvider) END_COM_MAP()
DECLARE_NO_REGISTRY() DECLARE_NOT_AGGREGATABLE(CClientExtractIcon)
public: // *** IShellExtInit ***
STDMETHODIMP Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdto, HKEY hkProgID);
// *** IExtractIconA ***
STDMETHODIMP GetIconLocation(UINT uFlags, LPSTR szIconFile, UINT cchMax, int *piIndex, UINT *pwFlags); STDMETHODIMP Extract(LPCSTR pszFile, UINT nIconIndex, HICON *phiconLarge, HICON *phiconSmall, UINT nIconSize);
// *** IExtractIconW ***
STDMETHODIMP GetIconLocation(UINT uFlags, LPWSTR szIconFile, UINT cchMax, int *piIndex, UINT *pwFlags); STDMETHODIMP Extract(LPCWSTR pszFile, UINT nIconIndex, HICON *phiconLarge, HICON *phiconSmall, UINT nIconSize);
// *** IContextMenu methods ***
STDMETHOD(QueryContextMenu)(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags); STDMETHOD(InvokeCommand)(LPCMINVOKECOMMANDINFO pici); STDMETHOD(GetCommandString)(UINT_PTR idCmd, UINT uType, UINT * pwReserved, LPSTR pszName, UINT cchMax);
// *** IPersist methods ***
STDMETHOD(GetClassID)(CLSID *pclsid) { *pclsid = CLSID_ClientExtractIcon; return S_OK; }
// *** IPersistPropertyBag methods ***
STDMETHOD(InitNew)(); STDMETHOD(Load)(IPropertyBag *pbg, IErrorLog *plog); STDMETHOD(Save)(IPropertyBag *pbg, BOOL fClearDirty, BOOL FSaveAllProperties) { return E_NOTIMPL; }
// *** IServiceProvider methods ***
STDMETHODIMP QueryService(REFGUID guidService, REFIID riid, void **ppvObj);
public: CClientExtractIcon() : _idmProperties(-1) { } ~CClientExtractIcon();
// *** IContextMenuCB forwarded back to us ***
HRESULT CallBack(IShellFolder *psf, HWND hwnd, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam);
public: IContextMenu * _pcmInner; IAssociationElement * _pae; IPropertyBag * _pbag; LPITEMIDLIST _pidlObject; CComObject<CClientExtractIconCB> * _pcb; HKEY _hkClass; UINT _idmProperties; UINT _idCmdFirst; };
/////////////////////////////////////////////////////////////////////////////
// CClientExtractIconCB - DefCM callback
//
class ATL_NO_VTABLE CClientExtractIconCB : public IContextMenuCB , public CComObjectRootEx<CComSingleThreadModel> , public CComCoClass<CClientExtractIconCB> { public:
BEGIN_COM_MAP(CClientExtractIconCB) // Need to use COM_INTERFACE_ENTRY_IID for the interfaces
// that don't have an idl
COM_INTERFACE_ENTRY_IID(IID_IContextMenuCB, IContextMenuCB) END_COM_MAP()
DECLARE_NO_REGISTRY() DECLARE_NOT_AGGREGATABLE(CClientExtractIconCB)
void Attach(CClientExtractIcon *pcxi) { _pcxi = pcxi; }
public: // *** IContextMenuCB ***
STDMETHODIMP CallBack(IShellFolder *psf, HWND hwnd, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam) { if (_pcxi) return _pcxi->CallBack(psf, hwnd, pdtobj, uMsg, wParam, lParam);
return E_FAIL; }
public: CClientExtractIcon *_pcxi; };
/////////////////////////////////////////////////////////////////////////////
STDAPI CClientExtractIcon_CreateInstance(IUnknown* punkOuter, REFIID riid, void** ppunk) { return CClientExtractIcon::_CreatorClass::CreateInstance(punkOuter, riid, ppunk); }
CClientExtractIcon::~CClientExtractIcon() { InitNew(); }
// *** IPersistPropertyBag::InitNew ***
HRESULT CClientExtractIcon::InitNew() { ATOMICRELEASE(_pcmInner); ATOMICRELEASE(_pae); ATOMICRELEASE(_pbag); ILFree(_pidlObject); if (_hkClass) RegCloseKey(_hkClass); if (_pcb) { _pcb->Attach(NULL); // break circular reference
_pcb->Release(); _pcb = NULL; }
return S_OK; }
//
// Property bag items:
//
// Element = CLSID_Assoc* element to create
// InitString = string to IPersistString2::SetString with
// opentext = display name for "open" command (if not overridden)
//
// If the client did not customize a "properties" command, then we
// also use these values:
//
// properties = program to execute for "properties"
// propertiestext = name for the "properties" command
//
// *** IPersistPropertyBag::Load ***
HRESULT CClientExtractIcon::Load(IPropertyBag *pbag, IErrorLog *plog) { HRESULT hr;
// Erase any old state
InitNew();
// Save the property bag so we can mess with him later
_pbag = pbag; if (_pbag) { _pbag->AddRef(); }
// Get the CLSID we are dispatching through and initialize it
// with the InitString.
CLSID clsid; hr = SHPropertyBag_ReadGUID(pbag, L"Element", &clsid); if (SUCCEEDED(hr)) { BSTR bs; hr = SHPropertyBag_ReadBSTR(pbag, L"InitString", &bs); if (SUCCEEDED(hr)) { hr = THR(AssocElemCreateForClass(&clsid, bs, &_pae)); ::SysFreeString(bs);
// Ignore failure of AssocElemCreateForClass
// It can fail if the user's default client got uninstalled
hr = S_OK; } }
return hr; }
// *** IServiceProvider::QueryService ***
//
// We cheat and use ISericeProvider::QueryService as a sort of
// "QueryInterface that's allowed to break COM identity rules".
//
HRESULT CClientExtractIcon::QueryService(REFGUID guidService, REFIID riid, void **ppvObj) { if (guidService == IID_IAssociationElement && _pae) { return _pae->QueryInterface(riid, ppvObj); } *ppvObj = NULL; return E_FAIL; }
// *** IShellExtInit::Initialize
//
// Only if the HKEY specifies a ClientType.
//
HRESULT CClientExtractIcon::Initialize(LPCITEMIDLIST, IDataObject *pdto, HKEY hkClass) { ILFree(_pidlObject);
HRESULT hr = PidlFromDataObject(pdto, &_pidlObject);
if (_hkClass) { RegCloseKey(_hkClass); } if (hkClass) { _hkClass = SHRegDuplicateHKey(hkClass); } return hr; }
// *** IExtractIconA::GetIconLocation
HRESULT CClientExtractIcon::GetIconLocation( UINT uFlags, LPSTR szIconFile, UINT cchMax, int *piIndex, UINT *pwFlags) { WCHAR wszPath[MAX_PATH]; HRESULT hr = GetIconLocation(uFlags, wszPath, ARRAYSIZE(wszPath), piIndex, pwFlags); if (SUCCEEDED(hr)) { SHUnicodeToAnsi(wszPath, szIconFile, cchMax); } return hr; }
// *** IExtractIconA::Extract
HRESULT CClientExtractIcon::Extract( LPCSTR pszFile, UINT nIconIndex, HICON *phiconLarge, HICON *phiconSmall, UINT nIconSize) { return S_FALSE; }
// *** IExtractIconW::GetIconLocation
HRESULT CClientExtractIcon::GetIconLocation( UINT uFlags, LPWSTR szIconFile, UINT cchMax, int *piIndex, UINT *pwFlags) { szIconFile[0] = L'\0';
if (_pae) { LPWSTR pszIcon; HRESULT hr = _pae->QueryString(AQS_DEFAULTICON, NULL, &pszIcon); if (SUCCEEDED(hr)) { lstrcpynW(szIconFile, pszIcon, cchMax); SHFree(pszIcon); } }
if (!szIconFile[0] && _hkClass) { LONG cb = cchMax * sizeof(TCHAR); RegQueryValueW(_hkClass, L"DefaultIcon", szIconFile, &cb); }
if (szIconFile[0]) { *pwFlags = GIL_PERCLASS; *piIndex = PathParseIconLocationW(szIconFile); return S_OK; } else { return E_FAIL; } }
// *** IExtractIconW::Extract
HRESULT CClientExtractIcon::Extract( LPCWSTR pszFile, UINT nIconIndex, HICON *phiconLarge, HICON *phiconSmall, UINT nIconSize) { return S_FALSE; }
// *** IContextMenuCB forwarded back to us ***
HRESULT CClientExtractIcon::CallBack(IShellFolder *psf, HWND hwnd, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam) { WCHAR wszBuf[MAX_PATH];
switch (uMsg) { case DFM_MERGECONTEXTMENU: { QCMINFO *pqcm = (QCMINFO *)lParam; // Add a "Properties" command in case we end up needing one.
// (And if not, we'll remove it afterwards.)
if (SUCCEEDED(SHPropertyBag_ReadStr(_pbag, L"propertiestext", wszBuf, ARRAYSIZE(wszBuf)))) { SHLoadIndirectString(wszBuf, wszBuf, ARRAYSIZE(wszBuf), NULL); InsertMenuW(pqcm->hmenu, pqcm->indexMenu, MF_BYPOSITION | MF_STRING, pqcm->idCmdFirst, wszBuf); _idmProperties = pqcm->idCmdFirst++; } return S_OK; // Please merge the HKEY\shell stuff
}
case DFM_MERGECONTEXTMENU_TOP: // The app has added its menu items; see what needs to be cleaned up
{ QCMINFO *pqcm = (QCMINFO *)lParam;
// See if they added a "Properties" item.
// If so, then delete ours.
if (GetMenuIndexForCanonicalVerb(pqcm->hmenu, _pcmInner, _idCmdFirst, L"properties") != 0xFFFFFFFF) { // Yes they do, so delete our bogus one
DeleteMenu(pqcm->hmenu, _idmProperties, MF_BYCOMMAND); _idmProperties = -1; }
// Now see if their "open" command uses the default name.
// If so, then we replace it with a friendlier name.
BOOL fCustomOpenString = FALSE; IAssociationElement *paeOpen; if (_pae && SUCCEEDED(_pae->QueryObject(AQVO_SHELLVERB_DELEGATE, L"open", IID_PPV_ARG(IAssociationElement, &paeOpen)))) { if (SUCCEEDED(paeOpen->QueryExists(AQN_NAMED_VALUE, L"MUIVerb")) || SUCCEEDED(paeOpen->QueryExists(AQN_NAMED_VALUE, NULL))) { fCustomOpenString = TRUE; } paeOpen->Release(); }
if (!fCustomOpenString) { UINT idm = GetMenuIndexForCanonicalVerb(pqcm->hmenu, _pcmInner, _idCmdFirst, L"open"); if (idm != 0xFFFFFFFF) { if (SUCCEEDED(SHPropertyBag_ReadStr(_pbag, L"opentext", wszBuf, ARRAYSIZE(wszBuf))) && wszBuf[0]) { SHLoadIndirectString(wszBuf, wszBuf, ARRAYSIZE(wszBuf), NULL); MENUITEMINFO mii; mii.cbSize = sizeof(mii); mii.fMask = MIIM_STRING; mii.dwTypeData = wszBuf; SetMenuItemInfo(pqcm->hmenu, idm, TRUE, &mii); } } } } return S_OK;
case DFM_INVOKECOMMANDEX: switch (wParam) { case 0: // Properties
if (SUCCEEDED(SHPropertyBag_ReadStr(_pbag, L"properties", wszBuf, ARRAYSIZE(wszBuf)))) { DFMICS *pdfmics = (DFMICS *)lParam; if (ShellExecCmdLine(pdfmics->pici->hwnd, wszBuf, NULL, SW_SHOWNORMAL, NULL, 0)) { return S_OK; } else { return E_FAIL; } } break;
default: ASSERT(!"Unexpected DFM_INVOKECOMMAND"); break; } return E_FAIL;
default: return E_NOTIMPL; } }
// *** IContextMenu::QueryContextMenu ***
HRESULT CClientExtractIcon::QueryContextMenu( HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) { HRESULT hr; if (!_pcmInner) { HKEY hk = NULL; if (_pae) { // If this fails, we continue with a NULL key
AssocKeyFromElement(_pae, &hk); }
if (!_pcb) { hr = CComObject<CClientExtractIconCB>::CreateInstance(&_pcb); if (SUCCEEDED(hr)) { _pcb->Attach(this); _pcb->AddRef(); } } else { hr = S_OK; }
if (SUCCEEDED(hr)) { IShellFolder *psf; hr = SHGetDesktopFolder(&psf); if (SUCCEEDED(hr)) { DEFCONTEXTMENU dcm = { NULL, // hwnd
_pcb, // pcmcb
NULL, // pidlFolder
psf, // IShellFolder
1, // cidl
(LPCITEMIDLIST*)&_pidlObject, // apidl
NULL, // paa
1, // cKeys
&hk, // aKeys
}; hr = CreateDefaultContextMenu(&dcm, &_pcmInner); psf->Release(); } }
if (hk) { RegCloseKey(hk); }
if (!_pcmInner) { return E_FAIL; } }
if (_pcmInner) { _idCmdFirst = idCmdFirst; uFlags |= CMF_VERBSONLY; // Don't do cut/copy/paste/link
hr = _pcmInner->QueryContextMenu(hmenu, indexMenu, idCmdFirst, idCmdLast, uFlags); } else { hr = E_FAIL; }
return hr; }
// *** IContextMenu::InvokeCommand ***
HRESULT CClientExtractIcon::InvokeCommand(LPCMINVOKECOMMANDINFO pici) { HRESULT hr = E_FAIL; if (_pcmInner) { hr = _pcmInner->InvokeCommand(pici); }
return hr; }
// *** IContextMenu::GetCommandString ***
HRESULT CClientExtractIcon::GetCommandString( UINT_PTR idCmd, UINT uType, UINT * pwReserved, LPSTR pszName, UINT cchMax) { if (!_pcmInner) return E_FAIL;
return _pcmInner->GetCommandString(idCmd, uType, pwReserved, pszName, cchMax); }
|