Click here to Skip to main content
Click here to Skip to main content

How to Use Submenus in a Context Menu Shell Extension

By , 14 Feb 2003
 

Submenus in a context menu extension

In this article, I'll cover a tricky aspect of context menu extensions - submenus. The approach most people take at first to creating submenus leads to odd behavior in Explorer, but once you know the trick to make Explorer manage the menu correctly, it's easy! This article assumes you have a good grasp of context menu extensions. If you need a refresher, see part 1 and part 2 of my shell extension series.

Adding a submenu

This article's extension is a simple Open With... submenu, which has two items, Notepad and Internet Explorer. It behaves like the enhanced Open With menu in XP, and opens the selected file in the program that you pick. This Open With menu will demonstrate how to properly create a submenu in an extension.

What you might try first

The first thing that comes to mind is to create a new menu with CreatePopupMenu() and insert it into the menu provided by Explorer.

HRESULT COpenWithCtxMenuExt::QueryContextMenu ( 
  HMENU hmenu, UINT uMenuIndex, UINT uidFirstCmd, UINT uidLastCmd, UINT uFlags )
{
    // If the flags include CMF_DEFAULTONLY then we shouldn't do anything.
    if ( uFlags & CMF_DEFAULTONLY )
        return MAKE_HRESULT ( SEVERITY_SUCCESS, FACILITY_NULL, 0 );

    // First, create and populate a submenu.
    HMENU hSubmenu = CreatePopupMenu();
    UINT uID = uidFirstCmd;

    InsertMenu ( hSubmenu, 0, MF_BYPOSITION, uID++, _T("&Notepad") );
    InsertMenu ( hSubmenu, 1, MF_BYPOSITION, uID++, _T("&Internet Explorer") );

    // Insert the submenu into the ctx menu provided by Explorer.
    <FONT color=red>InsertMenu ( hmenu, uMenuIndex, MF_BYPOSITION | MF_POPUP, 
                 (UINT_PTR) hSubmenu, _T("C&P Open With") );</FONT>

    return MAKE_HRESULT ( SEVERITY_SUCCESS, FACILITY_NULL, uID - uidFirstCmd );
}

This actually works fine for the context menu, however the context menu items you add are duplicated on Explorer's File menu. If you invoke the context menu repeatedly, you'll see leftover popups on the File menu:

 [Leftover submenus - 12K]

The cause is related to how Explorer cleans up its menus after extensions are invoked. The return value of QueryContextMenu() tells Explorer how many items we add, and Explorer cleans up by calculating the IDs of our items and deleting them. Explorer knows the ID of the first item (since it passes us the value as uidCmdFirst) and can calculate the IDs of the others since the IDs are assumed to be consecutive. However, the popup menu has no ID, so Explorer doesn't delete it.

The correct way

The secret is to insert the submenu using the InsertMenuItem() API. What's different about using InsertMenuItem() is that you can give the submenu an ID, which isn't possible when you use InsertMenu(). Here's the corrected QueryContextMenu() code.

HRESULT COpenWithCtxMenuExt::QueryContextMenu ( 
  HMENU hmenu, UINT uMenuIndex, UINT uidFirstCmd, UINT uidLastCmd, UINT uFlags )
{
    // If the flags include CMF_DEFAULTONLY then we shouldn't do anything.
    if ( uFlags & CMF_DEFAULTONLY )
        return MAKE_HRESULT ( SEVERITY_SUCCESS, FACILITY_NULL, 0 );

    // First, create and populate a submenu.
    HMENU hSubmenu = CreatePopupMenu();
    UINT uID = uidFirstCmd;

    InsertMenu ( hSubmenu, 0, MF_BYPOSITION, uID++, _T("&Notepad") );
    InsertMenu ( hSubmenu, 1, MF_BYPOSITION, uID++, _T("&Internet Explorer") );

    // Insert the submenu into the ctx menu provided by Explorer.
<FONT color=red>    MENUITEMINFO mii = { sizeof(MENUITEMINFO) };

    mii.fMask = MIIM_SUBMENU | MIIM_STRING | MIIM_ID;
    mii.wID = uID++;
    mii.hSubMenu = hSubmenu;
    mii.dwTypeData = _T("C&P Open With");</FONT>

    <FONT color=red>InsertMenuItem ( hmenu, uMenuIndex, TRUE, &mii );</FONT>

    return MAKE_HRESULT ( SEVERITY_SUCCESS, FACILITY_NULL, uID - uidFirstCmd );
}

The return value this time is 3, which tells Explorer that we inserted 3 items (the two Open With items, and the submenu itself). Since all 3 items have IDs, Explorer can delete them all and completely remove our items from its menu.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Michael Dunn
Software Developer (Senior) VMware
United States United States
Member
Michael lives in sunny Mountain View, California. He started programming with an Apple //e in 4th grade, graduated from UCLA with a math degree in 1994, and immediately landed a job as a QA engineer at Symantec, working on the Norton AntiVirus team. He pretty much taught himself Windows and MFC programming, and in 1999 he designed and coded a new interface for Norton AntiVirus 2000.
Mike has been a a developer at Napster and at his own lil' startup, Zabersoft, a development company he co-founded with offices in Los Angeles and Odense, Denmark. Mike is now a senior engineer at VMware.

He also enjoys his hobbies of playing pinball, bike riding, photography, and Domion on Friday nights (current favorite combo: Village + double Pirate Ship). He would get his own snooker table too if they weren't so darn big! He is also sad that he's forgotten the languages he's studied: French, Mandarin Chinese, and Japanese.
 
Mike was a VC MVP from 2005 to 2009.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 5memberjerry_wangjh25 Oct '11 - 22:11 
Hi Mick, You are my life saver!
QuestionConverting this project to Dev C++ - not possible?memberGoran _11 Jul '08 - 14:51 
Is it possible to convert this project to Dev C++ project?
 
Thanks,
Gora
QuestionCan we make it dynamic?memberGoran _5 Jul '08 - 15:33 
Can we make subitems dynamically change, reading items from a text file, for example? And the list should be changed immediately after the items are read, not after restaring windows?
 
Also, is it possible to create shortcuts for menu items?
 
And can this code reside in a exe file, or it is a must to reside in a dll?
 
Thanks,
Goran
QuestionHelp needed - Submenus using c#memberSharath Ambati1 Apr '08 - 23:55 
I need to achieve the same using C#. Help Needed.
GeneralRe: Help needed - Submenus using c#mvpMichael Dunn3 Apr '08 - 13:38 
Try the C# forum[^]
 
--Mike--
Visual C++ MVP Cool | :cool:
LINKS~! PimpFish | CP SearchBar v3.0 | C++ Forum FAQ
 
"That's what's great about doing user interface work. No matter what you do, people will say that what you did was idiotic." -- Raymond Chen

Generaldefault context menu loading issue in vista...........memberaditya_mnit3 Mar '08 - 0:45 
Hi MIke,
We are implementing explorer like a virtual folder in windows vista and while creating "New" menu along with our customized context menu it is not loading default sub menu items("New Text document", "New Word Document" etc as shown in snapshot Windows_contextmenu.jpg (http://www.driveway.com/c6f1j0y1y3)) for the "New" menu of our customized explorer as shown in snapshot customeized_contextmenu.jpg ( http://www.driveway.com/h3q9s0x9q2 )
We are creating the shell folder using SHCreateShellFolderView() routine.
any suggestions..... how to solve this...........
 
Aditya

GeneralRe: default context menu loading issue in vista...........mvpMichael Dunn16 Mar '08 - 16:16 
See How to host an IContextMenu, part 5 - Handling menu messages[^], Raymond mentions the built-in New and Send To menus.
 
--Mike--
Visual C++ MVP Cool | :cool:
LINKS~! CP SearchBar v3.0 | C++ Forum FAQ
 
I work for Keyser Söze

Questionhow to get the pressed item ?membercarabutnicolae123413 Dec '07 - 3:33 
hello,
 
need to ask it because don't get it.
how do you get the ID of item being pressed ?
QuestionMFT_OWNERDRAW but not reallymemberXygorn10 Nov '06 - 20:14 
Michael,
 
I am working on a problem similar to that of Grigri's from back in 2003. I know that he has a Code Project article about his solution to the problem (called ShellPilot). As he discussed with you, he uses MFT_OWNERDRAW and stores some information in the WM_DRAWITEM handler.
Unfortunately, he is then required to do all the handling of the drawing of the menu item himself. I am worried that this may not work properly with all the wide variety of UI customizations that can be done (Themes, skinning, changing default fonts, etc.).
 
Is there a way that I can, after receiving the WM_DRAWITEM and WM_MEASUREITEM messages, and storing some information, pass the message back to the default handler?
 
--
Eli Gibson
AnswerRe: MFT_OWNERDRAW but not reallysitebuilderMichael Dunn12 Nov '06 - 13:47 
If you use the APIs to get the various system metrics and colors, your menus will look "right" no matter what the current theme is.
There is no default behavior for WM_MEASUREITEM and WM_DRAWITEM. What would the system do? The app has an owner-draw object in its UI, but doesn't want to handle an owner-draw message?
 
--Mike--
Visual C++ MVP Cool | :cool:
LINKS~! Ericahist | PimpFish | CP SearchBar v3.0 | C++ Forum FAQ

QuestionWhat about using MF_BYCOMMANDmemberhighdoe19 Mar '06 - 19:41 
Hi,
 
If context menu items depends on user selection(s), and one item must be removed for some reason, using menu index is not reliable anymore because GetCommandString and InvokeCommand won't display and start what is supposed. Your example is great if context menu items we add are always the same, but there is a way to use a command id like 101,102,103 and being able to display correct information in statusbar and start the right function if we remove command id 102.
I'm not sure if my question is clear (english is not my first language).
 
I'm coding a context menu with MASM and your example is helping but I just can't find what is missing to do what I want. It's almost impossible that there is no way of doing it because WinZip, WinRAR are doing it in some way I can't figure.
 
Thanks for your help.

AnswerRe: What about using MF_BYCOMMANDsitebuilderMichael Dunn22 Mar '06 - 11:07 
I haven't thought about this in a while, nor have I tested this (yeah I'm lazy), but I think the index parameter is the index relative to idFirst.
 
--Mike--
Visual C++ MVP Cool | :cool:
LINKS~! Ericahist | NEW!! PimpFish | CP SearchBar v3.0 | C++ Forum FAQ
GeneralRe: What about using MF_BYCOMMANDmemberhighdoe23 Mar '06 - 6:35 
It's ok I'll do the research as I should.
 
BTW, There is one mistake that make GetCommandString to failed on first menu item.
 
The first index should start with 1 like this...

InsertMenu ( hSubmenu, 1, MF_BYPOSITION, uID++, _T("&Notepad") );
InsertMenu ( hSubmenu, 2, MF_BYPOSITION, uID++, _T("&Internet Explorer") );

 
And wID from MENUITEMINFO should be uidFirstCmd like this...

mii.fMask = MIIM_SUBMENU | MIIM_STRING | MIIM_ID;
mii.wID = uidFirstCmd;
mii.hSubMenu = hSubmenu;
mii.dwTypeData = _T("C&P Open With");

 
Finally you must increment uID by 1 as it was before replacing wID from uID to uidFirstCmd (it's a menu item anyway and it counts).

uID++
 
InsertMenuItem ( hmenu, uMenuIndex, TRUE, &mii );

 

This way you don't loose the ability to get statusbar text on any menu items that you add (GetCommandString).
 

 
-- modified at 23:30 Thursday 23rd March, 2006
QuestionThere's one thing I can't understand ...memberAssarbad31 Dec '05 - 23:40 
You claim that I have to return the number of added menu items. But what for? If I add *one* submenu item and give it the correct ID, I still added *only one* item.
 
Why is that? Well, the popup menu obviously has its own range of IDs (which you mention in you first reply below: "Re: All well and dandy, but what about dynamic submenus?"), so explorer can *at most* know that the current item is a submenu and kill the submenu, which will consequently remove the items, but isn't it ridiculous to assume that Explorer takes the same ID and then tries to remove items from the submenus - which require a different menu handle for one but would be easier to remove by just killing the submenu (popup menu)?! If you ever created a menu for a window, did you remove the menus after it one-by-one or did you just delete the menu to see all items vanish?
 
Are you sure that is the case? I will now try it without caring for the submenu IDs and will turn back to this thread with my results.
 
Cheers,
 
Oliver
 
-- modified at 5:44 Sunday 1st January, 2006
AnswerRe: There's one thing I can't understand ...sitebuilderMichael Dunn1 Jan '06 - 8:28 
Assarbad wrote:
You claim that I have to return the number of added menu items. But what for?


Because that's what the documentation says to do. Ignore the docs at your own risk; the fact that it works now doesn't mean it'll work in future revs of the OS.
 
--Mike--
Visual C++ MVP Cool | :cool:
LINKS~! Ericahist | PimpFish | CP SearchBar v3.0 | C++ Forum FAQ
Come quietly or there will be... trouble.
GeneralRe: There's one thing I can't understand ...memberAssarbad1 Jan '06 - 9:04 
Actually it doesn't (at least mine, how about yours Wink | ;) ):
If successful, returns an HRESULT value that has its severity value set to SEVERITY_SUCCESS and its code value set to the offset of the largest command identifier that was assigned, plus one. For example, assume that idCmdFirst is set to 5 and you add three items to the menu with command identifiers of 5, 7, and 8. The return value should be MAKE_HRESULT(SEVERITY_SUCCESS, 0, 8 - 5 + 1). Otherwise, it returns an OLE error value.
It says "the menu", which, by my understanding of the English language (I am not a native speaker), refers to the HMENU parameter passed in to the function, not to any other menu item belonging to a completely different menu identified by a completely different menu handle. The question here is, why should Explorer even care about what (or how much) is inside the submenu, if the function DeleteMenu (which is likely to be used by Explorer; and if only implicitly) does the following according to the documentation: The DeleteMenu function deletes an item from the specified menu. If the menu item opens a menu or submenu, this function destroys the handle to the menu or submenu and frees the memory used by the menu or submenu.
 
For your convenience I have looked up the functions on MSDN, the documentation there seems to be the same as on my disk (although I still use the Server 2003 PSDK from Feb. 2003).
 
DeleteMenu
IContextMenu::QueryContextMenu
 
//Added:
By the way, some people asked why there is only a narrow range defined by idCmdFirst and idCmdLast. If it was like I said (has not been proven, yet) this would be the answer as well. Actually any other behavior would be quite odd, since the similar behavior on a file system would mean no less than that a certain file name can only be used once throughout the file system, no matter in which folder. I guess most of us would call this strange, no? Confused | :confused:
 
Cheers,
 
Oliver
 
-- modified at 15:11 Sunday 1st January, 2006
GeneralStill duplicated Menumemberswim071027 Dec '05 - 14:03 
This article is really helpful to me but I still have a problem with making a submenu.
 
I'm working on Windows Mobile 5.0 which doesn't support InsertMenuItem().
 
so I tried to do like this,
 

MENUITEMINFO menuItemInfo ;
menuItemInfo.cbSize = sizeof (MENUITEMINFO);
menuItemInfo.fMask = MIIM_ID | MIIM_SUBMENU ;
menuItemInfo.wID = m_idc1 ;
menuItemInfo.hSubMenu = hSubMenu;
 

InsertMenu ( hmenu, indexMenu ,MF_BYPOSITION | MF_STRING | MF_POPUP, (UINT)hSubMenu, _T("MENU"));
SetMenuItemInfo ( hmenu ,indexMenu , TRUE , &menuItemInfo );
 

But...the context menu items I add are still duplicated on menu.
 
and, what about SEPARATOR which doesn't have a item ID
 
I hope you help me~~^^ Thanks a lot.
 
-- modified at 20:25 Tuesday 27th December, 2005
QuestionRe: Still duplicated Menu [modified]memberNvmkpk29 Aug '06 - 6:49 
I tried it the other way (with windows mobile 5.0).
 
I added a normal menu item and tried to attach it with a submenu but SetMenuInfo doesn't change the menu item to a popup menu:
InsertMenu(hmenu, indexMenu, MF_BYPOSITION, idCmdFirst, _T("My Menu"));
mii.cbSize = sizeof(MENUITEMINFO);
mii.hSubMenu = hMySubMenu;
mii.fMask = MIIM_SUBMENU;
SetMenuItemInfo(hmenu, indexMenu, true, &mii);

Could anybody help?

AnswerRe: Still duplicated Menumembercrino3 Sep '06 - 8:29 
i tried on PPC too but without results Frown | :(
i hope Micheal can give us some tips Smile | :)
AnswerRe: Still duplicated Menumembertdinneen10 Apr '07 - 21:28 
Did you ever get this resolved? help would very much be appreciated!!
 
T.
GeneralRe: Still duplicated MenumemberMarx Chen13 Apr '07 - 15:25 
I have the same issue. Please Help.Cry | :((
GeneralRe: Still duplicated Menumemberdalsegno18 Jun '07 - 0:40 
I have the same problem, is there another solution to create submenus on windows mobile ?
GeneralRe: Still duplicated MenumemberMeNot2 Aug '09 - 18:58 
Hi,
Have u got any solution?
I m facing the same problem.
Plz help me.
Regards,
Kaustubh
Kaaustubh@gmail.com
 

GeneralRe: Still duplicated Menumemberwookong21 Feb '12 - 23:04 
i solve this issue by modify the last line, the return value shoudl be the largest uID + 1, as bellow:
 
return MAKE_HRESULT ( SEVERITY_SUCCESS, FACILITY_NULL, uID + 1);
GeneralAdding command to the right click context menu in Windows XPmemberMr_Wilson_Wilson1 Dec '05 - 14:09 
I know I can add this command to right click context menu like the following:
 
; context_defrag.INF
; Adds Defrag to the right click context menu in Windows XP
[version]
signature="$CHICAGO$"
[DefaultInstall]
AddReg=AddMe
[AddMe]
HKCR,"Drive\Shell\Defrag\command",,,"DEFRAG.EXE %1"
 
But how do I add "scan with Norton AntiVirus" to right click context menu(s) in registry without having to re-install NAV?
 
Thank you
 
From an old guyCool | :cool:
 
"I have no particular talent. I am merely inquisitive" - Albert Einstein

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130516.1 | Last Updated 15 Feb 2003
Article Copyright 2003 by Michael Dunn
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid