-1

I am trying to use Process.Start (with UseShellExecute=true) to create an instance of notepad.exe. The code below runs inside of a COM object called by another program (a voice-input program).

Most of the time the code runs fine and creates the notepad instances as expected as a new top window with the focus. I can open and close and open, open, close, close, notepad windows in random order as expected if I close the notepad windows with a WM_CLOSE message as follows:

    Win32.PostMessage(Hwnd, WM_SYSCOMMAND, SC_CLOSE, 0);

A Second Way of Closing the Notepad Window

The mainstream third-party voice software also provides a “close window” command that will close the notepad window when it is in the foreground. This method of closing the notepad instance is part of the problem.

The Problem

The problem is that after closing the notepad with the voice command “close window,” the next open operation opens notepad but does not bring it to the foreground. It opens behind Outlook or Chrome and is visible on the taskbar. For some reason, the Process.Start code does not bring the notepad instance to the foreground after notepad has been closed with the “close window” command. I don't understand why. The Process.Start code should have no clue how (or if) the previous notepad instance was closed.

The code below tries to SetWindowPos to force the notepad window to the foreground/top in case Process.Start did not do the job, but the SetWindowPos code never fails and the failure message box never appears, even when the problem occurs. (SetWindowPos is only a side issue, since the code normally works fine with WM_CLOSE messages.)

Reproducible on My Machine

Here is a typical pattern that I logged on my machine. FChrome/FOutlook means Chrome or Outlook were the foreground windows for the experiment (both were tried with the same results). No = notepad open, Nc = notepad close (as above), CW = “close window”, and NoXX means failure (notepad did not appear as the top foreground window).

Sequences of operations: open, close, open, "close window", open (notepad open fails NoXX)
FChrome, No, Nc, No, CW, NoXX
FOutlook, No, Nc, No, CW, NoXX

Does anyone know why a window-closing method (maybe SendMessage or Win32.WindowDestroy?) would make Process.Start fail to bring a new instance of Notepad to the foreground?

    // record name of foreground window before the operation
    var forehandle = GetForegroundWindow ();
    var forenamebefore = WinFuns.GetForegroundWindow ().Title;

    // create process to start notepad
    var psi = new ProcessStartInfo ();
    psi.UseShellExecute = true;
    psi.FileName = "notepad.exe";
    var proc = Process.Start (psi);
    Thread.Sleep (2000);

    // get foreground process name after Process.Start - it should be notepad
    proc.Refresh (); // refresh before retrieving the handle
    IntPtr prochandle = (IntPtr)proc.MainWindowHandle;
    var forenameafter = WinFuns.GetForegroundWindow ().Title;

    // get the process name of this code
    var myhandle = Process.GetCurrentProcess ().MainWindowHandle;
    var myname = (new WindowHandle (myhandle)).Title;

    // ensure the notepad instance is on top
    bool r;
    var p = new IntPtr (0);
    r = Win32.SetWindowPos (prochandle, p, 0, 0, 0, 0, 3); //window on top
    if (!r) MessageBox.Show ($"Failed to set top window position.");

    // show before/after/mynames after the operation is complete
    var msg = $"Foreground before: {forenamebefore}\n" +
              $"Foreground after: {forenameafter}\n" +
              $"My name is:       {myname}";
    MessageBox.Show (msg);

Community
  • 1
  • 1
Kevin
  • 1,548
  • 2
  • 19
  • 34
  • `WaitForInputIdle` [doesn't do](https://stackoverflow.com/q/33405201/1889329) what you think it does (or what the name implies, for that matter). – IInspectable Feb 25 '20 at 20:51
  • Everytime I got problem with something close to SerWindowPos it was link to some cache settings in the registry (regedit). Try to clear it after closing the app by the voice control – Flavien Marianacci Feb 25 '20 at 20:57
  • Hi, `WaitForInputIdle` was named in another thread here, and may well do something (or not) that I don't understand. I used `Sleep` just in case, but I don't think waiting is an issue since the code normally works fine with WM_CLOSE. I also don't think `SetWindowPos` is an issue, since `Process.Start` can bring up the window even without `SetWindowPos`. And how can SetWindowPos be connected to anything in registry? Do you have a link to some doc on that? Thank you both. – Kevin Feb 25 '20 at 21:11
  • And the down vote "doesn't show any research effort" is a silly, unhelpful statement and vote. I've spent weeks on this problem and dozens of hours and have read hundreds of web pages. – Kevin Feb 25 '20 at 21:13
  • The link I posted contains solutions on how to *properly* wait for a GUI process to display its UI. – IInspectable Feb 25 '20 at 23:00
  • Besides, `MainWindowHandle` is a lie. There is no such thing as a *"main"* window in a Win32 application. .NET uses some heuristics to figure out what a user of .NET may recognize as the *"main"* window. Sometimes that works, and other times it fails. If your code relies on it, it, too, will fail. – IInspectable Feb 25 '20 at 23:10
  • I deleted my previous comment because part of it was way off the mark. I apologize; I was looking at a different link. I followed your link and read a pile of material on WinEvents as the proper way to wait. I confess it was daunting, and there were no simple code examples. If you know of a link to an example that would work with my COM object containing the callback, please let me know. Thank you. After attaching threads before the SetForegroundWindow op, and loop-with-limit-trying-waiting until it gives me the desired answer, my code is working better (not perfect yet). – Kevin Feb 26 '20 at 04:34
  • I also read up on MainWindowHandle is a lie, and saw (and understood) the code there; I also use EnumWindows quite a bit. Your use of a LINQ-like "EnumGetWindows.where " was interesting! But in my case, MainWindowHandle returns the right result after the wait is complete, so searching for the window by its title (like in the example) seems unnecessary. Besides, I don't always know what the title of the "notepad" window will be (it could be any app). – Kevin Feb 26 '20 at 04:36

1 Answers1

0

After working on this problem for another few days and trying out various solution attempts, I still cannot explain why the original problem occurred (failing to bring notepad to the front after closing it with a different app).

However, over time my code grew in size as I checked every Win32 error code after every Win32 operation and retried by looping and trying again as necessary or appropriate.

I conclude that checking every Win32 error code on every operation and retrying failed operations every 100 milliseconds caught some failed operations, but did not solve the original problem.

Kevin
  • 1,548
  • 2
  • 19
  • 34