{"id":109947,"date":"2024-07-01T07:00:00","date_gmt":"2024-07-01T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=109947"},"modified":"2024-06-24T09:11:18","modified_gmt":"2024-06-24T16:11:18","slug":"20240701-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20240701-00\/?p=109947","title":{"rendered":"If I register the same shell extension as both a context menu extension and a drag\/drop extension, how do I know which one the system is using?"},"content":{"rendered":"<p>A customer was developing a shell extension that implemented <code>IContextMenu<\/code>. This extension interface is used for both context menu extensions and drag\/drop extensions, and they registered their extension&#8217;s CLSID in both places. When the user right-clicked on a file, their extension was called to add menu items to the file&#8217;s context menu. And when the user did a right-mouse drag\/drop, their extension was called to add menu items to the drag\/drop context menu.<\/p>\n<p>But they wanted their shell extension to know whether it is being used as a context menu extension or a drag\/drop extension. How can their extension detect which scenario is active? Other combination shell extensions can detect the scenario by seeing which interface&#8217;s methods are called. For example, if <code>IShellPropSheetExt::<wbr \/>AddPages<\/code> is called, then it knows that it&#8217;s being used as a property sheet extension. But both context menu extensions and drag\/drop extensions use the same interface <code>IContextMenu<\/code>, so they can&#8217;t use that to detect the scenario.<\/p>\n<p>My kids have a pretend store called &#8220;Fred Tire&#8221;, a pun on <a title=\"Fred Meyer\" href=\"https:\/\/en.wikipedia.org\/wiki\/Fred_Meyer\"> Fred Meyer<\/a>, a local chain of grocery\/department stores. When you call Fred Tire, you can order automobile tires, or you can order a pizza.\u00b9<\/p>\n<p>This customer basically wrote their own Fred Tire.<\/p>\n<p>When a customer calls the store, you can answer the phone &#8220;Hello, this is Fred Tire. How can I help you?&#8221; If the customer says, &#8220;I&#8217;d like to buy some tires,&#8221; then you take down their tire order. If they say, &#8220;I&#8217;d like to order a pizza,&#8221; then you take down their pizza order. This is the Fred Tire version of using the interface to detect which scenario is being used. Furthermore, after the customer places their tire order, they might say, &#8220;And I&#8217;d also like to order a pizza,&#8221; and you can take their pizza order too. The same order-taker can handle both tires and pizzas. They just follow the customer&#8217;s lead as to which order form to fill out.<\/p>\n<p>This customer, however, wanted to answer the phone in a different way depending on what the customer wants. They want to know whether to say &#8220;Hello, this is Fred Tire, what kind of tire do you need?&#8221; Or whether they should say, &#8220;Hello, this is Fred Tire, what kind of pizza would you like?&#8221; How can they answer the phone correctly?<\/p>\n<p>If you want to answer the phone differently, what you can do is get two telephone numbers. Put one phone number on your tire flyers and another number on your pizza flyers. Both numbers ring the same phone, but you can see which line is ringing. That way, when somebody calls the tire number, you say, &#8220;Hello, this is Fred Tire, what kind of tire do you need?&#8221; And if somebody calls the pizzeria number, you say, &#8220;Hello, this is Fred Tire, what kind of pizza would you like?&#8221;<\/p>\n<p>For shell extensions, this means that you register two different CLSIDs, one for the context menu extension and another for the drag\/drop extension. Creating either of the CLSIDs produces the same object, but also passes the CLSID to the constructor so that the object knows which scenario is active.<\/p>\n<pre>\/\/ sketch\r\nclass MyShellExtension : IContextMenu, \u27e6 other interfaces \u27e7\r\n{\r\n    MyShellExtension(<span style=\"border: solid 1px currentcolor;\">REFCLSID clsid<\/span>) :\r\n        <span style=\"border: solid 1px currentcolor;\">m_clsid(clsid)<\/span> {}\r\n\r\n    CLSID const m_clsid;\r\n\r\n    bool IsCreatedForContextMenu()\r\n        { return m_clsid == CLSID_MyContextMenu; }\r\n    bool IsCreatedForDragDrop()\r\n        { return m_clsid == CLSID_MyDragDrop; }\r\n\r\n    \u27e6 ... other methods ... \u27e7\r\n};\r\n\r\nclass MyShellExtensionFactory : IClassFactory\r\n{\r\n    MyShellExtensionFactory(<span style=\"border: solid 1px currentcolor;\">REFCLSID clsid<\/span>) :\r\n        <span style=\"border: solid 1px currentcolor;\">m_clsid(clsid)<\/span> {}\r\n\r\n    <span style=\"border: solid 1px currentcolor;\">CLSID const m_clsid;<\/span>\r\n\r\n    STDMETHODIMP CreateInstance(IUnknown* outer, REFIID iid, void** ppv)\r\n    {\r\n        *ppv = nullptr;\r\n        if (outer) return CLASS_E_NOAGGREGATION;\r\n        auto instance = new (std::nothrow) MyShellExtension(<span style=\"border: solid 1px currentcolor;\">clsid<\/span>);\r\n        if (!instance) return E_OUTOFMEMORY;\r\n        auto hr = instance-&gt;QueryInterface(iid, ppv);\r\n        factory-&gt;Release();\r\n        return hr;\r\n    }\r\n\r\n    \u27e6 ... other methods ... \u27e7\r\n};\r\n\r\nSTDAPI DllGetClassObject(REFCLSID clsid, REFIID iid, void** ppv)\r\n{\r\n    *ppv = nullptr;\r\n    if (clsid == CLSID_MyContextMenu || clsid == CLSID_MyDragDrop)\r\n    {\r\n        auto factory = new(std::nothrow) MyShellExtensionFactory(<span style=\"border: solid 1px currentcolor;\">clsid<\/span>);\r\n        if (!factory) return E_OUTOFMEMORY;\r\n        auto hr = factory-&gt;QueryInterface(iid, ppv);\r\n        factory-&gt;Release();\r\n        return hr;\r\n    }\r\n    return REGDB_E_CLASSNOTREG;\r\n}\r\n<\/pre>\n<p>You can pass the <code>clsid<\/code> to the factory, who in turn passes it to the <code>MyShellExtension<\/code>, who can then use it to figure out which kind of shell extension it was created for.<\/p>\n<p>If your framework doesn&#8217;t let you pass parameters to factories, you can just create separate factories.<\/p>\n<pre><span style=\"border: solid 1px currentcolor; border-bottom: none;\">enum class ExtensionKind<\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">{                       <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">    ContextMenu,        <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">    DragDrop,           <\/span>\r\n<span style=\"border: solid 1px currentcolor; border-top: none;\">};                      <\/span>\r\n\r\nclass MyShellExtension : IContextMenu, \u27e6 other interfaces \u27e7\r\n{\r\n    MyShellExtension(<span style=\"border: solid 1px currentcolor;\">ExtensionKind kind<\/span>) : <span style=\"border: solid 1px currentcolor;\">m_kind(kind)<\/span> {}\r\n\r\n    <span style=\"border: solid 1px currentcolor; border-bottom: none;\">ExtensionKind const m_kind = kind;                  <\/span>\r\n                                                        \r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">bool IsCreatedForContextMenu()                      <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">    { return m_kind == ExtensionKind::ContextMenu; }<\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">bool IsCreatedForDragDrop()                         <\/span>\r\n    <span style=\"border: solid 1px currentcolor; border-top: none;\">    { return m_clsid == ExtensionKind::DragDrop; }  <\/span>\r\n\r\n    \u27e6 ... other methods ... \u27e7\r\n};\r\n\r\nclass MyContextMenuShellExtension : MyShellExtension\r\n{\r\n    MyContextMenuShellExtension() :\r\n        <span style=\"border: solid 1px currentcolor;\">MyShellExtension(ExtensionKind::ContextMenu)<\/span> {}\r\n};\r\n\r\nclass MyDragDropShellExtension : MyShellExtension\r\n{\r\n    MyDragDropShellExtension() :\r\n        <span style=\"border: solid 1px currentcolor;\">MyShellExtension(ExtensionKind::DragDrop)<\/span> {}\r\n};\r\n\r\nCoCreatableClass(MyContextMenuShellExtension)\r\nCoCreatableClass(MyDragDropShellExtension)\r\n<\/pre>\n<p>If your framework doesn&#8217;t even allow that, then you could templatize the class.<\/p>\n<pre>template&lt;ExtensionKind kind&gt;\r\nclass MyShellExtension : IContextMenu, \u27e6 other interfaces \u27e7\r\n{\r\n    MyShellExtension() = default;\r\n\r\n    <span style=\"border: solid 1px currentcolor; border-bottom: none;\">ExtensionKind const m_kind = kind;                  <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">\u00a0                                                   <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">bool IsCreatedForContextMenu()                      <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">    { return m_kind == ExtensionKind::ContextMenu; }<\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">bool IsCreatedForDragDrop()                         <\/span>\r\n    <span style=\"border: solid 1px currentcolor; border-top: none;\">    { return m_clsid == ExtensionKind::DragDrop; }  <\/span>\r\n\r\n    \u27e6 ... other methods ... \u27e7\r\n};\r\n\r\n\r\n<span style=\"border: solid 1px currentcolor; border-bottom: none;\">using MyContextMenuShellExtension =             <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">    MyShellExtension&lt;ExtensionKind::ContextMenu&gt;<\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">using MyDragDropShellExtension =                <\/span>\r\n<span style=\"border: solid 1px currentcolor; border-top: none;\">    MyShellExtension&lt;ExtensionKind::DragDrop&gt;   <\/span>\r\n<\/pre>\n<p>\u00b9Or (and this is where things get fun) you can order a &#8220;tire pizza&#8221;. I always order the pineapple tire pizza.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Who forced you to register the same shell extension for both?<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-109947","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Who forced you to register the same shell extension for both?<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/109947","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=109947"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/109947\/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=109947"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=109947"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=109947"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}