#include "shellprv.h" #include "clsobj.h" #include "dpa.h" #include "ids.h" #include "ole2dup.h" ///////////////////////////////////////////////////////////////////////////// // CAutomationCM class CAutomationCM : public IContextMenu, public IShellExtInit, public IPersistPropertyBag { public: // *** IUnknown methods *** STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObj); STDMETHOD_(ULONG,AddRef)(void); STDMETHOD_(ULONG,Release)(void); // *** IContextMenu methods *** STDMETHOD(QueryContextMenu)(THIS_ HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags); STDMETHOD(InvokeCommand)(THIS_ LPCMINVOKECOMMANDINFO pici); STDMETHOD(GetCommandString)(THIS_ UINT_PTR idCmd, UINT uType, UINT * pwReserved, LPSTR pszName, UINT cchMax); // *** IShellExtInit methods *** STDMETHOD(Initialize)(THIS_ LPCITEMIDLIST pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID) { return S_OK; } // *** IPersist methods *** STDMETHOD(GetClassID)(THIS_ CLSID *pclsid); // *** IPersistPropertyBag methods *** STDMETHOD(InitNew)(THIS); STDMETHOD(Load)(THIS_ IPropertyBag *pbg, IErrorLog *plog); STDMETHOD(Save)(THIS_ IPropertyBag *pbg, BOOL fClearDirty, BOOL FSaveAllProperties) { return E_NOTIMPL; } public: CAutomationCM() : _cRef(1) { } private: ~CAutomationCM(); static BOOL _DestroyVARIANTARG(VARIANTARG *pvarg, LPVOID) { ::VariantClear(pvarg); return TRUE; } enum { // Any method with more than MAXPARAMS parameters should be taken // outside and shot. // Note: If you change MAXPARAMS, make sure that szParamN[] is // big enough in IPersistPropertyBag::Load. MAXPARAMS = 1000, }; LONG _cRef; IDispatch * _pdisp; BSTR _bsProperties; DISPID _dispid; BOOL _fInitialized; DISPPARAMS _dp; CDSA _dsaVarg; TCHAR _szCommandName[MAX_PATH]; TCHAR _szMenuItem[MAX_PATH]; }; STDAPI CAutomationCM_CreateInstance(IUnknown * punkOuter, REFIID riid, void ** ppvOut) { // clsobj.c should've filtered out the aggregated scenario already ASSERT(punkOuter == NULL); *ppvOut = NULL; CAutomationCM *pauto = new CAutomationCM; if (!pauto) return E_OUTOFMEMORY; HRESULT hr = pauto->QueryInterface(riid, ppvOut); pauto->Release(); return hr; } CAutomationCM::~CAutomationCM() { InitNew(); ASSERT(!_dsaVarg); } // *** IUnknown::QueryInterface *** HRESULT CAutomationCM::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CAutomationCM, IContextMenu), // IID_IContextMenu QITABENT(CAutomationCM, IShellExtInit), // IID_IShellExtInit QITABENT(CAutomationCM, IPersist), // IID_IPersist (base for IPersistPropertyBag) QITABENT(CAutomationCM, IPersistPropertyBag), // IID_IPersistPropertyBag { 0 }, }; return QISearch(this, qit, riid, ppv); } // *** IUnknown::AddRef *** STDMETHODIMP_(ULONG) CAutomationCM::AddRef() { return InterlockedIncrement(&_cRef); } // *** IUnknown::Release *** STDMETHODIMP_(ULONG) CAutomationCM::Release() { ASSERT( 0 != _cRef ); ULONG cRef = InterlockedDecrement(&_cRef); if ( 0 == cRef ) { delete this; } return cRef; } // *** IPersist::GetClassID *** HRESULT CAutomationCM::GetClassID(CLSID *pclsid) { *pclsid = CLSID_AutomationCM; return S_OK; } // *** IPersistPropertyBag::InitNew *** HRESULT CAutomationCM::InitNew() { ATOMICRELEASE(_pdisp); ::SysFreeString(_bsProperties); _bsProperties = NULL; // Free the DISPPARAMs if (_dsaVarg) { _dsaVarg.DestroyCallback(_DestroyVARIANTARG, 0); } _dp.cArgs = 0; _fInitialized = FALSE; return S_OK; } // // Property bag items: // // CLSID = object to CoCreate(IID_IDispatch) // command = display name of command // method = name of method (GetIDsOfNames) // param1 .. paramN = parameters (up to MAXPARAMS) // // Parameters are passed as BSTRs (or whatever type SHPropertyBagOnRegKey // returns.) // // It is the responsibility of the target IDispatch to coerce the types // as appropriate. // // *** IPersistPropertyBag::Load *** HRESULT CAutomationCM::Load(IPropertyBag *pbag, IErrorLog *plog) { HRESULT hr; // Erase any old state InitNew(); // Get the CLSID we are dispatching through CLSID clsid; hr = SHPropertyBag_ReadGUID(pbag, L"CLSID", &clsid); if (SUCCEEDED(hr)) { // Must use SHExCoCreateInstance to go through the approval/app compat layer hr = SHExtCoCreateInstance(NULL, &clsid, NULL, IID_PPV_ARG(IDispatch, &_pdisp)); } // Map the method to a DISPID if (SUCCEEDED(hr)) { BSTR bs; hr = SHPropertyBag_ReadBSTR(pbag, L"method", &bs); if (SUCCEEDED(hr)) { LPOLESTR pname = bs; hr = _pdisp->GetIDsOfNames(IID_NULL, &pname, 1, 0, &_dispid); ::SysFreeString(bs); } } // Read in the parameters if (SUCCEEDED(hr)) { if (_dsaVarg.Create(4)) { WCHAR szParamN[16]; // worst-case: "param1000" VARIANT var; while (_dsaVarg.GetItemCount() < MAXPARAMS) { wnsprintfW(szParamN, ARRAYSIZE(szParamN), L"param%d", _dsaVarg.GetItemCount()+1); VariantInit(&var); var.vt = VT_BSTR; if (FAILED(pbag->Read(szParamN, &var, NULL))) { // No more parameters break; } if (_dsaVarg.AppendItem((VARIANTARG*)&var) < 0) { ::VariantClear(&var); hr = E_OUTOFMEMORY; break; } } } else { // Could not create _dsaVarg hr = E_OUTOFMEMORY; } } // Get the command name if (SUCCEEDED(hr)) { hr = SHPropertyBag_ReadStr(pbag, L"command", _szCommandName, ARRAYSIZE(_szCommandName)); if (SUCCEEDED(hr)) { hr = SHLoadIndirectString(_szCommandName, _szCommandName, ARRAYSIZE(_szCommandName), NULL); } } // Get the properties string (optional) if (SUCCEEDED(hr)) { if (SUCCEEDED(SHPropertyBag_ReadBSTR(pbag, L"properties", &_bsProperties))) { ASSERT(_bsProperties); // Ignore failure here; we'll detect it later SHPropertyBag_ReadStr(pbag, L"propertiestext", _szMenuItem, ARRAYSIZE(_szMenuItem)); SHLoadIndirectString(_szMenuItem, _szMenuItem, ARRAYSIZE(_szMenuItem), NULL); } } _fInitialized = SUCCEEDED(hr); return hr; } // *** IContextMenu::QueryContextMenu *** HRESULT CAutomationCM::QueryContextMenu( HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) { if (!_fInitialized) return E_FAIL; HRESULT hr; // Must have room for two items (the command and possibly also Properties) if (idCmdFirst + 1 <= idCmdLast) { if (InsertMenuW(hmenu, indexMenu, MF_BYPOSITION | MF_STRING, idCmdFirst, _szCommandName)) { if (_szMenuItem[0]) { InsertMenuW(hmenu, indexMenu+1, MF_BYPOSITION | MF_STRING, idCmdFirst+1, _szMenuItem); } } hr = ResultFromShort(2); // number of items added } else { hr = E_FAIL; // unable to add items } return hr; } const LPCSTR c_rgAutoCMCommands[] = { "open", // command 0 "properties", // command 1 - optional }; // *** IContextMenu::InvokeCommand *** HRESULT CAutomationCM::InvokeCommand(LPCMINVOKECOMMANDINFO pici) { if (!_fInitialized) return E_FAIL; HRESULT hr; int iCmd; if (!IS_INTRESOURCE(pici->lpVerb)) { // If this loop fails to find a match, iCmd will be out of range // and will hit the "default:" in the switch statement below. for (iCmd = 0; iCmd < ARRAYSIZE(c_rgAutoCMCommands) - 1; iCmd++) { if (lstrcmpiA(pici->lpVerb, c_rgAutoCMCommands[iCmd]) == 0) { break; } } } else { iCmd = PtrToLong(pici->lpVerb); } switch (iCmd) { case 0: // open _dp.cArgs = _dsaVarg.GetItemCount(); _dp.rgvarg = _dp.cArgs ? _dsaVarg.GetItemPtr(0) : NULL; hr = _pdisp->Invoke(_dispid, IID_NULL, 0, DISPATCH_METHOD, &_dp, NULL, NULL, NULL); break; case 1: if (_bsProperties) { hr = ShellExecCmdLine(pici->hwnd, _bsProperties, NULL, SW_SHOWNORMAL, NULL, 0) ? S_OK : E_FAIL; } else { hr = E_INVALIDARG; } break; default: hr = E_INVALIDARG; break; } return hr; } // *** IContextMenu::GetCommandString *** HRESULT CAutomationCM::GetCommandString( UINT_PTR idCmd, UINT uType, UINT * pwReserved, LPSTR pszName, UINT cchMax) { if (!_fInitialized) return E_FAIL; switch (uType) { case GCS_VERBA: if (idCmd < ARRAYSIZE(c_rgAutoCMCommands)) { SHAnsiToAnsi(c_rgAutoCMCommands[idCmd], (LPSTR)pszName, cchMax); return S_OK; } break; case GCS_VERBW: if (idCmd < ARRAYSIZE(c_rgAutoCMCommands)) { SHAnsiToUnicode(c_rgAutoCMCommands[idCmd], (LPWSTR)pszName, cchMax); return S_OK; } break; } return E_NOTIMPL; }