To create a context menu handler, you must implement both the IShellExtInit and the IContextMenu interfaces. In addition to the usual IUnknown functions, the context menu handler interface uses the QueryContextMenu, InvokeCommand, and GetCommandString member functions.
The QueryContextMenu member function is called just before the system displays an object's context menu. A context menu handler inserts a menu item by position (MF_BYPOSITION) directly into the context menu by calling InsertMenu. In the call to InsertMenu, QueryContextMenu passes the handle to the context menu in the hMenu parameter. The second parameter, indexMenu, specifies where to insert the menu item; and the third parameter, idCmdFirst, provides the first menu item identifier you should use for that context menu. Because menu items must be string items (MF_STRING), the uFlags parameter of InsertMenu must be MF_BYPOSITION | MF_STRING for each menu item the context menu handler inserts. The following example demonstrates how to add three new menu items (Sample Menu Item 1, Sample Menu Item 2, and Sample Menu Item 3) to a context menu:
STDMETHODIMP Sample::QueryContextMenu (HMENU hmenu,
UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
{
int cVerbs = THREE_ITEMS;
for (int i = 0; i < cVerbs; i++)
{
char szMenu [80];
wsprintf (szMenu, "Sample Menu Item #%d", i + 1);
InsertMenu (hmenu, indexMenu + i, MF_BYPOSITION | MF_STRING,
idCmdFirst + i, szMenu);
}
return (HRESULT)cVerbs;
}
InvokeCommand is called when the user selects an item from the context menu for which you have registered a handler. This function is passed a pointer to the LPCMINVOKECOMMANDINFO structure, which contains information including the size of the structure (cbSize), the owner window for any message box or dialog box (hwnd), the validity of the dwHotkey and hIcon parameters (fMask), the command to be executed (lpVerb), the flag to be passed to ShowWindow (nShow), the hot key to be assigned to the application after it is opened (dwHotkey), and the handle to an icon (hIcon). Hot keys and icons are optional. The LOWORD of the lpVerb member contains the menu item identifier offset (the menu item ID minus idCmdFirst), which is used to determine what command the user has chosen. In the following code from the CTXTMENU sample, this value is the new command, ID_NEWCMD, and a message box is displayed:
STDMETHODIMP CTxtMenu::XMenuExt::InvokeCommand (
LPCMINVOKECOMMANDINFO lpici)
{
METHOD_PROLOGUE (CTxtMenu, MenuExt);
char szTest [MAX_PATH * 2];
if (HIWORD (lpici->lpVerb))
{
AfxMessageBox ("E_FAIL");
return E_FAIL;
}
if (LOWORD (lpici->lpVerb) > ID_NEWCMD)
{
AfxMessageBox ("Invalid Arg");
return E_INVALIDARG;
}
if (LOWORD (lpici->lpVerb) == ID_NEWCMD)
{
wsprintf (szTest, "Context menu for file: %s",
pThis->m_szFileName);
::MessageBox (lpici->hwnd, szTest, "Context Menu
Handler", MB_OK);
}
else
::MessageBox (lpici->hwnd, "ID != ID_NEWCMD",
"Context Menu Handler",
MB_OK);
return NOERROR;
}
After you install the context menu handler, click the item labeled Test Context Menu to see the dialog box shown in Figure 13-2.
Figure 13-2.
A context menu handler must also implement GetCommandString. This function is called to provide Help text for a context menu item. GetCommandString simply copies into the pszName string the text that will be displayed:
STDMETHODIMP CTxtMenu::XMenuExt::GetCommandString (
UINT idCmd, UINT uType, UINT *pwReserved, LPSTR pszName, UINT
cchMax)
{
if (idCmd > ID_NEWCMD)
return ResultFromScode (E_INVALIDARG);
if (idCmd == ID_NEWCMD)
lstrcpy (pszName, "Context Menu Test");
return NOERROR;
}