{"id":4083,"date":"2013-06-14T07:00:00","date_gmt":"2013-06-14T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2013\/06\/14\/a-big-little-program-monitoring-internet-explorer-and-explorer-windows-part-3-tracking-creation-and-destruction\/"},"modified":"2013-06-14T07:00:00","modified_gmt":"2013-06-14T07:00:00","slug":"a-big-little-program-monitoring-internet-explorer-and-explorer-windows-part-3-tracking-creation-and-destruction","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20130614-00\/?p=4083","title":{"rendered":"A big little program: Monitoring Internet Explorer and Explorer windows, part 3: Tracking creation and destruction"},"content":{"rendered":"<p><P>\nLast time, we\n<A HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2013\/06\/13\/10425505.aspx\">\nlistener for window navigations<\/A>.\nToday we&#8217;ll learn about tracking window creation and destruction.\n<\/P>\n<P>\nThe events to listen to are the\n<A HREF=\"http:\/\/msdn.microsoft.com\/library\/cc836565\">\n<CODE>DShell&shy;Windows&shy;Events<\/CODE><\/A>.\nThe\n<CODE>Window&shy;Registered<\/CODE> event fires\nwhen a new window is created, and the\n<CODE>Window&shy;Revoked<\/CODE> event fires\nwhen a window is destroyed.\n<\/P>\n<P>\nThe bad news is that the parameter to those events is a cookie,\nwhich is not useful for much,\nso we just use the events to tell us that it&#8217;s time to\nkick off a new enumeration to see what changed.\nThis will also catch the case where something fell out of sync\nbecause a window closed without unregistering (say,\nbecause the application crashed).\n<\/P>\n<P>\nTake our program from last time and make these changes:\n<\/P>\n<PRE>\n<FONT COLOR=\"blue\">LONG g_lCounter;<\/FONT><\/p>\n<p>struct ItemInfo\n{\n ItemInfo(HWND hwnd, IDispatch *pdisp)\n  : hwnd(hwnd), <FONT COLOR=\"blue\">lCounter(g_lCounter)<\/FONT> { &#8230; }\n &#8230;<\/p>\n<p> HWND hwnd;\n CComPtr&lt;CWebBrowserEventsSink&gt; spSink;\n <FONT COLOR=\"blue\">LONG lCounter;<\/FONT>\n};\n<\/PRE>\n<P>\nThe counter is used to detect stale windows when we re-enumerate.\n<\/P>\n<PRE>\nHRESULT BuildWindowList()\n{\n CComPtr&lt;IUnknown&gt; spunkEnum;\n HRESULT hr = g_spWindows-&gt;_NewEnum(&amp;spunkEnum);\n if (FAILED(hr)) return hr;<\/p>\n<p> <FONT COLOR=\"blue\">++g_lCounter;<\/FONT><\/p>\n<p> CComQIPtr&lt;IEnumVARIANT&gt; spev(spunkEnum);\n for (CComVariant svar;\n      spev-&gt;Next(1, &amp;svar, nullptr) == S_OK;\n      svar.Clear()) {\n  if (svar.vt != VT_DISPATCH) continue;<\/p>\n<p>  HWND hwnd;\n  CComHeapPtr&lt;WCHAR&gt; spszLocation;\n  if (FAILED(GetBrowserInfo(svar.pdispVal,\n             &amp;hwnd, &amp;spszLocation))) continue;<\/p>\n<p>  ItemInfo *pii = <FONT COLOR=\"blue\">GetItemByWindow(hwnd, nullptr);\n  if (pii) { pii-&gt;lCounter = g_lCounter; continue; }\n  pii =<\/FONT> new(std::nothrow) ItemInfo(hwnd, svar.pdispVal);\n  if (!pii) continue;<\/p>\n<p>  LVITEM item;\n  item.mask = LVIF_TEXT | LVIF_PARAM;\n  item.iItem = MAXLONG;\n  item.iSubItem = 0;\n  item.pszText = spszLocation;\n  item.lParam = reinterpret_cast&lt;LPARAM&gt;(pii);\n  int iItem = ListView_InsertItem(g_hwndChild, &amp;item);\n  if (iItem &lt; 0) delete pii;\n }<\/p>\n<p> <FONT COLOR=\"blue\">int iItem = ListView_GetItemCount(g_hwndChild);\n while (&#8211;iItem &gt;= 0) {\n  ItemInfo *pii = GetItemByIndex(iItem);\n  if (pii-&gt;lCounter != g_lCounter) {\n   ListView_DeleteItem(g_hwndChild, iItem);\n  }\n }<\/FONT><\/p>\n<p> return S_OK;\n}<\/FONT>\n<\/PRE>\n<P>\nBuilding the window list is now a two-step process,\nsince what we are really doing is <I>updating<\/I>\nthe window list.\nFirst, we enumerate\nthe contents of the <CODE>IShell&shy;Windows<\/CODE>.\nFor each window, we get its window handle and see if there\nis already an item for that window.\nIf so, then we update the counter for that item.\nIf there is not already an item for that window,\nthen we create one like we did before.\n<\/P>\n<P>\nAfter we&#8217;ve processed all the windows that exist,\nwe go look for the deletion by walking through all\nour items and deleting any whose counter was not updated\nby the previous loop.\n<\/P>\n<P>\nOkay, but so far we haven&#8217;t actually done anything new.\nHere&#8217;s the new stuff:\n<\/P>\n<PRE>\n<FONT COLOR=\"blue\">class CShellWindowsEventsSink :\n    public CDispInterfaceBase&lt;DShellWindowsEvents&gt;\n{\npublic:\n HRESULT SimpleInvoke(\n    DISPID dispid, DISPPARAMS *pdispparams, VARIANT *pvarResult)\n {\n  switch (dispid) {\n  case DISPID_WINDOWREGISTERED:\n  case DISPID_WINDOWREVOKED:\n   BuildWindowList();\n   break;\n  }\n  return S_OK;\n }\n};<\/p>\n<p>CComPtr&lt;CShellWindowsEventsSink&gt; g_spShellSink;<\/FONT>\n<\/PRE>\n<P>\nThis is the object that listens for changes to the window list.\nAnd whether the change is that a window arrived or a window\ndeparted, the response is the same: Refresh the window list.\n<\/P>\n<P>\nAll that&#8217;s left to do is hook up this event sink (and clean it up):\n<\/P>\n<PRE>\nBOOL\nOnCreate(HWND hwnd, LPCREATESTRUCT lpcs)\n{\n g_hwndChild = CreateWindow(WC_LISTVIEW, 0,\n    LVS_LIST | WS_CHILD | WS_VISIBLE |\n    WS_HSCROLL | WS_VSCROLL, 0, 0, 0, 0,\n    hwnd, (HMENU)1, g_hinst, 0);\n g_spWindows.CoCreateInstance(CLSID_ShellWindows);\n BuildWindowList();<\/p>\n<p> <FONT COLOR=\"blue\">g_spShellSink.Attach(new CShellWindowsEventsSink());\n g_spShellSink-&gt;Connect(g_spWindows);<\/FONT><\/p>\n<p> return TRUE;\n}<\/p>\n<p>void OnDestroy(HWND hwnd)\n{\n g_spWindows.Release();\n <FONT COLOR=\"blue\">if (g_spShellSink) {\n  g_spShellSink-&gt;Disconnect();\n  g_spShellSink.Release();\n }<\/FONT>\n PostQuitMessage(0);\n}\n<\/PRE>\n<P>\nWe now have a program that\ndisplays all the\nInternet Explorer and Explorer windows, updates\ntheir locations as you navigate,\nand adds and removes them as new windows are created\nor existing ones are closed.\n<\/P>\n<P>\n<B>Reminder<\/B>: This is a Little Program,\nwhich means that there is little to no error checking,\nand the design may be somewhat suboptimal.\n(For example, I use global variables everywhere\nbecause I&#8217;m lazy.)\nBut it should give you enough of a head start so you can\nwrite a more robust version.\n<\/P>\n<P>\n<B>Exercise<\/B>:\nThere is still a subtle bug in <CODE>Build&shy;Window&shy;List<\/CODE>.\nIdentify it and discuss how you would address it.\n<\/P><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Last time, we listener for window navigations. Today we&#8217;ll learn about tracking window creation and destruction. The events to listen to are the DShell&shy;Windows&shy;Events. The Window&shy;Registered event fires when a new window is created, and the Window&shy;Revoked event fires when a window is destroyed. The bad news is that the parameter to those events is [&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-4083","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Last time, we listener for window navigations. Today we&#8217;ll learn about tracking window creation and destruction. The events to listen to are the DShell&shy;Windows&shy;Events. The Window&shy;Registered event fires when a new window is created, and the Window&shy;Revoked event fires when a window is destroyed. The bad news is that the parameter to those events is [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/4083","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=4083"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/4083\/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=4083"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=4083"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=4083"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}