{"id":37673,"date":"2004-10-04T07:01:00","date_gmt":"2004-10-04T07:01:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2004\/10\/04\/how-to-host-an-icontextmenu-part-9-adding-custom-commands\/"},"modified":"2004-10-04T07:01:00","modified_gmt":"2004-10-04T07:01:00","slug":"how-to-host-an-icontextmenu-part-9-adding-custom-commands","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20041004-00\/?p=37673","title":{"rendered":"How to host an IContextMenu, part 9 &#8211; Adding custom commands"},"content":{"rendered":"<p><P>\nThe indexMenu, idCmdFirst and idCmdLast parameters to\n<A HREF=\"http:\/\/msdn.microsoft.com\/library\/en-us\/shellcc\/platform\/shell\/reference\/ifaces\/icontextmenu\/QueryContextMenu.asp\">\nthe IContextMenu::QueryContextMenu method<\/A>\nallow you, the host, to control where in the context menu\nthe IContextMenu will insert its commands.\nTo illustrate this, let&#8217;s put two bonus commands on our\ncontext menu, with the boring names &#8220;Top&#8221; and &#8220;Bottom&#8221;.\n<\/P>\n<P>\nWe need to reserve some space in our menu identifiers, so\nlet&#8217;s carve some space out for our private commands:\n<\/P>\n<PRE>\n#define SCRATCH_QCM_FIRST 1\n<FONT COLOR=\"blue\">#define SCRATCH_QCM_LAST  0x6FFF\n#define IDM_TOP           0x7000\n#define IDM_BOTTOM        0x7001<\/FONT>\n<\/PRE>\n<P>\nWe reserved 0x1000 commands for ourselves, allowing the\nIContextMenu to play with commands 1 through 0x6FFF.\n(We could have carved our space out of the low end, too,\nby increasing SCRATCH_QCM_FIRST instead of decreasing\nSCRATCH_QCM_LAST.)\n<\/P>\n<P>\nGo back to\n<A HREF=\"\/oldnewthing\/archive\/2004\/09\/28\/235242.aspx\">\nthe program we had in part&nbsp;6<\/A>\nand make these changes:\n<\/P>\n<PRE>\nvoid OnContextMenu(HWND hwnd, HWND hwndContext, int xPos, int yPos)\n{\n  POINT pt = { xPos, yPos };\n  if (pt.x == -1 &amp;&amp; pt.y == -1) {\n    pt.x = pt.y = 0;\n    ClientToScreen(hwnd, &amp;pt);\n  }<\/p>\n<p>  IContextMenu *pcm;\n  if (SUCCEEDED(GetUIObjectOfFile(hwnd, L&#8221;C:\\\\Windows\\\\clock.avi&#8221;,\n                   IID_IContextMenu, (void**)&amp;pcm))) {\n    HMENU hmenu = CreatePopupMenu();\n    if (hmenu) {\n      if (<FONT COLOR=\"blue\">InsertMenu(hmenu, 0, MF_BYPOSITION,\n                     IDM_TOP, TEXT(&#8220;Top&#8221;)) &amp;&amp;\n          InsertMenu(hmenu, 1, MF_BYPOSITION,\n                     IDM_BOTTOM, TEXT(&#8220;Bottom&#8221;)) &amp;&amp;<\/FONT>\n          SUCCEEDED(pcm-&gt;QueryContextMenu(hmenu, <FONT COLOR=\"blue\">1<\/FONT>,\n                             SCRATCH_QCM_FIRST, SCRATCH_QCM_LAST,\n                             CMF_NORMAL))) {\n        pcm-&gt;QueryInterface(IID_IContextMenu2, (void**)&amp;g_pcm2);\n        pcm-&gt;QueryInterface(IID_IContextMenu3, (void**)&amp;g_pcm3);\n        int iCmd = TrackPopupMenuEx(hmenu, TPM_RETURNCMD,\n                                    pt.x, pt.y, hwnd, NULL);\n        if (g_pcm2) {\n          g_pcm2-&gt;Release();\n          g_pcm2 = NULL;\n        }\n        if (g_pcm3) {\n          g_pcm3-&gt;Release();\n          g_pcm3 = NULL;\n        }\n        <FONT COLOR=\"blue\">if (iCmd == IDM_TOP) {\n          MessageBox(hwnd, TEXT(&#8220;Top&#8221;), TEXT(&#8220;Custom&#8221;), MB_OK);\n        } else if (iCmd == IDM_BOTTOM) {\n          MessageBox(hwnd, TEXT(&#8220;Bottom&#8221;), TEXT(&#8220;Custom&#8221;), MB_OK);\n        } else<\/FONT> if (iCmd &gt; 0) {\n          CMINVOKECOMMANDINFOEX info = { 0 };\n          info.cbSize = sizeof(info);\n          info.fMask = CMIC_MASK_UNICODE | CMIC_MASK_PTINVOKE;\n          if (GetKeyState(VK_CONTROL) &lt; 0) {\n            info.fMask |= CMIC_MASK_CONTROL_DOWN;\n          }\n          if (GetKeyState(VK_SHIFT) &lt; 0) {\n            info.fMask |= CMIC_MASK_SHIFT_DOWN;\n          }\n          info.hwnd = hwnd;\n          info.lpVerb  = MAKEINTRESOURCEA(iCmd &#8211; SCRATCH_QCM_FIRST);\n          info.lpVerbW = MAKEINTRESOURCEW(iCmd &#8211; SCRATCH_QCM_FIRST);\n          info.nShow = SW_SHOWNORMAL;\n          info.ptInvoke = pt;\n          pcm-&gt;InvokeCommand((LPCMINVOKECOMMANDINFO)&amp;info);\n        }\n      }\n      DestroyMenu(hmenu);\n    }\n    pcm-&gt;Release();\n  }\n}\n<\/PRE>\n<P>\n[Corrected insertion location for &#8220;Bottom&#8221; 9:42am.]\n<\/P>\n<P>\nBefore calling IContextMenu::QueryContextMenu, we added our own\ncustom commands (with menu identifiers outside the range we offer\nto IContextMenu::QueryContextMenu so they won&#8217;t conflict), and\nthen call IContextMenu::QueryContextMenu passing the new reduced\nrange as well as specifying that the insertion position is 1 instead\nof 0.\n<\/P>\n<P>\nWhen we pass the context menu to to IContextMenu::QueryContextMenu,\nthe menu looks like this:\n<\/P>\n<TABLE STYLE=\"border: gray solid .75pt\">\n<TR><TD>Top<\/TD><\/TR>\n<TR><TD>Bottom<\/TD><\/TR>\n<\/TABLE>\n<P>\nBy passing 1 as the insertion point, we are telling the context menu\nhandler that it should insert its commands at position 1\n(pushing out what is currently at positions 1 and onwards).\n<\/P>\n<TABLE STYLE=\"border: gray solid .75pt\">\n<TR><TD>Top<\/TD><\/TR>\n<TR><TD STYLE=\"border: gray solid .75pt\"><BR>&#8230; new stuff &#8230;<BR>&nbsp;<\/TD><\/TR>\n<TR><TD>Bottom<\/TD><\/TR>\n<\/TABLE>\n<P>\nAfter displaying this enhanced context menu, we check which\ncommand the user picked, whether it&#8217;s one of ours (which we handle\ndirectly) or one from the inserted portion of the context menu\n(which we dispatch to the handler).\n<\/P><\/p>\n","protected":false},"excerpt":{"rendered":"<p>The indexMenu, idCmdFirst and idCmdLast parameters to the IContextMenu::QueryContextMenu method allow you, the host, to control where in the context menu the IContextMenu will insert its commands. To illustrate this, let&#8217;s put two bonus commands on our context menu, with the boring names &#8220;Top&#8221; and &#8220;Bottom&#8221;. We need to reserve some space in our menu [&hellip;]<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-37673","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>The indexMenu, idCmdFirst and idCmdLast parameters to the IContextMenu::QueryContextMenu method allow you, the host, to control where in the context menu the IContextMenu will insert its commands. To illustrate this, let&#8217;s put two bonus commands on our context menu, with the boring names &#8220;Top&#8221; and &#8220;Bottom&#8221;. We need to reserve some space in our menu [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/37673","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=37673"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/37673\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=37673"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=37673"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=37673"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}