Psychic debugging: Why your IContextMenu::InvokeCommand doesn’t get called even though you returned success from IContextMenu::QueryContextMenu

A customer was having trouble with their IContext­Menu implementation. They observed that their IContext­Menu::Query­Context­Menu method was being called, but when the user selected their menu item, IContext­Menu::Invoke­Command was not being called.

Given what you know about shell context menus, you can already direct the investigation. I'll let you read up about it first, especially the part about composition, then we can see how much you've learned.

Welcome back. (Okay, I know you didn't actually do the reading, but I'm welcoming you back anyway.)

Your first theory as to why IContext­Menu::Invoke­Command is not being called is probably that they returned S_OK from IContext­Menu::Query­Context­Menu instead of MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, 1). That would explain the problem, because a return value of S_OK is equivalent to MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, 0), which means "I successfully added up to zero menu items starting at idCmd­First." When the user picks the menu item they added, the dispatcher will go looking for the corresponding composite menu component, and since they said that they used zero entries, they will naturally never be called, since they disavowed any responsibility for those items.

"Nope, that's not it. We're returning MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, 1). Any other guesses?"

Oh great, the customer is now playing Twenty Questions.

"I'm going to pose a puzzle with almost no clues, and you get to propose solutions, and I'll say whether or not you're right."

I don't know why customers play this game. Maybe they don't realize that asking for help via email is very different from asking for help face-to-face. In a face-to-face conversation, the answer to a question arrives within seconds, whereas in email it can take hours or days. This means that when the answer finally comes back, the person asking the question has to go back and read the conversation history thread to re-establish context.

Or maybe they think they're going to be disclosing Top Secret Information to Microsoft if they share the code that they can't get to work. Trust me, we don't care about your Top Secret Algorithm for Beating the Stock Market With No Money Down. Go ahead and remove those from the code. We just want to see how you are interfacing with the shell. (And besides, you probably should be removing them anyway, since they are irrelevant and are not part of a minimal program that reproduces the problem.)

What would your next guess be?

"Perhaps you're adding your items with the wrong menu item ID." That would explain the problem, because returning MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, 1) means "I successfully added up to one menu item starting at idCmd­First." But if they didn't actually add it at idCmd­First, then when the user selects the item, it won't be in the range they claimed, and therefore the invoke won't get routed to them.

"Your intuition is wrong again. Here's the code we're using."

I can abide by the "Ha ha, you guessed wrong again!" because it at least prodded them into sharing some code. Not much code, mind you, but at least some.

HRESULT SooperSeekrit::QueryContextMenu(
    HMENU hmenu,
    UINT indexMenu,
    UINT idCmdFirst,
    UINT idCmdLast,
    UINT uFlags)
  UINT cItemsAdded = 0;
  if (!(uFlags & CMF_DEFAULTONLY) &&
                     &globalMenuItemInfo)) {

The next thing you should have noticed is that they never actually used the idCmd­First parameter. So how could they claim to be adding the items with the correct menu item ID if they ignore the variable that tells them what the correct menu item ID is?

"Could you tell us more about the global­Menu­Item­Info variable? In particular, what value does it use for the wID member, and how do you make sure that it is equal to idCmd­First? It seems that you are missing some lines of code here:

globalMenuItemInfo.fMask |= MIIM_ID;
globalMenuItemInfo.wID = idCmdFirst;

but perhaps there's something going on that we are missing."

The customer cheerfully replied, "Oops, sorry, didn't notice that. Works great now, thanks!"

I didn't bother to draw their attention to the fact that they lied when they responded to the question "Did you add the menu item with the correct ID?" with "Wrong again! BZZZT!"

The point of today's story is that you, gentle reader, already know how to debug these types of issues. You just have to take what you know and apply it to the situation at hand. If you know how composite context menu dispatch works, then you can come up with failure modes in which the dispatcher fails to match up the menu item with the component.

Exercise: The customer is still not out of the woods yet. What other bug remains in their IContext­Menu::Query­Context­Menu implementation?

Comments (20)
  1. Hi Raymond. I'm going to write a comment that has nothing to do with this post. Sorry about that, but it seems the only way I can contact you.

    One of the applications I maintain (written in C#) need to copy HTML to the clipboard. After reading the HTML Clipboard Format (…/aa767917(VS.85).aspx) I started the implementation, but I knew that it was going to be a painful process (thanks to docmnttion).

    The description:

    Version vv Version number of the clipboard. Starting version is 0.9.

    Starting version is 0.9. Great! I will give it 3.14!

    I began to investigate and I landed on your blog ( Basically the reading is: no one knows what to put in Version but since 0.9 works quite well then why bother?

    Open Internet Explorer, copy some HTML to the clipboard and retrieve the data from the clipboard. What do you see? I see Version: 1.0

    So, what is this rogue version 1.0 of the HTML clipboard format, Raymond?

  2. @RaceProUK:

    Is that something like Bing? I'll give it a try. In the meantime, 0.9? 1.0? 3.14? Is there any difference? What is Version?

    [I think you have a fundamental misunderstanding of what protocol versions are for. -Raymond]
  3. When I first read the question, my first guess was a mismatched item ID, but then I said "nah, that would be too evident", and went into reading the linked articles and making all kinds of crazy guesses. It turns out that my first instinct was right. Corolary: don't discard your first (and simpler) idea too soon, it will make you loss time! :-)

  4. Mordachai says:

    Heh – maybe this blog should be subtitled "Psychic Debugging"

  5. kog999 says:

    "I'm going to pose a puzzle with almost no clues, and you get to propose solutions, and I'll say whether or not you're right."

    As a sys admin I find this happens in my field a lot too. More than once I’ll get a ticket like this "I've deleted a file can you get it back". With no other details. It’s like they are doing it just to annoy me. How about we start with where the file was located or maybe even the name of the file. I mean did you really not think that that information would be needed or useful. Or when I get the “I got an error in Excel” ticket well maybe you could have included what the error said.

  6. Joshua says:

    “I got an error in Excel”

    Maybe because you corrupted your own formulas by inappropriate drag & drop of the cells and can't figure out what you did or your sheet has been producing garbage for the past six months.

  7. Brian_EE says:

    @kog999: "I've deleted a file can you get it back"

    I would have sent them a random file from my temp folder and asked "Is this it?"

  8. rich says:

    it's astonishing that there exists a group of people that pay (presumably a huge amount) to have Microsoft help solve simple programming problems like this for them, when half an hour on google would achieve the same result

  9. @Raymond:

    I know what protocol versions are for, but it seems that my sarcasm has gone unnoticed.

    I'm trying to find good documentation in order to see if I should use 0.9 or 1.0. I would like to know what means 0.9 or 1.0 to the OS. I would like to understand the protocol. I want to understand what I'm developing.

    Ok, let's use 0.9. But why is Internet Explorer using 1.0? After all if Microsoft is using 1.0 then it must have an improvement over the previous version, don't you think?

    Never mind. I will follow the documentation and use 0.9.

    [Seeing as you don't know what the rules for version 1.0 are, it would be inadvisable to claim that you followed them. -Raymond]
  10. I haven't followed them since I don't know them.

    I started reading the documentation. According to it, Version starts from 0.9. After reading that, the first thing it came to my mind was: what if there is a new version? Then I continue reading until the end.

    Once I read the documentation, before implementing my solution, I decided to take a look at how Internet Explorer works. To my surprise I found that Internet Explorer was using 1.0. I said: Great! I'll take a look at the 1.0 documentation. But I didn't find it.

    While I was searching for the 1.0 documentation I found that the WPF Toolkit was using 1.0 (at least that's what it claim in the Description), so I thought: if the 0.9 documentation is available (unlike the 1.0 documentation) then why the WPF Toolkit is using an undocumented version? Is something wrong with 0.9?

    Dude, I was trying to get enough information before implementing a solution in order to avoid releasing buggy software. Internet Explorer + WPF Toolkit generated me this doubt.

  11. Joshua says:

    @Nicolás.Ferreira: WPF Toolkit is known to be broken. It declared itself to be 1.0 and wrote corrupted records due to an encoding bug. The fact that it declared itself to be 1.0 with no documentation to a 1.0 in existence (no prior document cited nor known and they didn't publish one) is enough to say it is broken. However, the fact that they declared 1.0 makes working around their stupid bug possible.

  12. Anonymous Coward says:

    And this is why you shouldn't close comment threads so soon.

    [That the topic was discussed just a few weeks ago appears to be coincidence. Or are you saying that comments should never be closed? -Raymond]
  13. Paul Parks says:

    I was once the network administrator for the private elementary school my daughters attended. My favorite phone calls always began, "My Microsoft crashed."

    Psychic powers, activate!

  14. cheong00 says:

    @Steve Wolf: Or maybe Raymond can add tag "Psychic Debugging" as new tag for this blog.

  15. > comments should never be closed

    Some say so.

    [I tried that once, but when you have over a decade of blog posts, it becomes quite unwieldy because each post becomes a product support thread. "Hey, you wrote a blog post about X in 2004. I'm having a problem with X, maybe you can help me." -Raymond]
  16. Steve Ballmer says:

    @Paul M. Parks: Sorry. That must have been me. Nobody else owns a Microsoft, so it can't have been anyone else.

  17. Joshua says:

    [I tried that once, but when you have over a decade of blog posts, it becomes quite unwieldy because each post becomes a product support thread. "Hey, you wrote a blog post about X in 2004. I'm having a problem with X, maybe you can help me." -Raymond]

    Well that's one solution …

  18. Mark says:


    > why the WPF Toolkit is using an undocumented version?

    You clearly haven't read Raymond's article that you referenced.  Best not to start with sarcasm when you *know* you don't know the full story.

  19. John Doe says:

    A go at the exercise: the global variable globalMenuItemInfo may be problematic, there can be (will be) multiple calls to QueryContextMenu, and each will change the struct. The call to IContextMenu.InvokeCommand may be done at a later time where the menu item's ID is no longer the expected one. Although, this might be a non-issue when there's only 1 menu item (that's not a popup menu; is it?).

    [Recall that context menu verbs can be invoked programmatically, so multithreading is definitely an issue. -Raymond]

Comments are closed.