{"id":98735,"date":"2018-05-11T07:00:00","date_gmt":"2018-05-11T21:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/?p=98735"},"modified":"2019-03-13T00:43:49","modified_gmt":"2019-03-13T07:43:49","slug":"20180511-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20180511-00\/?p=98735","title":{"rendered":"Why can&#8217;t FindWindowEx find another program&#8217;s window by name?"},"content":{"rendered":"<p>A customer had a test app that did something. They then wanted to write a test harness for their test app. The idea is that the test harness launches the test app, finds the test app&#8217;s <i>Start<\/i> button, waits for it to become enabled, and then presses it, and then does some more stuff. The customer was stuck at the second step: Find the <i>Start<\/i> button. <\/p>\n<pre>\n  Console.WriteLine($\"Main window handle: {hwnd}\");\n  IntPtr button = IntPtr.Zero;\n  while (true) {\n    button = FindWindowEx(hwnd, IntPtr.Zero, null, \"Start\");\n    Console.WriteLine($\"Start button window handle: {button}\");\n    if (button != IntPtr.Zero) break;\n    Thread.Sleep(TimeSpan.FromSeconds(1));\n  }\n<\/pre>\n<p>The retry loop is to cover the race condition where the test harness looks for the <i>Start<\/i> button before the test app creates it. <\/p>\n<blockquote CLASS=\"q\"><p>The main window handle is the handle we expect from the test app. But even after a minute, with the <i>Start<\/i> button right there on the screen, the test harness can&#8217;t find it. <\/p><\/blockquote>\n<p>Okay, so what would prevent <code>Find&shy;Window&shy;Ex<\/code> from finding a window? Here are some possibilities: <\/p>\n<ul>\n<li>The thing you want isn&#8217;t actually a window.     It could be a     <a HREF=\"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/20050211-00\/?p=36473\">    windowless control<\/a>.     (The fact that their test harness is written in C# suggests that     maybe their test app is a WinForms or WPF program, both of which     use windowless controls.)<\/li>\n<li>The window title isn&#8217;t exactly what you specified.     Maybe there&#8217;s an accelerator on it: <code>&amp;Start<\/code>.<\/li>\n<li>The thing you want isn&#8217;t a direct child of that window.     Maybe it&#8217;s a grandchild.<\/li>\n<li>The thing you want isn&#8217;t a descendent of that window.     Maybe you&#8217;re passing the wrong parent window.<\/li>\n<\/ul>\n<p>Basically, all of these options boil down to &#8220;<code>Find&shy;Window&shy;Ex<\/code> is not finding your window because there is in fact no window that meets all the criteria you specified.&#8221; <code>Find&shy;Window&shy;Ex<\/code> is working exactly as defined. You need to check that the window you want really does satisfy the criteria you passed. <\/p>\n<p>Double-checking with Spy++ showed that the customer was in the last case: The wrong parent window was being passed. They got the main window from the <code>Process.MainWindowHandle<\/code> property. But that property is synthetic. Windows doesn&#8217;t have a formal concept of a &#8220;main window&#8221;; a program can create multiple top-level visible windows, and as far as Windows is concerned, they are all of equal importance. <\/p>\n<p>My guess as to what happened is that the test app created a splash screen, and that got detected as &#8220;the main window&#8221;. Too bad the splash screen doesn&#8217;t have a <i>Start<\/i> button in it. <\/p>\n<p>The customer updated their test harness to perform an <code>Enum&shy;Windows<\/code> to find the top-level window of the test app whose title indicates that it is the window with the <i>Start<\/i> button. <\/p>\n<p>But this is really a case of answering the question without solving the problem. <\/p>\n<p>If you have a test harness that wants to communicate with a test app, you&#8217;d be better off having a formal mechanism for the test harness to tell the test app what to do, and a formal mechanism for the test app to report results. Because I suspect the customer&#8217;s test harness also polls the test app waiting for the phrase <i>Test passed<\/i> or <i>Test failed<\/i> to appear. <\/p>\n<p>For example, the test harness can pass a <code>\/auto<\/code> command line option to the test app, and the test app goes into automated mode, where it auto-presses the <i>Start<\/i> button, auto-fills in the text boxes with the appropriate test values, and so on. You might even have <code>\/auto:config.xml<\/code> that configures what the test app will do. And then the test app signals whether the test was successful, say by using special exit codes or printing a specific message to <code>stdout<\/code>. <\/p>\n<p>Because what&#8217;s going to happen sooner or later is that somebody&#8217;s going to make changes to the test app, maybe rename or rearrange some buttons, or add a new text box that needs to be filled in, and inadvertently break the test harness. <\/p>\n<p>The test harness and the test app are in cahoots. Make the most of it. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>Are you sure it&#8217;s there?<\/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-98735","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Are you sure it&#8217;s there?<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/98735","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=98735"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/98735\/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=98735"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=98735"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=98735"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}