{"id":104656,"date":"2021-01-04T07:00:00","date_gmt":"2021-01-04T15:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=104656"},"modified":"2021-01-04T06:34:14","modified_gmt":"2021-01-04T14:34:14","slug":"20210104-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210104-00\/?p=104656","title":{"rendered":"How can I write a program that monitors another window for a change in size or position?"},"content":{"rendered":"<p>A few days ago, a reader bemoaned, &#8220;<a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20201228-00\/?p=104610#comment-137587\">There&#8217;s no way to update the position\/aspect ratio of live window thumbnails unless you poll<\/a>.&#8221;<\/p>\n<p>Today&#8217;s Little Program monitors another window for a change in its size and position, without polling. It&#8217;s basically another variation on the basic &#8220;window monitoring&#8221; pattern. This time, instead of <a title=\"How can I write a program that monitors another window for a title change?\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20180601-00\/?p=98895\"> monitoring the title<\/a>, we monitor the location (which is the combination of size, position, and shape).<\/p>\n<pre>#define UNICODE\r\n#define <a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20040212-00\/?p=40643\">_UNICODE<\/a>\r\n#define STRICT\r\n#include &lt;windows.h&gt;\r\n#include &lt;stdio.h&gt;\r\n\r\nHWND g_hwndMonitor;\r\n\r\nvoid CALLBACK WinEventProc(\r\n  HWINEVENTHOOK hook,\r\n  DWORD event,\r\n  HWND hwnd,\r\n  LONG idObject,\r\n  LONG idChild,\r\n  DWORD idEventThread,\r\n  DWORD time)\r\n{\r\n  if (hwnd == g_hwndMonitor &amp;&amp;\r\n      idObject == OBJID_WINDOW &amp;&amp;\r\n      idChild == CHILDID_SELF &amp;&amp;\r\n      event == EVENT_OBJECT_LOCATIONCHANGE) {\r\n    RECT rc;\r\n    if (GetWindowRect(g_hwndMonitor, &amp;rc)) {\r\n        printf(\"window rect is (%d,%d)-(%d,%d)\\n\",\r\n            rc.left, rc.top, rc.right, rc.bottom);\r\n    }\r\n  }\r\n}\r\n\r\nint __cdecl main(int, char**)\r\n{\r\n  g_hwndMonitor = FindWindow(L\"Awesome Program\", nullptr);\r\n  DWORD processId;\r\n  DWORD threadId = GetWindowThreadProcessId(g_hwndMonitor, &amp;processId);\r\n  HWINEVENTHOOK hook = SetWinEventHook(\r\n    EVENT_OBJECT_LOCATIONCHANGE,\r\n    EVENT_OBJECT_LOCATIONCHANGE,\r\n    nullptr,\r\n    WinEventProc,\r\n    processId,\r\n    threadId,\r\n    WINEVENT_OUTOFCONTEXT);\r\n  MessageBox(nullptr, L\"Press OK when bored\", L\"Title\", MB_OK);\r\n\r\n  UnhookWinEvent(hook);\r\n  return 0;\r\n}\r\n<\/pre>\n<p>The program starts by identifying the window it wants to monitor. This is your application&#8217;s business logic, so I&#8217;ll just fake it with a <code>Find<\/code><code>Window<\/code>.<\/p>\n<p>We get the thread and process ID for the window and use it to register a thread-specific accessibility event hook, filtered to location changes.<\/p>\n<p>In the event callback, we see if the notification is for the window we are monitoring. If so, we get the window bounds and print it. The attempt to get the window bounds might fail if the window has been destroyed, so watch out for that.\u00b9<\/p>\n<p>\u00b9 One way to track object destruction is with, yup, another accessibility hook, this time watching for <code>EVENT_<wbr \/>OBJECT_<wbr \/>DESTROY<\/code>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Accessibility hooks come to the rescue once again.<\/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-104656","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Accessibility hooks come to the rescue once again.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/104656","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=104656"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/104656\/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=104656"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=104656"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=104656"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}