{"id":2503,"date":"2013-12-02T07:00:00","date_gmt":"2013-12-02T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2013\/12\/02\/logging-the-foreground-process-as-it-changes\/"},"modified":"2013-12-02T07:00:00","modified_gmt":"2013-12-02T07:00:00","slug":"logging-the-foreground-process-as-it-changes","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20131202-00\/?p=2503","title":{"rendered":"Logging the foreground process as it changes"},"content":{"rendered":"<p>\nToday&#8217;s Little Program simply logs all changes to the foreground\nwindow by recording the path to the application the user switched to.\nYou might use this as part of a usability study to monitor what\napplications users spend most of their time in.\n<\/p>\n<p>\nMost of this code is just taking things we already know and snapping\nthem together.\n<\/p>\n<ol>\n<li>\n    <a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2013\/03\/25\/10404940.aspx\">\n    Using accessibility to monitor events<\/a>, specifically\n    to\n    <!-- backref: Playing a sound every time the foreground window changes -->\n    monitor foreground changes<\/a>.<\/p>\n<li><code>Get&shy;Window&shy;Thread&shy;Process&shy;Id<\/code>\n    to get the process ID from a window.<\/p>\n<li><code>Open&shy;Process<\/code> to get a handle to a process\n    given the process ID.<\/p>\n<li><code>Query&shy;Full&shy;Process&shy;ImageName<\/code> to\n    get the path to the application from the handle.\n    (For Windows XP, you can use\n    <code>Get&shy;Process&shy;Image&shy;File&shy;Name<\/code>.)\n<\/ol>\n<p>\nTake our\n<a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2003\/07\/23\/54576.aspx\">\nscratch program<\/a>\nand make these changes:\n<\/p>\n<pre>\nBOOL QueryWindowFullProcessImageName(\n    HWND hwnd,\n    DWORD dwFlags,\n    PTSTR lpExeName,\n    DWORD dwSize)\n{\n DWORD pid = 0;\n BOOL fRc = FALSE;\n if (GetWindowThreadProcessId(hwnd, &amp;pid)) {\n  HANDLE hProcess = OpenProcess(\n          PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);\n  if (hProcess) {\n   fRc = QueryFullProcessImageName(\n          hProcess, dwFlags, lpExeName, &amp;dwSize);\n   CloseHandle(hProcess);\n  }\n }\n return fRc;\n}\n<\/pre>\n<p>\nThe <code>Query&shy;Window&shy;Full&shy;Process&shy;Image&shy;Name<\/code>\nfunction is the meat of the program,\nperforming steps 2&nbsp;through&nbsp;4 above.\n<\/p>\n<p>\nNow we just hook this up in our event callback function.\nThis should look really familiar, since we\n<a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2013\/03\/25\/10404940.aspx\">\ndid pretty much the same thing earlier this year<\/a>.\n<\/p>\n<pre>\nBOOL\nOnCreate(HWND hwnd, LPCREATESTRUCT lpcs)\n{\n <font COLOR=\"blue\">g_hwndChild = CreateWindow(TEXT(\"listbox\"), NULL,\n     LBS_HASSTRINGS | WS_CHILD | WS_VISIBLE | WS_VSCROLL,\n     0, 0, 0, 0, hwnd, NULL, g_hinst, 0);\n if (!g_hwndChild) return FALSE;<\/font>\n return TRUE;\n}\n<font COLOR=\"blue\">void CALLBACK WinEventProc(\n    HWINEVENTHOOK hWinEventHook,\n    DWORD event,\n    HWND hwnd,\n    LONG idObject,\n    LONG idChild,\n    DWORD dwEventThread,\n    DWORD dwmsEventTime\n)\n{\n if (event == EVENT_SYSTEM_FOREGROUND &amp;amp;\n     idObject == OBJID_WINDOW &amp;&amp;\n     idChild == CHILDID_SELF)\n {\n  PCTSTR pszMsg;\n  TCHAR szBuf[MAX_PATH];\n  if (hwnd) {\n   DWORD cch = ARRAYSIZE(szBuf);\n   if (QueryWindowFullProcessImageName(hwnd, 0,\n                      szBuf, ARRAYSIZE(szBuf))) {\n    pszMsg = szBuf;\n   } else {\n    pszMsg = TEXT(\"&lt;unknown&gt;\");\n   }\n  } else {\n   pszMsg = TEXT(\"&lt;none&gt;\");\n  }\n  ListBox_AddString(g_hwndChild, pszMsg);\n }\n}<\/font>\nint WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev,\n                   LPSTR lpCmdLine, int nShowCmd)\n{\n ...\n  ShowWindow(hwnd, nShowCmd);\n <font COLOR=\"blue\">HWINEVENTHOOK hWinEventHook = SetWinEventHook(\n     EVENT_SYSTEM_FOREGROUND,\n     EVENT_SYSTEM_FOREGROUND,\n     NULL, WinEventProc, 0, 0,\n     WINEVENT_OUTOFCONTEXT);<\/font>\n  while (GetMessage(&amp;msg, NULL, 0, 0)) {\n   TranslateMessage(&amp;msg);\n   DispatchMessage(&amp;msg);\n  }\n  <font COLOR=\"blue\">if (hWinEventHook) UnhookWinEvent(hWinEventHook);<\/font>\n...\n}\n<\/pre>\n<p>\nThe main program installs an accessibility hook for the\n<code>EVENT_SYSTEM_FOREGROUND<\/code> event,\nand each time the event fires,\nit extracts the process name and logs it to\nthe screen.\nSince the notification is asynchronous, the foreground window\nmay have been destroyed by the time the notification is received,\nso we have to be prepared for that case.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Today&#8217;s Little Program simply logs all changes to the foreground window by recording the path to the application the user switched to. You might use this as part of a usability study to monitor what applications users spend most of their time in. Most of this code is just taking things we already know and [&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-2503","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Today&#8217;s Little Program simply logs all changes to the foreground window by recording the path to the application the user switched to. You might use this as part of a usability study to monitor what applications users spend most of their time in. Most of this code is just taking things we already know and [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/2503","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=2503"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/2503\/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=2503"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=2503"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=2503"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}