Contents
The Windows shell provides the ability to extend the context menu with new menu options. To enable this feature a shell extension must register an IContextMenu
interface for a specific file extension or the global �*� extension. The Mini Shell framework discussed in this article provides a set of helper classes that can be used to create such a shell extension. The IContextMenuImpl
class provides the following functionality:
- An embedded class
CMenu
that makes it easy to create the correct menu entries.
- A registration system that will forward owner drawn menu events to a �paint� handler and select a menu item command to the correct command handler.
- A
CSmallBitmapHandler
class is provided by the framework to create menu items with a small bitmap in front of the menu text.
My original plan was to document IContextMenuImpl
and IShellFolderImpl
in one article. During finalization of the IShellFolderImpl
, I decided to split the description of both classes in two articles. The primary reason for this decision is that, IShellFolderImpl
is very complex and requires on its own already a lot of text to describe (will be documented in part 3). For more background info about the design model and the used .vvv file sample the reader is referred to the first article on the Mini Shell extension Framework.
The requirements for the framework are not changed since the first article. VC.NET 2002 + latest SDK or VC.NET 2003. The framework is verified on Win 98, ME, W2K and XP.
A context menu extension is, as all other shell extensions, a COM object. The first requirement is thus, the registration of the COM class. The framework includes two ATL registrations script to do this. One registration script is intended to be used for files that are normally opened with an application and the other script should be used if the file is opened in explorer (in combination with a shellfolder extension). The included VVV sample demonstrates also a shellfolder extension and uses therefore the contextmenu_sf.rgs registration script.
class ATL_NO_VTABLE CContextMenu :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CContextMenu, & __uuidof(CContextMenu)>,
public IContextMenuImpl<CContextMenu>
{
public:
static HRESULT WINAPI UpdateRegistry(BOOL bRegister) throw()
{
return IContextMenuImpl<CContextMenu>::UpdateRegistry(IDR_CONTEXTMENU,
bRegister, L"Sample ShellExtension ContextMenu",
__uuidof(CShellFolder), wszVVVExtension);
}
The code example above shows the class definition and the static function UpdateRegistry
that ATL will call to register and unregister the extensions. This static function is normally implemented with the DECLARE_REGISTRY_RESOURCEID
macro which is added automatically by the ATL class wizard. To instruct ATL which interfaces our object supports a �COM_MAP� must be setup. The code below shows the interfaces the VVV sample selects to support. The IContextMenu2
and IContextMenu3
interfaces are needed because the VVV sample uses an owner drawn menu item.
BEGIN_COM_MAP(CContextMenu)
COM_INTERFACE_ENTRY(IShellExtInit)
COM_INTERFACE_ENTRY(IContextMenu)
COM_INTERFACE_ENTRY(IContextMenu2)
COM_INTERFACE_ENTRY(IContextMenu3)
END_COM_MAP()
The base class of the framework IContextMenuImpl<>
must be notified which extensions needs to be handled.
CContextMenu()
{
RegisterExtension(_T(�.vvv�));
}
The last required function that must be implemented is the OnQueryContextMenu
. This function is called by the framework after it received a call from the shell through the COM function QueryContextMenu
. The code below shows the implementation for the VVV sample. The first step checks if the files selected by the user only contain .vvv selected files. In the second step the extension checks if only one file is selected (theoretically, only one check is needed). The first entry that is created is a submenu with an owner draw handler. Two menu items are added to this new submenu. The CSmallBitmapHandler
class is provided by the framework. CEditWithNotepadCommand
and CAboutMSFCommand
are command handlers of the VVV sample.
void OnQueryContextMenu(IContextMenuImpl<CContextMenu>::CMenu& menu,
const std::vector<CString>& filenames)
{
if (ContainsUnknownExtension(filenames))
return;
if (filenames.size() != 1)
return;
CMenu menuVVV =
menu.AddSubMenu(IDS_CONTEXTMENU_VVV_SUBMENU_HELP,
CCustomMenuHandlerPtr(new
CSmallBitmapHandler(IDS_CONTEXTMENU_VVV_SUBMENU,
IDB_MENUICON)));
menuVVV.AddItem(IDS_CONTEXTMENU_EDIT_WITH_NOTEPAD,
IDS_CONTEXTMENU_EDIT_WITH_NOTEPAD_HELP,
CContextCommandPtr(new CEditWithNotepadCommand()));
menuVVV.AddItem(IDS_CONTEXTMENU_ABOUT_MSF_HELP,
CContextCommandPtr(new CAboutMSFCommand()),
CCustomMenuHandlerPtr(new
CSmallBitmapHandler(IDS_CONTEXTMENU_ABOUT_MSF,
IDB_MENUICON)));
}
The framework uses functors to forwarded the selected menu item as action to the shell extension. These functor objects must be passed as one of the arguments when menu items are created. The functor CEditWithNotepadCommand
used by the sample will start notepad to edit the selected .VVV file. The CreateProcess
function that is used is a �short� version of the Win32 CreateProcess
function and is one of the utility functions provided by the framework.
class CEditWithNotepadCommand : public CContextCommand
{
public:
virtual void operator()(const CMINVOKECOMMANDINFO* ,
const std::vector<CString> & filenames)
{
ATLASSERT(filenames.size() == 1);
CString strCmd = _T("notepad.exe \"") + filenames[0] + _T("\"");
CreateProcess(NULL, strCmd.GetBuffer());
}
};
The other CAboutMSFCommand
functor is even simpler. It just formats a string and displays it to the user. The IsolationAwareMessageBox
messagebox function is called to use the new XP style (when running on XP). Tip: MessageBox
is one of the functions that is not redefined by the SDK headers when isolation aware is enabled, it must be called explicit.
class CAboutMSFCommand : public CContextCommand
{
public:
virtual void operator()
(const CMINVOKECOMMANDINFO* pici,
const std::vector<CString> & )
{
CString strText;
strText.Format(IDS_CONTEXTMENU_ABOUT_MASK,
HIWORD(MSF_VER), LOWORD(MSF_VER));
IsolationAwareMessageBox(pici->hwnd, strText,
LoadString(IDS_CONTEXTMENU_CAPTION), MB_OK);
}
};
The basic menu system has changed very little in the history of Windows. To provide a �cooler� look, many applications draw their own menus. The windows shell menu itself only uses images for the �open with� and �send to� submenus. A contextmenu extension can also use owner draw menu items. There are three options to add graphics to a menu item:
- Owner draw. The extension is completely free to draw the menu itself.
- Using a small menu bitmap. Menu items can use a small bitmap before the text. The X mark before �close� in the system menu is a good example (not supported on all Windows versions).
- Use a custom check mark.
The framework provided a support class CSmallBitmapHandler
that can be used to add small bitmaps (13x13) to menu items. This class uses the custom check mark option to add the small bitmap before the text. The main advantage of this technique is that the text of the menu item is drawn the same as all other menu items. This prevents that the �added� menu item looks different or strange. Complete owner drawn items and bitmap menu items are better reserved for a submenu that is under complete control of the extension.