{"id":22163,"date":"2008-05-28T10:00:00","date_gmt":"2008-05-28T10:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2008\/05\/28\/reading-a-contract-from-the-other-side-shsetinstanceexplorer-and-shgetinstanceexplorer\/"},"modified":"2008-05-28T10:00:00","modified_gmt":"2008-05-28T10:00:00","slug":"reading-a-contract-from-the-other-side-shsetinstanceexplorer-and-shgetinstanceexplorer","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20080528-00\/?p=22163","title":{"rendered":"Reading a contract from the other side: SHSetInstanceExplorer and SHGetInstanceExplorer"},"content":{"rendered":"<p>\nShell extensions that create worker threads need to call the\n<code>SHGetInstanceExplorer<\/code> function\nso that Explorer will not exit while the worker thread is still running.\nWhen your worker thread finishes, you release the <code>IUnknown<\/code>\nthat you obtained to tell the host program,\n&#8220;Okay, I&#8217;m done now, thanks for waiting.&#8221;\n<\/p>\n<p>\nYou can read this contract from the other side.\nInstead of thinking of yourself as the shell extension running\ninside a host program,\nthink of yourself as the host program that has a shell extension\nrunning inside of it.\nConsider\n<a HREF=\"http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2004\/11\/26\/270710.aspx\">\na simple program<\/a>\nthat displays the properties of a file,\nor at least tries to:\n<\/p>\n<pre>\n#include &lt;windows.h&gt;\n#include &lt;shellapi.h&gt;\n#include &lt;tchar.h&gt;\nint __cdecl _tmain(int argc, TCHAR **argv)\n{\n  SHELLEXECUTEINFO sei = { sizeof(sei) };\n  sei.fMask = SEE_MASK_FLAG_DDEWAIT | SEE_MASK_INVOKEIDLIST;\n  sei.nShow = SW_SHOWNORMAL;\n  sei.lpVerb = TEXT(\"properties\");\n  sei.lpFile = TEXT(\"C:\\\\Windows\\\\Explorer.exe\");\n  ShellExecuteEx(&amp;sei);\n  return 0;\n}\n<\/pre>\n<p>\nOh dear!\nWhen you run this program, nothing happens.\nWell, actually, something did happen, but the program exited\ntoo fast for you to see it.\nTo slow things down, add the line\n<\/p>\n<pre>\n  MessageBox(NULL, TEXT(\"Waiting\"), TEXT(\"Title\"), MB_OK);\n<\/pre>\n<p>\nright before the <code>return 0<\/code>.\nRun the program again, and this time the properties dialog\nappears, as well as the message box.\nAha, the problem is that our program is exiting while the property\nsheet is still active.\n(Now delete that call to <code>MessageBox<\/code> before something\nstupid happens.)\n<\/p>\n<p>\nThe question now is how to know when the property sheet is\nfinished so we can exit.\nThat&#8217;s where <code>SHSetInstanceExplorer<\/code> comes in.\nThe name &#8220;Explorer&#8221; in the function name is really a placeholder\nfor &#8220;the host program&#8221;;\nit just happens to be called &#8220;Explorer&#8221; because the function\nwas written from the point of view of the shell extension,\nand the host program is nearly always Explorer.exe.\n<\/p>\n<p>\nIn this case, however, we are the host program, not Explorer.\nThe <code>SHSetInstanceExplorer<\/code> lets you register a\nfree-threaded <code>IUnknown<\/code> that shell extensions can\nobtain by calling <code>SHGetInstanceExplorer<\/code>.\nFollowing COM reference counting conventions,\nthe <code>SHGetInstanceExplorer<\/code>\nperforms an <code>AddRef()<\/code> on the <code>IUnknown<\/code>\nthat it returns;\nthe shell extension&#8217;s worker thread\nperforms the corresponding <code>Release()<\/code>\nwhen it is finished.\n<\/p>\n<p>\nAll that is required of the <code>IUnknown<\/code> that you\npass to <code>SHSetInstanceExplorer<\/code> is that it be\nfree-threaded; in other words, that it support being called\nfrom multiple threads.\nThis means managing the &#8220;process reference count&#8221;\nwith interlocked functions rather than boring <code>++<\/code>\nand <code>--<\/code> operators.\nOf course, in practice, you also need to tell your main program\n&#8220;Okay, all the shell extensions are finished; you can exit now&#8221;\nwhen the reference count drops to zero.\n<\/p>\n<p>\nThere are many ways to accomplish this task.\nHere&#8217;s one that I threw together just now.\nI didn&#8217;t think too hard about this class;\nI&#8217;m not positioning this as the best way of implementing it,\nor even as a particularly good one.\nThe purpose of this article is to show the <i>principle<\/i> behind\nprocess references.\nOnce you understand that,\nyou are free to go ahead and solve the problem your own way.\nBut here&#8217;s <i>a<\/i> way.\n<\/p>\n<pre>\n#include &lt;shlobj.h&gt;\nclass ProcessReference : public IUnknown {\npublic:\n  STDMETHODIMP QueryInterface(REFIID riid, void **ppv)\n  {\n    if (riid == IID_IUnknown) {\n      *ppv = static_cast&lt;IUnknown*&gt;(this);\n      AddRef();\n      return S_OK;\n    }\n   *ppv = NULL; return E_NOINTERFACE;\n  }\n  STDMETHODIMP_(ULONG) AddRef()\n    { return InterlockedIncrement(&amp;m_cRef); }\n  STDMETHODIMP_(ULONG) Release()\n  {\n    LONG lRef = InterlockedDecrement(&amp;m_cRef);\n    if (lRef == 0) PostThreadMessage(m_dwThread, WM_NULL, 0, 0);\n    return lRef;\n  }\n  ProcessReference()\n    : m_cRef(1), m_dwThread(GetCurrentThreadId())\n    { SHSetInstanceExplorer(this); }\n  ~ProcessReference()\n  {\n    SHSetInstanceExplorer(NULL);\n    Release();\n    MSG msg;\n    while (m_cRef &amp;&amp; GetMessage(&amp;msg, NULL, 0, 0)) {\n      TranslateMessage(&amp;msg);\n      DispatchMessage(&amp;msg);\n    }\n  }\nprivate:\n  LONG m_cRef;\n  DWORD m_dwThread;\n};\n<\/pre>\n<p>\nThe idea behind this class is that the main thread (and only\nthe main thread) creates it on the stack.\nWhen constructed, the object registers itself as the\n&#8220;process <code>IUnknown<\/code>&#8220;;\nany shell extensions that call <code>SHGetInstanceExplorer<\/code>\nwill get a pointer to this object.\nWhen the object is destructed, it unregisters itself as the process\nreference (to avoid dangling references) and\nwaits for the reference count\nto drop to zero.\nNotice that the <code>Release<\/code> method posts a dummy thread\nmessage so that the &#8220;waiting for the reference count to go to zero&#8221;\nmessage loop will wake up.\n<\/p>\n<p>\nIn a sense, this is backwards from the way normal COM objects work,\nwhich operate under the principle of &#8220;When the reference count drops to\nzero, the object is destructed.&#8221;\nWe turn it around and code it up as\n&#8220;when the object is destructed, it waits for the reference count\nto drop to zero.&#8221;\nIf you wanted to do it the more traditional COM way,\nyou could have the main thread go into a wait loop and have\nthe object&#8217;s destructor signal the main thread.\nI did it this way because it makes using the class very convenient.\n<\/p>\n<p>\nNow that we have a process reference object, it&#8217;s a simple matter\nof adding it to our main thread:\n<\/p>\n<pre>\nint __cdecl _tmain(int argc, TCHAR **argv)\n{\n  <font COLOR=\"blue\">ProcessReference ref;<\/font>\n  ...\n<\/pre>\n<p>\nWith this modification, the program displays the property sheet\nand patiently waits for the property sheet to be dismissed\nbefore it exits.\n<\/p>\n<p>\n<b>Exercise<\/b>: Explain how the object behaves if we initialized\nthe reference count to zero and deleted the call to <code>Release<\/code>\nin the destructor.\n<\/p>\n<p>\n<b>Bonus reading<\/b>:\n<a HREF=\"http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2004\/05\/21\/136701.aspx\">\nDo you know when your destructors run? Part 2<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Shell extensions that create worker threads need to call the SHGetInstanceExplorer function so that Explorer will not exit while the worker thread is still running. When your worker thread finishes, you release the IUnknown that you obtained to tell the host program, &#8220;Okay, I&#8217;m done now, thanks for waiting.&#8221; You can read this contract from [&hellip;]<\/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-22163","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Shell extensions that create worker threads need to call the SHGetInstanceExplorer function so that Explorer will not exit while the worker thread is still running. When your worker thread finishes, you release the IUnknown that you obtained to tell the host program, &#8220;Okay, I&#8217;m done now, thanks for waiting.&#8221; You can read this contract from [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/22163","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=22163"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/22163\/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=22163"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=22163"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=22163"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}