{"id":109342,"date":"2024-01-31T07:00:00","date_gmt":"2024-01-31T15:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=109342"},"modified":"2024-01-31T05:54:21","modified_gmt":"2024-01-31T13:54:21","slug":"20240131-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20240131-00\/?p=109342","title":{"rendered":"How can I add an environment variable to a process launched via <CODE>Shell&shy;Execute&shy;Ex<\/CODE> or <CODE>IContext&shy;Menu<\/CODE>?"},"content":{"rendered":"<p>The <code>Shell\u00adExecute\u00adEx<\/code> function and <code>IContext\u00adMenu<\/code> interface provide the caller a number of places to customize how the execution occurs by allowing the call to pass a &#8220;site&#8221; which <code>Shell\u00adExecute\u00adEx<\/code>\/<wbr \/><code>IContext\u00adMenu<\/code> will access at various points in the execution process.<\/p>\n<p>Today we&#8217;ll demonstrate this technique by adding environment variables to processes launched via <code>Shell\u00adExecute\u00adEx<\/code> and <code>IContext\u00adMenu<\/code>.<\/p>\n<p>The <code>ICreating\u00adProcess<\/code> interface is used by <code>Shell\u00adExecute\u00adEx<\/code>. and <code>IContext\u00adMenu<\/code> to allow the caller to customize how processes are created. The extension point is obtained by querying the site for <code>SID_<wbr \/>Execute\u00adCreating\u00adProcess<\/code> and requesting an <code>ICreating\u00adProcess<\/code>. If one is produced, then the system calls the <code>OnCreating<\/code> method with an object that allows the creation to be customized.<\/p>\n<p>Today&#8217;s C++ COM library is (rolls dice) C++\/WinRT.<\/p>\n<pre>struct AddEnvironmentVariableSite :\r\n    winrt::implements&lt;AddEnvironmentVariableSite,\r\n        ::IServiceProvider,\r\n        ::ICreatingProcess&gt;\r\n{\r\n    IFACEMETHOD(QueryService)\r\n        (REFGUID service, REFIID riid, void** ppv)\r\n    {\r\n        if (service == SID_ExecuteCreatingProcess)\r\n        {\r\n            return this-&gt;QueryInterface(riid, ppv);\r\n        }\r\n        else\r\n        {\r\n            *ppv = nullptr;\r\n            return E_NOTIMPL;\r\n        }\r\n    }\r\n\r\n    IFACEMETHOD(OnCreating)(ICreateProcessInputs* inputs)\r\n    {\r\n        return inputs-&gt;SetEnvironmentVariable(\r\n            L\"EXTRAVARIABLE\", L\"Bonus\");\r\n    }\r\n};\r\n<\/pre>\n<p>This site responds to <code>SID_<wbr \/>Execute\u00adCreating\u00adProcess<\/code> by returning itself, and it implements <code>ICreating\u00adProcess<\/code> by having its <code>OnCreating<\/code> method set an environment variable called <code>EXTRAVARIABLE<\/code> with a value of <code>Bonus<\/code>. Since that is the only thing we do, we can just return the result of <code>Set\u00adEnvironment\u00adVariable()<\/code> as our own return value. If you intend to add multiple environment variables, you would check the return value of each call to <code>Set\u00adEnvironment\u00adVariable()<\/code>.<\/p>\n<p>In general, your custom site would be part of a so-called &#8220;site chain&#8221; and forward any unhandled services to your own site, so that an outer site could respond to it.<\/p>\n<p>Here&#8217;s an example of how to use this special environment variable site in conjunction with <code>Shell\u00adExecute\u00adEx<\/code>:<\/p>\n<pre>BOOL Sample()\r\n{\r\n    SHELLEXECUTEINFO sei{ sizeof(sei) };\r\n    sei.lpFile = LR\"(C:\\Windows\\system32\\charmap.exe)\";\r\n    sei.nShow = SW_SHOWNORMAL;\r\n    <span style=\"border: solid 1px currentcolor; border-bottom: none;\">auto site = winrt::make_self&lt;AddEnvironmentVariableSite&gt;();<\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">sei.hInstApp = reinterpret_cast&lt;HINSTANCE&gt;(site.get());    <\/span>\r\n    <span style=\"border: solid 1px currentcolor; border-top: none;\">sei.fMask = SEE_MASK_FLAG_HINST_IS_SITE;                   <\/span>\r\n    return ShellExecuteEx(&amp;sei);\r\n}\r\n<\/pre>\n<p>To pass a site to <code>Shell\u00adExecute\u00adEx<\/code>, we put it in the <code>hInstApp<\/code> member and set the <code>SEE_<wbr \/>MASK_<wbr \/>FLAG_<wbr \/>HINST_<wbr \/>IS_<wbr \/>SITE<\/code> flag to say &#8220;If you look at the <code>hInstApp<\/code>, you&#8217;ll find a site!&#8221;\u00b9<\/p>\n<p>For context menus, we explicitly set our custom site as the context menu&#8217;s site. Building on <a title=\"How to host an IContextMenu, part 1 - Initial foray\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20040920-00\/?p=37823\"> our original sample for hosting an <code>IContext\u00adMenu<\/code><\/a>:<\/p>\n<pre>void OnContextMenu(HWND hwnd, HWND hwndContext, UINT xPos, UINT yPos)\r\n{\r\n  IContextMenu *pcm;\r\n  if (SUCCEEDED(GetUIObjectOfFile(hwnd, L\"C:\\\\Windows\\\\clock.avi\",\r\n                   IID_IContextMenu, (void**)&amp;pcm))) {\r\n    HMENU hmenu = CreatePopupMenu();\r\n    if (hmenu) {\r\n      if (SUCCEEDED(pcm-&gt;QueryContextMenu(hmenu, 0,\r\n                             SCRATCH_QCM_FIRST, SCRATCH_QCM_LAST,\r\n                             CMF_NORMAL))) {\r\n        CMINVOKECOMMANDINFO info = { 0 };\r\n        info.cbSize = sizeof(info);\r\n        info.hwnd = hwnd;\r\n        info.lpVerb = \"play\";\r\n        <span style=\"border: solid 1px currentcolor; border-bottom: none;\">auto site = winrt::make_self&lt;AddEnvironmentVariableSite&gt;();<\/span>\r\n        <span style=\"border: solid 1px currentcolor; border-top: none;\">IUnknown_SetSite(pcm, site.get());                         <\/span>\r\n        pcm-&gt;InvokeCommand(&amp;info);\r\n        <span style=\"border: solid 1px currentcolor;\">IUnknown_SetSite(pcm, nullptr);                            <\/span>\r\n      }\r\n      DestroyMenu(hmenu);\r\n    }\r\n    pcm-&gt;Release();\r\n  }\r\n}\r\n<\/pre>\n<p>(I&#8217;m ignoring the fact that <code>winrt::<wbr \/>make_self<\/code> can throw an exception which results in a memory leak in the sample code above.)<\/p>\n<p>\u00b9 This is admitted a rather ugly way to pass a site, but the ability to add a site was retrofitted onto the existing structure, so we had to hide it in an other-wise unused input member.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hooking into the way the Windows shell launches processes.<\/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-109342","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Hooking into the way the Windows shell launches processes.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/109342","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=109342"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/109342\/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=109342"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=109342"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=109342"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}