{"id":37793,"date":"2004-09-22T07:00:00","date_gmt":"2004-09-22T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2004\/09\/22\/how-to-host-an-icontextmenu-part-2-displaying-the-context-menu\/"},"modified":"2004-09-22T07:00:00","modified_gmt":"2004-09-22T07:00:00","slug":"how-to-host-an-icontextmenu-part-2-displaying-the-context-menu","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20040922-00\/?p=37793","title":{"rendered":"How to host an IContextMenu, part 2 &#8211; Displaying the context menu"},"content":{"rendered":"<p><P>\nInstead of\n<A HREF=\"\/oldnewthing\/archive\/2004\/09\/20\/231739.aspx\">\ninvoking a fixed verb<\/a>,\nwe&#8217;ll ask the user to choose\nfrom the context menu and invoke the result.\n<\/P>\n<P>\nMake these changes to the OnContextMenu function:\n<\/P>\n<PRE>\n#define SCRATCH_QCM_FIRST 1\n#define SCRATCH_QCM_LAST  0x7FFF<\/p>\n<p><FONT COLOR=\"blue\">#undef HANDLE_WM_CONTEXTMENU\n#define HANDLE_WM_CONTEXTMENU(hwnd, wParam, lParam, fn) \\\n    ((fn)((hwnd), (HWND)(wParam), <FONT COLOR=\"blue\">GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)<\/FONT>), 0L)<\/FONT><\/p>\n<p><FONT COLOR=\"blue\">\/\/ WARNING! Incomplete and buggy! See discussion<\/FONT>\nvoid OnContextMenu(HWND hwnd, HWND hwndContext, <FONT COLOR=\"blue\">int<\/FONT> xPos, <FONT COLOR=\"blue\">int<\/FONT> yPos)\n{\n<FONT COLOR=\"blue\">  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  }\n<\/FONT>\n  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 (SUCCEEDED(pcm-&gt;QueryContextMenu(hmenu, 0,\n                             SCRATCH_QCM_FIRST, SCRATCH_QCM_LAST,\n                             CMF_NORMAL))) {\n        <FONT COLOR=\"blue\">int iCmd = TrackPopupMenuEx(hmenu, TPM_RETURNCMD,\n                                    pt.x, pt.y, hwnd, NULL);\n        if (iCmd &gt; 0) {\n          CMINVOKECOMMANDINFOEX info = { 0 };\n          info.cbSize = sizeof(info);\n          info.fMask = CMIC_MASK_UNICODE;\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          pcm-&gt;InvokeCommand((LPCMINVOKECOMMANDINFO)&amp;info);\n        }<\/FONT>\n      }\n      DestroyMenu(hmenu);\n    }\n    pcm-&gt;Release();\n  }\n}\n<\/PRE>\n<P>\nThe first change addresses the first issue brought up in the\n<A HREF=\"\/oldnewthing\/archive\/2004\/09\/21\/232369.aspx\">\ndiscussion of the WM_CONTEXTMENU message<\/A>\nand fixes the HANDLE_WM_CONTEXTMENU message.\n<\/P>\n<P>\nThe second change addresses the second issue, and that&#8217;s the\nspecial handling of keyboard-invoked context menus.\nWhen we receive a keyboard-invoked context menu, we move it to\nthe (0, 0) position of our client area.  This keeps the context\nmenu displayed in a vaguely sane position.\n(If we were a container with objects, it would have been better\nto display the context menu over the selected sub-object.)\n<\/P>\n<P>\nThe third change actually does what we&#8217;re talking about:\nDisplaying the context menu to the user, collecting the result,\nand acting on it.\n<\/P>\n<P>\nYou are certainly familiar with\n<A HREF=\"http:\/\/msdn.microsoft.com\/library\/en-us\/winui\/winui\/windowsuserinterface\/resources\/menus\/menureference\/menufunctions\/trackpopupmenuex.asp\">\nthe TrackPopupMenuEx function<\/a>.\nHere we use the TPS_RETURNCMD flag to indicate that the item the\nuser selected should be returned by the function instead of being\nposted as a WM_COMMAND to our window.\n<\/P>\n<P>\nThis highlights the importance of the fact that\nSCRATCH_QCM_FIRST is 1 and not zero.\nIf it were zero, then we wouldn&#8217;t be able to distinguish\nbetween the user selecting item zero and the user cancelling the menu.\n<\/P>\n<P>\nOnce we are confident that the user has selected an item\nfrom the menu, we fill out a\n<A HREF=\"http:\/\/msdn.microsoft.com\/library\/en-us\/shellcc\/platform\/shell\/reference\/structures\/cminvokecommandinfoex.asp\">\nCMINVOKECOMMANDEX structure<\/A>,\nspecifying the user&#8217;s selection in the two verb fields and\nindicating the invocation point via the ptInvoke member.\n<\/P>\n<P>\nNote that when you invoke a command by menu ID, you must\nspecify the <STRONG>offset<\/STRONG> of the menu item\nrelative to the starting point passed to\nIContextMenu::QueryContextMenu.\nThat&#8217;s why we subtracted SCRATCH_QCM_FIRST.\n<\/P>\n<P>\nWhen you run this program, you may notice that some things\ndon&#8217;t quite work.  Most obviously, the Open With and Send To submenus\ndon&#8217;t work, but there are more subtle bugs too.\nWe&#8217;ll address them over the next few days.\n<\/P><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Instead of invoking a fixed verb, we&#8217;ll ask the user to choose from the context menu and invoke the result. Make these changes to the OnContextMenu function: #define SCRATCH_QCM_FIRST 1 #define SCRATCH_QCM_LAST 0x7FFF #undef HANDLE_WM_CONTEXTMENU #define HANDLE_WM_CONTEXTMENU(hwnd, wParam, lParam, fn) \\ ((fn)((hwnd), (HWND)(wParam), GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)), 0L) \/\/ WARNING! Incomplete and buggy! See discussion void OnContextMenu(HWND [&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-37793","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Instead of invoking a fixed verb, we&#8217;ll ask the user to choose from the context menu and invoke the result. Make these changes to the OnContextMenu function: #define SCRATCH_QCM_FIRST 1 #define SCRATCH_QCM_LAST 0x7FFF #undef HANDLE_WM_CONTEXTMENU #define HANDLE_WM_CONTEXTMENU(hwnd, wParam, lParam, fn) \\ ((fn)((hwnd), (HWND)(wParam), GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)), 0L) \/\/ WARNING! Incomplete and buggy! See discussion void OnContextMenu(HWND [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/37793","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=37793"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/37793\/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=37793"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=37793"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=37793"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}