#include "shellprv.h" #include "cowsite.h" #include "contextmenu.h" // Context Menu Forwarding base class, desinged to delegate // to a real IContextMenu, and provide inheriting class // an easy way to override minor bits of functionality // CContextMenuForwarder::CContextMenuForwarder(IUnknown* punk) : _cRef(1) { _punk = punk; _punk->AddRef(); _punk->QueryInterface(IID_PPV_ARG(IObjectWithSite, &_pows)); _punk->QueryInterface(IID_PPV_ARG(IContextMenu, &_pcm)); _punk->QueryInterface(IID_PPV_ARG(IContextMenu2, &_pcm2)); _punk->QueryInterface(IID_PPV_ARG(IContextMenu3, &_pcm3)); } CContextMenuForwarder::~CContextMenuForwarder() { if (_pows) _pows->Release(); if (_pcm) _pcm->Release(); if (_pcm2) _pcm2->Release(); if (_pcm3) _pcm3->Release(); _punk->Release(); } STDMETHODIMP CContextMenuForwarder::QueryInterface(REFIID riid, void **ppv) { HRESULT hr = _punk->QueryInterface(riid, ppv); if (SUCCEEDED(hr)) { IUnknown* punkTmp = (IUnknown*)(*ppv); static const QITAB qit[] = { QITABENT(CContextMenuForwarder, IObjectWithSite), // IID_IObjectWithSite QITABENT(CContextMenuForwarder, IContextMenu3), // IID_IContextMenu3 QITABENTMULTI(CContextMenuForwarder, IContextMenu2, IContextMenu3), // IID_IContextMenu2 QITABENTMULTI(CContextMenuForwarder, IContextMenu, IContextMenu3), // IID_IContextMenu { 0 }, }; HRESULT hrTmp = QISearch(this, qit, riid, ppv); if (SUCCEEDED(hrTmp)) { punkTmp->Release(); } else { RIPMSG(FALSE, "CContextMenuForwarder asked for an interface it doesn't support"); *ppv = NULL; hr = E_NOINTERFACE; } } return hr; } STDMETHODIMP_(ULONG) CContextMenuForwarder::AddRef() { return InterlockedIncrement(&_cRef); } STDMETHODIMP_(ULONG) CContextMenuForwarder::Release() { if (InterlockedDecrement(&_cRef)) return _cRef; delete this; return 0; } // A context menu implementation on an array of context menus // // use the Create_ContextMenuOnContextMenuArray construction function // #define MAX_CM_WRAP 5 class CContextMenuOnContextMenuArray : public IContextMenu3, public CObjectWithSite { public: // IUnknown STDMETHODIMP QueryInterface(REFIID, void **); STDMETHODIMP_(ULONG) AddRef(void); STDMETHODIMP_(ULONG) Release(void); // IContextMenu STDMETHODIMP QueryContextMenu(HMENU hmenu, UINT indexMenu,UINT idCmdFirst,UINT idCmdLast,UINT uFlags); STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO lpici); STDMETHODIMP GetCommandString(UINT_PTR idCmd, UINT uType, UINT *pwReserved, LPSTR pszName, UINT cchMax); // IContextMenu2 STDMETHODIMP HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam); // IContextMenu3 STDMETHODIMP HandleMenuMsg2(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plResult); // IObjectWithSite STDMETHODIMP SetSite(IUnknown *punkSite); // override BOOL IsEmpty() { return 0 == _count; } protected: CContextMenuOnContextMenuArray(IContextMenu* rgpcm[], UINT cpcm); ~CContextMenuOnContextMenuArray(); friend HRESULT Create_ContextMenuOnContextMenuArray(IContextMenu* rgpcm[], UINT cpcm, REFIID riid, void** ppv); private: LONG _cRef; UINT _count; UINT _idFirst; // The begining of the first range is _idFirst UINT _idOffsets[MAX_CM_WRAP]; // The END of each range (BEGINing of next range is +1) IContextMenu *_pcmItem[MAX_CM_WRAP]; // The contextmenu for the item IContextMenu2 *_pcm2Item[MAX_CM_WRAP]; // The contextmenu for the item IContextMenu3 *_pcm3Item[MAX_CM_WRAP]; // The contextmenu for the item }; CContextMenuOnContextMenuArray::CContextMenuOnContextMenuArray(IContextMenu* rgpcm[], UINT cpcm) : _cRef(1) { ASSERT(cpcm <= MAX_CM_WRAP); ASSERT(0 == _count); for (UINT i = 0 ; i < cpcm ; i++) { if (rgpcm[i]) { rgpcm[i]->QueryInterface(IID_PPV_ARG(IContextMenu, &_pcmItem[_count])); ASSERT(_pcmItem[_count]); rgpcm[i]->QueryInterface(IID_PPV_ARG(IContextMenu2, &_pcm2Item[_count])); rgpcm[i]->QueryInterface(IID_PPV_ARG(IContextMenu3, &_pcm3Item[_count])); _count++; } } } HRESULT Create_ContextMenuOnContextMenuArray(IContextMenu* rgpcm[], UINT cpcm, REFIID riid, void** ppv) { HRESULT hr; *ppv = NULL; if (cpcm < MAX_CM_WRAP) { CContextMenuOnContextMenuArray* p = new CContextMenuOnContextMenuArray(rgpcm, cpcm); if (p) { if (p->IsEmpty()) { hr = E_OUTOFMEMORY; // caller didn't check the array it gave us? } else { hr = p->QueryInterface(riid, ppv); } p->Release(); } else { hr = E_OUTOFMEMORY; } } else { RIPMSG(FALSE, "Create_ContextMenuOnContextMenuArray with too many items!"); hr = E_INVALIDARG; } return hr; } CContextMenuOnContextMenuArray::~CContextMenuOnContextMenuArray() { for (UINT i = 0 ; i < _count ; i++) { _pcmItem[i]->Release(); if (_pcm2Item[i]) _pcm2Item[i]->Release(); if (_pcm3Item[i]) _pcm3Item[i]->Release(); } } STDMETHODIMP CContextMenuOnContextMenuArray::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENTMULTI(CContextMenuOnContextMenuArray, IContextMenu, IContextMenu3), // IID_IContextMenu QITABENTMULTI(CContextMenuOnContextMenuArray, IContextMenu2, IContextMenu3), // IID_IContextMenu2 QITABENT(CContextMenuOnContextMenuArray, IContextMenu3), // IID_IContextMenu3 QITABENT(CContextMenuOnContextMenuArray, IObjectWithSite), // IID_IObjectWithSite { 0 }, }; return QISearch(this, qit, riid, ppv); } STDMETHODIMP_(ULONG) CContextMenuOnContextMenuArray::AddRef() { return InterlockedIncrement(&_cRef); } STDMETHODIMP_(ULONG) CContextMenuOnContextMenuArray::Release() { if (InterlockedDecrement(&_cRef)) return _cRef; delete this; return 0; } STDMETHODIMP CContextMenuOnContextMenuArray::SetSite(IUnknown *punkSite) { // let all the kids know for (UINT i = 0; i < _count; i++) { IUnknown_SetSite(_pcmItem[i], punkSite); } return CObjectWithSite::SetSite(punkSite); } STDMETHODIMP CContextMenuOnContextMenuArray::QueryContextMenu(HMENU hmenu, UINT indexMenu,UINT idCmdFirst,UINT idCmdLast,UINT uFlags) { _idFirst = idCmdFirst; // We need the placeholder for the below to work if (InsertMenu(hmenu, indexMenu, MF_BYPOSITION|MF_STRING, 0, L"{44075D61-2050-4DF4-BC5D-CBA88A84E75B}")) { BOOL fIndexMenuIsPlaceholder = TRUE; // For each of our context menus... for (UINT i = 0; i < _count && idCmdFirst < idCmdLast; i++) { HRESULT hr = _pcmItem[i]->QueryContextMenu(hmenu, indexMenu, idCmdFirst, idCmdLast, uFlags); if (SUCCEEDED(hr)) { fIndexMenuIsPlaceholder = FALSE; _idOffsets[i] = idCmdFirst - _idFirst + (UINT)ShortFromResult(hr); idCmdFirst = idCmdFirst + (UINT)ShortFromResult(hr) + 1; // Find the placeholder so we know where to insert the next menu int cMenuItems = GetMenuItemCount(hmenu); for (int iItem = 0; iItem < cMenuItems; iItem++) { WCHAR szName[60]; if (GetMenuString(hmenu, (iItem + indexMenu) % cMenuItems, szName, ARRAYSIZE(szName), MF_BYPOSITION) && !lstrcmp(szName, L"{44075D61-2050-4DF4-BC5D-CBA88A84E75B}")) { indexMenu = (iItem + indexMenu) % cMenuItems; fIndexMenuIsPlaceholder = TRUE; break; } } RIPMSG(fIndexMenuIsPlaceholder, "CContextMenuOnContextMenuArray::QueryContextMenu - some context menu removed our placeholder string"); } else { if (0 == i) _idOffsets[i] = 0; else _idOffsets[i] = _idOffsets[i-1]; } } // Remove the placeholder if (fIndexMenuIsPlaceholder) { DeleteMenu(hmenu, indexMenu, MF_BYPOSITION); } } else { TraceMsg(TF_ERROR, "CContextMenuOnContextMenuArray::QueryContextMenu - could not add placeholder element"); } return idCmdFirst - _idFirst; } STDMETHODIMP CContextMenuOnContextMenuArray::InvokeCommand(LPCMINVOKECOMMANDINFO lpici) { HRESULT hr; for (UINT i = 0; i < _count; i++) { if (IS_INTRESOURCE(lpici->lpVerb)) { UINT idCmd = (UINT)LOWORD((DWORD_PTR)lpici->lpVerb); if (idCmd <= _idOffsets[i]) { // adjust id to be in proper range for this pcm if (i > 0) { lpici->lpVerb = MAKEINTRESOURCEA(idCmd - _idOffsets[i-1] - 1); } hr = _pcmItem[i]->InvokeCommand(lpici); return hr; } } else { // I guess we try until it works hr = _pcmItem[i]->InvokeCommand(lpici); if (SUCCEEDED(hr)) return hr; } } TraceMsg(TF_ERROR, "Someone's passing CContextMenuOnContextMenuArray::InvokeCommand an id we didn't insert..."); return E_FAIL; } STDMETHODIMP CContextMenuOnContextMenuArray::GetCommandString(UINT_PTR idCmd, UINT wFlags, UINT * pmf, LPSTR pszName, UINT cchMax) { for (UINT i = 0; i < _count; i++) { if (idCmd <= _idOffsets[i]) { // adjust id to be in proper range for this pcm if (i>0) { idCmd = idCmd - _idOffsets[i-1] - 1; } return _pcmItem[i]->GetCommandString(idCmd, wFlags, pmf, pszName, cchMax); } } TraceMsg(TF_ERROR, "Someone's passing CContextMenuOnContextMenuArray::GetCommandString an id we didn't insert..."); return E_FAIL; } STDMETHODIMP CContextMenuOnContextMenuArray::HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam) { return HandleMenuMsg2(uMsg, wParam, lParam, NULL); } STDMETHODIMP CContextMenuOnContextMenuArray::HandleMenuMsg2(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plResult) { HRESULT hr = E_FAIL; UINT idCmd; // Find the menu command id -- it's packed differently depending on the message // switch (uMsg) { case WM_MEASUREITEM: idCmd = ((MEASUREITEMSTRUCT *)lParam)->itemID; break; case WM_DRAWITEM: idCmd = ((LPDRAWITEMSTRUCT)lParam)->itemID; break; case WM_INITMENUPOPUP: idCmd = GetMenuItemID((HMENU)wParam, 0); break; case WM_MENUSELECT: { idCmd = GET_WM_MENUSELECT_CMD(wParam, lParam); UINT wFlags = GET_WM_MENUSELECT_FLAGS(wParam, lParam); // if idCmd is an offset, convert it to a menu id if (wFlags & MF_POPUP) { MENUITEMINFO miiSubMenu; miiSubMenu.cbSize = sizeof(MENUITEMINFO); miiSubMenu.fMask = MIIM_ID; miiSubMenu.cch = 0; // just in case if (GetMenuItemInfo(GET_WM_MENUSELECT_HMENU(wParam, lParam), idCmd, TRUE, &miiSubMenu)) { idCmd = miiSubMenu.wID; } else { return E_FAIL; } } } break; case WM_MENUCHAR: if (NULL != plResult) { for (UINT i = 0; i < _count; i++) { if (_pcm3Item[i]) { hr = _pcm3Item[i]->HandleMenuMsg2(uMsg, wParam, lParam, plResult); if (S_OK == hr) return hr; } } } return E_FAIL; default: return E_FAIL; } // make sure it's in our range if (idCmd >= _idFirst) { idCmd -= _idFirst; for (UINT i = 0; i < _count; i++) { if (idCmd <= _idOffsets[i]) { if (_pcm3Item[i]) hr = _pcm3Item[i]->HandleMenuMsg2(uMsg, wParam, lParam, plResult); else if (_pcm2Item[i] && NULL == plResult) hr = _pcm2Item[i]->HandleMenuMsg(uMsg, wParam, lParam); break; } } } return hr; } // CContextMenuOnHMENU takes ownership of HMENU and creates // an IContextMenu implementation out of it, forwarding all // messages to hwndOwner. // class CContextMenuOnHMENU : IContextMenu3 { public: // IUnknown STDMETHODIMP QueryInterface(REFIID riid, void **ppv); STDMETHODIMP_(ULONG) AddRef(void) ; STDMETHODIMP_(ULONG) Release(void); // IContextMenu STDMETHODIMP QueryContextMenu(HMENU hmenu, UINT indexMenu,UINT idCmdFirst,UINT idCmdLast,UINT uFlags); STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO lpici); STDMETHODIMP GetCommandString(UINT_PTR idCmd, UINT uType, UINT *pwReserved, LPSTR pszName, UINT cchMax); // IContextMenu2 STDMETHODIMP HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam); // IContextMenu3 STDMETHODIMP HandleMenuMsg2(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plResult); protected: CContextMenuOnHMENU(HMENU hmenu, HWND hwndOwner); virtual ~CContextMenuOnHMENU(); friend HRESULT Create_ContextMenuOnHMENU(HMENU hmenu, HWND hwndOwner, REFIID iid, void** ppv); private: LONG _cRef; HMENU _hmenu; // menu to wrap HWND _hwndOwner;// window to forward menu messages to UINT _idCmdFirst; UINT _rgid[200]; // mapping of context menu ids to the original hmenu command ids (arbitrary limit to the size of an hmenu we support) UINT _crgid; void _RebaseMenu(HMENU hmenu, UINT uFlags); // maps _hmenu's ids such that _rgid[newid-1]=oldid BOOL _IsValidID(UINT wID) { return (wID > 0 && wID <= _crgid); } // can we index _rgid[] with [wID-1]? }; CContextMenuOnHMENU::CContextMenuOnHMENU(HMENU hmenu, HWND hwndOwner) : _cRef(1) { _hmenu = hmenu; _hwndOwner = hwndOwner; } // takes ownership of hmenu HRESULT Create_ContextMenuOnHMENU(HMENU hmenu, HWND hwndOwner, REFIID riid, void** ppv) { HRESULT hr; *ppv = NULL; if (hmenu) { CContextMenuOnHMENU* p = new CContextMenuOnHMENU(hmenu, hwndOwner); if (p) { hr = p->QueryInterface(riid, ppv); p->Release(); } else { DestroyMenu(hmenu); hr = E_OUTOFMEMORY; } } else { hr = E_OUTOFMEMORY; // caller probably just didn't check for this error case } return hr; } CContextMenuOnHMENU::~CContextMenuOnHMENU() { DestroyMenu(_hmenu); } STDMETHODIMP CContextMenuOnHMENU::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CContextMenuOnHMENU, IContextMenu3), // IID_IContextMenu3 QITABENTMULTI(CContextMenuOnHMENU, IContextMenu2, IContextMenu3), // IID_IContextMenu2 QITABENTMULTI(CContextMenuOnHMENU, IContextMenu, IContextMenu3), // IID_IContextMenu { 0 }, }; return QISearch(this, qit, riid, ppv); } STDMETHODIMP_(ULONG) CContextMenuOnHMENU::AddRef() { return InterlockedIncrement(&_cRef); } STDMETHODIMP_(ULONG) CContextMenuOnHMENU::Release() { if (InterlockedDecrement(&_cRef)) return _cRef; delete this; return 0; } // What is the lowest menu id used in hmenu? // (Note that "-1" is often used for separators, // but that is a very LARGE number...) // void CContextMenuOnHMENU::_RebaseMenu(HMENU hmenu, UINT uFlags) { for (int nItem = GetMenuItemCount(hmenu) - 1; nItem >= 0; nItem--) { MENUITEMINFO mii = {0}; mii.cbSize = sizeof(MENUITEMINFO); mii.fMask = MIIM_ID | MIIM_SUBMENU; if (!GetMenuItemInfo(hmenu, nItem, TRUE, &mii)) { continue; } if (!mii.hSubMenu || (uFlags & MM_SUBMENUSHAVEIDS)) { if (_crgid < ARRAYSIZE(_rgid)) { _rgid[_crgid] = mii.wID; mii.wID = ++_crgid; SetMenuItemInfo(hmenu, nItem, TRUE, &mii); } else { RIPMSG(FALSE, "CContextMenuOnHMENU::_RebaseMenu() - Someone is using an HMENU that's too big..."); DeleteMenu(hmenu, nItem, MF_BYPOSITION); continue; } } if (mii.hSubMenu) { _RebaseMenu(mii.hSubMenu, uFlags); } } } HRESULT CContextMenuOnHMENU::QueryContextMenu(HMENU hmenu, UINT indexMenu,UINT idCmdFirst,UINT idCmdLast,UINT uFlags) { _idCmdFirst = idCmdFirst; _RebaseMenu(_hmenu, uFlags); UINT idMax = Shell_MergeMenus(hmenu, _hmenu, indexMenu, idCmdFirst, idCmdLast, uFlags); return idMax - _idCmdFirst; } HRESULT CContextMenuOnHMENU::InvokeCommand(LPCMINVOKECOMMANDINFO lpici) { if (IS_INTRESOURCE(lpici->lpVerb)) { UINT wID = LOWORD((UINT_PTR)lpici->lpVerb); RIPMSG(_IsValidID(wID), "CContextMenuOnHMENU::InvokeCommand() received invalid wID"); if (_IsValidID(wID)) { wID = _rgid[wID-1]; SendMessage(_hwndOwner, WM_COMMAND, GET_WM_COMMAND_MPS(wID, 0, 0)); return S_OK; } } return E_INVALIDARG; } HRESULT CContextMenuOnHMENU::GetCommandString(UINT_PTR idCmd, UINT uType, UINT *pwReserved, LPSTR pszName, UINT cchMax) { if (cchMax) pszName[0] = 0; if (IS_INTRESOURCE(idCmd)) { RIPMSG(_IsValidID(idCmd), "CContextMenuOnHMENU::InvokeCommand() received invalid idCmd"); if (_IsValidID(idCmd)) { UINT wID = _rgid[idCmd - 1]; switch (uType) { case GCS_HELPTEXT: // The only time this seems to be called is in response to a WM_MENUSELECT, // so forward it back to _hwndOwner so it can be the real WM_MENUSELECT SendMessage(_hwndOwner, WM_MENUSELECT, GET_WM_MENUSELECT_MPS(wID, 0, _hmenu)); return E_FAIL; } return E_NOTIMPL; } } return E_INVALIDARG; } HRESULT CContextMenuOnHMENU::HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam) { return HandleMenuMsg2(uMsg,wParam,lParam,NULL); } HRESULT CContextMenuOnHMENU::HandleMenuMsg2(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plResult) { HRESULT hr = E_FAIL; LRESULT lRes = 0; switch (uMsg) { case WM_INITMENUPOPUP: lRes = SendMessage(_hwndOwner, uMsg, (WPARAM)_hmenu, lParam); hr = S_OK; break; case WM_DRAWITEM: { LPDRAWITEMSTRUCT pdi = ((LPDRAWITEMSTRUCT)lParam); DRAWITEMSTRUCT di = *pdi; RIPMSG(_IsValidID(di.itemID - _idCmdFirst), "CContextMenuOnHMENU::HandleMenuMsg2(WM_DRAWITEM) received invalid itemID"); if (_IsValidID(di.itemID - _idCmdFirst)) { di.itemID = _rgid[di.itemID - _idCmdFirst - 1]; lRes = SendMessage(_hwndOwner, uMsg, wParam, (LPARAM)&di); hr = S_OK; } else hr = E_INVALIDARG; break; } case WM_MEASUREITEM: { LPMEASUREITEMSTRUCT pmi =((LPMEASUREITEMSTRUCT)lParam); MEASUREITEMSTRUCT mi = *pmi; RIPMSG(_IsValidID(mi.itemID - _idCmdFirst), "CContextMenuOnHMENU::HandleMenuMsg2(WM_MEASUREITEM) received invalid itemID"); if (_IsValidID(mi.itemID - _idCmdFirst)) { mi.itemID = _rgid[mi.itemID - _idCmdFirst - 1]; lRes = SendMessage(_hwndOwner, uMsg, wParam, (LPARAM)&mi); hr = S_OK; } else hr = E_INVALIDARG; break; } case WM_MENUSELECT: { UINT wID = GET_WM_MENUSELECT_CMD(wParam, lParam); UINT wFlags = GET_WM_MENUSELECT_FLAGS(wParam, lParam); if (!(wFlags & MF_POPUP)) { RIPMSG(_IsValidID(wID - _idCmdFirst), "CContextMenuOnHMENU::HandleMenuMsg2(WM_MENUSELECT) received invalid wID"); if (_IsValidID(wID - _idCmdFirst)) { wID = _rgid[wID - _idCmdFirst - 1]; } else { hr = E_INVALIDARG; break; } } lRes = SendMessage(_hwndOwner, uMsg, GET_WM_MENUSELECT_MPS(wID, wFlags, _hmenu)); hr = S_OK; break; } case WM_MENUCHAR: // should probably be SendMessage(_hwndOwner, uMsg, wParam, (LPARAM)_hmenu) // but our WM_MENUCHAR forwarding doesn't find the correct owner... // lRes = DefWindowProc(_hwndOwner, uMsg, wParam, (LPARAM)_hmenu); hr = (0 == lRes) ? S_FALSE : S_OK; break; default: RIPMSG(FALSE, "CContextMenuOnHMENU::HandleMenuMsg2 was forwarded an unexpected window message"); lRes = 0; hr = E_FAIL; break; } if (plResult) *plResult = lRes; return hr; } // Forward everything to the given context menu, // but remove menu items with the canonical verbs // given in the semicolon-separated list of canonical verbs // class CContextMenuWithoutVerbs : CContextMenuForwarder { public: STDMETHODIMP QueryContextMenu(HMENU hmenu, UINT indexMenu,UINT idCmdFirst,UINT idCmdLast,UINT uFlags); protected: CContextMenuWithoutVerbs(IUnknown* punk, LPCWSTR pszVerbList); friend HRESULT Create_ContextMenuWithoutVerbs(IUnknown* punk, LPCWSTR pszVerbList, REFIID riid, void **ppv); private: LPCWSTR _pszVerbList; }; CContextMenuWithoutVerbs::CContextMenuWithoutVerbs(IUnknown* punk, LPCWSTR pszVerbList) : CContextMenuForwarder(punk) { _pszVerbList = pszVerbList; // no reference - this should be a pointer to the code segment } HRESULT Create_ContextMenuWithoutVerbs(IUnknown* punk, LPCWSTR pszVerbList, REFIID riid, void **ppv) { HRESULT hr = E_OUTOFMEMORY; *ppv = NULL; if (pszVerbList) { CContextMenuWithoutVerbs* p = new CContextMenuWithoutVerbs(punk, pszVerbList); if (p) { hr = p->QueryInterface(riid, ppv); p->Release(); } } return hr; } HRESULT CContextMenuWithoutVerbs::QueryContextMenu(HMENU hmenu, UINT indexMenu,UINT idCmdFirst,UINT idCmdLast,UINT uFlags) { HRESULT hr = CContextMenuForwarder::QueryContextMenu(hmenu,indexMenu,idCmdFirst,idCmdLast,uFlags); if (SUCCEEDED(hr)) { LPCWSTR pszVerb = _pszVerbList; while (*pszVerb) { WCHAR szVerb[64]; LPCWSTR pszNext = StrChrW(pszVerb, L';'); if (pszNext) { UINT cch = (UINT)(pszNext - pszVerb) + 1; ASSERT(0 < cch && cch < ARRAYSIZE(szVerb)); // we should be large enough for all the canonical verbs we use StrCpyN(szVerb, pszVerb, min(cch, ARRAYSIZE(szVerb))); pszVerb = pszNext + 1; } else { UINT cch = lstrlen(pszVerb) + 1; ASSERT(0 < cch && cch < ARRAYSIZE(szVerb)); // we should be large enough for all the canonical verbs we use StrCpyN(szVerb, pszVerb, min(cch, ARRAYSIZE(szVerb))); pszVerb += cch - 1; // point at NULL } ContextMenu_DeleteCommandByName(_pcm, hmenu, idCmdFirst, szVerb); } } return hr; }