{"id":37823,"date":"2004-09-20T07:00:00","date_gmt":"2004-09-20T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2004\/09\/20\/how-to-host-an-icontextmenu-part-1-initial-foray\/"},"modified":"2004-09-20T07:00:00","modified_gmt":"2004-09-20T07:00:00","slug":"how-to-host-an-icontextmenu-part-1-initial-foray","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20040920-00\/?p=37823","title":{"rendered":"How to host an IContextMenu, part 1 &#8211; Initial foray"},"content":{"rendered":"<p><P>\nMost documentation describes how to plug into the shell\ncontext menu structure and be a context menu provider.  If you\n<A HREF=\"\/oldnewthing\/archive\/2003\/12\/26\/45979.aspx\">\nread the documentation from the other side<\/A>,\nthen you also see how to <STRONG>host<\/STRONG> the context menu.\n(This is the first of an eleven-part series with three digressions.\nYes, eleven parts&mdash;sorry for all you folks who are in it just\nfor the history articles. I&#8217;ll try to toss in an occasional\namusing diversion.)\n<\/P>\n<P>\nThe usage pattern for an IContextMenu is as follows:\n<\/P>\n<UL>\n<LI>Creation.\n<LI>\n<A HREF=\"http:\/\/msdn.microsoft.com\/library\/en-us\/shellcc\/platform\/shell\/reference\/ifaces\/icontextmenu\/QueryContextMenu.asp\">\nIContextMenu::QueryContextMenu<\/A>.\nThis initializes the context menu.\nDuring this call,\nthe context menu decides which items appear in it,\nbased on the flags you pass.\n<LI>\nDisplay the menu or otherwise select a command to execute,\nusing\n<A HREF=\"http:\/\/msdn.microsoft.com\/library\/en-us\/shellcc\/platform\/shell\/reference\/ifaces\/icontextmenu\/GetCommandString.asp\">\nIContextMenu::GetCommandString<\/A>,\n<A HREF=\"http:\/\/msdn.microsoft.com\/library\/en-us\/shellcc\/platform\/shell\/reference\/ifaces\/icontextmenu2\/handlemenumsg.asp\">\nIContextMenu2::HandleMenuMsg<\/A> and\n<A HREF=\"http:\/\/msdn.microsoft.com\/library\/en-us\/shellcc\/platform\/shell\/reference\/ifaces\/icontextmenu3\/handlemenumsg2.asp\">\nIContextMenu3::HandleMenuMsg2<\/A> to faciliate the user interaction.\n<LI>\n<A HREF=\"http:\/\/msdn.microsoft.com\/library\/en-us\/shellcc\/platform\/shell\/reference\/ifaces\/icontextmenu\/InvokeCommand.asp\">\nIContextMenu::InvokeCommand<\/A>.\nThis executes the command.\n<\/UL>\n<P>\nThe details of this are explained in\n<A HREF=\"http:\/\/msdn.microsoft.com\/library\/en-us\/shellcc\/platform\/shell\/programmersguide\/shell_int\/shell_int_extending\/extensionhandlers\/contextmenuhandlers.asp\">\nCreating Context MenuHandlers<\/A> from the point of view of\nthe IContextMenu implementor.\n<\/P>\n<BLOCKQUOTE CLASS=\"q\">\nThe Shell first calls IContextMenu::QueryContextMenu.\nIt passes in an HMENU handle that the method\ncan use to add items to the context menu.\nIf the user selects one of the commands,\nIContextMenu::GetCommandString\nis called to retrieve the Help string\nthat will be displayed on the Microsoft Windows Explorer status bar.\nIf the user clicks one of the handler&#8217;s items,\nthe Shell calls IContextMenu::InvokeCommand.\nThe handler can then execute the appropriate command.\n<\/BLOCKQUOTE>\n<P>\n<A HREF=\"\/oldnewthing\/archive\/2003\/12\/26\/45979.aspx\">\nRead it from the other side<\/A> to see what it says you need to\ndo as the IContextMenu host:\n<\/P>\n<BLOCKQUOTE CLASS=\"m\">\nThe <I>IContextMenu host<\/I> first calls IContextMenu::QueryContextMenu.\nIt passes in an HMENU handle that the method\ncan use to add items to the context menu.\nIf the user selects one of the commands,\nIContextMenu::GetCommandString\nis called to retrieve the Help string\nthat will be displayed on <I>the host&#8217;s<\/I> status bar.\nIf the user clicks one of the handler&#8217;s items,\nthe <I>IContextMenu host<\/I> calls IContextMenu::InvokeCommand.\nThe handler can then execute the appropriate command.\n<\/BLOCKQUOTE>\n<P>\nExploring the consequences of this new interpretation of the\ncontext menu documentation will be our focus for the next few weeks.\n<\/P>\n<P>\nOkay, let&#8217;s get started.\nWe begin, as always, with\n<A HREF=\"\/oldnewthing\/archive\/2003\/07\/23\/54576.aspx\">\nour scratch program<\/A>.\nI&#8217;m going to assume you&#8217;re already familiar with the shell namespace\nand pidls so I can focus on the context menu part of the issue.\n<\/P>\n<PRE>\n<FONT COLOR=\"blue\">#include &lt;shlobj.h&gt;<\/p>\n<p>HRESULT GetUIObjectOfFile(HWND hwnd, LPCWSTR pszPath, REFIID riid, void **ppv)\n{\n  *ppv = NULL;\n  HRESULT hr;\n  LPITEMIDLIST pidl;\n  SFGAOF sfgao;\n  if (SUCCEEDED(hr = SHParseDisplayName(pszPath, NULL, &amp;pidl, 0, &amp;sfgao))) {\n    IShellFolder *psf;\n    LPCITEMIDLIST pidlChild;\n    if (SUCCEEDED(hr = SHBindToParent(pidl, IID_IShellFolder,\n                                      (void**)&amp;psf, &amp;pidlChild))) {\n      hr = psf-&gt;GetUIObjectOf(hwnd, 1, &amp;pidlChild, riid, NULL, ppv);\n      psf-&gt;Release();\n    }\n    CoTaskMemFree(pidl);\n  }\n  return hr;\n}<\/FONT>\n<\/PRE>\n<P>\nThis simple function takes a path and gets a shell UI object from it.\nWe convert the path to a pidl with\n<A HREF=\"http:\/\/msdn.microsoft.com\/library\/en-us\/shellcc\/platform\/shell\/reference\/functions\/shparsedisplayname.asp\">\nSHParseDisplayName<\/A>,\nthen bind to the pidl&#8217;s parent with\n<A HREF=\"http:\/\/msdn.microsoft.com\/library\/en-us\/shellcc\/platform\/shell\/reference\/functions\/shbindtoparent.asp\">\nSHBindToParent<\/A>, then ask the parent\nfor the UI object of the child with\n<A HREF=\"http:\/\/msdn.microsoft.com\/library\/en-us\/shellcc\/platform\/shell\/reference\/ifaces\/ishellfolder\/getuiobjectof.asp\">\nIShellFolder::GetUIObjectOf<\/A>.\nI&#8217;m assuming you&#8217;ve had enough experience with the namespace that\nthis is ho-hum.\n<\/P>\n<P>\n(The helper functions SHParseDisplayName and SHBindToParent don&#8217;t\ndo anything you couldn&#8217;t have done yourself.  They just save you\nsome typing.  Once you start using the shell namespace\nfor any nontrivial amount of time, you build up a library of little\nfunctions like these.)\n<\/P>\n<P>\nFor our first pass, all we&#8217;re going to do is invoke the &#8220;Play&#8221; verb\non the file when the user right-clicks.  (Why right-click?\nBecause a future version of this program will display a context menu.)\n<\/P>\n<PRE>\n<FONT COLOR=\"blue\">#define SCRATCH_QCM_FIRST 1\n#define SCRATCH_QCM_LAST  0x7FFF<\/p>\n<p>void OnContextMenu(HWND hwnd, HWND hwndContext, UINT xPos, UINT yPos)\n{\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        CMINVOKECOMMANDINFO info = { 0 };\n        info.cbSize = sizeof(info);\n        info.hwnd = hwnd;\n        info.lpVerb = &#8220;play&#8221;;\n        pcm-&gt;InvokeCommand(&amp;info);\n      }\n      DestroyMenu(hmenu);\n    }\n    pcm-&gt;Release();\n  }\n}<\/p>\n<p>    HANDLE_MSG(hwnd, WM_CONTEXTMENU, OnContextMenu);<\/FONT>\n<\/PRE>\n<P>\nAs noted in the checklist above, first we create the IContextMenu,\nthen initialize it by calling IContextMenu::QueryContextMenu.\nNotice that even though we don&#8217;t intend to display the menu,\nwe still have to create a popup menu because\nIContextMenu::QueryContextMenu requires on.\nWe don&#8217;t actually display the resulting menu, however;\ninstead of asking the user to pick an item from the menu,\nwe make the choice for the user and select &#8220;Play&#8221;,\nfilling in\n<A HREF=\"http:\/\/msdn.microsoft.com\/library\/en-us\/shellcc\/platform\/shell\/reference\/structures\/cminvokecommandinfo.asp\">\nthe CMINVOKECOMMANDINFO structure<\/A> and\ninvoking it.\n<\/P>\n<P>\nBut how did we know that the correct verb was &#8220;Play&#8221;?\nIn this case, we knew because we hard-coded the file\nto &#8220;clock.avi&#8221; and we knew that AVI files have a &#8220;Play&#8221; verb.\nBut of course that doesn&#8217;t work in general.\nBefore getting to invoking the default verb, let&#8217;s\nfirst take the easier step of asking the user what\nverb to invoke.  That exercise will actually distract us\nfor a while, but we&#8217;ll come back to the issue of the\ndefault verb afterwards.\n<\/P>\n<P>\nIf the code above is all you really wanted (invoking a fixed\nverb on a file), then you didn&#8217;t need to go through all the\ncontext menu stuff.\nThe code above is equivalent to calling\n<A HREF=\"http:\/\/msdn.microsoft.com\/library\/en-us\/shellcc\/platform\/shell\/reference\/functions\/shellexecuteex.asp\">\nthe ShellExecuteEx function<\/A>,\npassing the SEE_MASK_INVOKEIDLIST flag to indicate that you\nwant the invoke to go through the IContextMenu.\n<\/P>\n<P>\n[Typo fixed 25 September.]\n<\/P><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Most documentation describes how to plug into the shell context menu structure and be a context menu provider. If you read the documentation from the other side, then you also see how to host the context menu. (This is the first of an eleven-part series with three digressions. Yes, eleven parts&mdash;sorry for all you folks [&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-37823","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Most documentation describes how to plug into the shell context menu structure and be a context menu provider. If you read the documentation from the other side, then you also see how to host the context menu. (This is the first of an eleven-part series with three digressions. Yes, eleven parts&mdash;sorry for all you folks [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/37823","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=37823"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/37823\/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=37823"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=37823"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=37823"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}