Create a WordPad instance and bring it to the top z-order, all from a non-foreground app

229 Views Asked by At

I am working with voice-operated software (Dragon from Nuance, or maybe Windows Speech Recognition (haven't tried that yet)).

In operation, the user (me) issues a voice command to make something happen. For this question, I am trying to write code that starts up a simple app such as notepad or wordpad so that it can receive dictation. Another form of voice command switches apps to bring a background app (mail, browser, spreadsheet, etc.) to the foreground so that it can receive voice input. So this is not a "focus-stealing" malware scenario; it is user-driven but from the microphone rather than from the keyboard or mouse.

I have a C# app (let’s call it Creator) that creates a new process that runs WordPad. I want the newly created WordPad instance to appear as the top window on the screen as if it had been created from the Start menu.

Everything works fine as long as the Creator app is the foreground app when it creates the WordPad process. In that case, the WordPad instance appears above the Creator process in the Z-order as expected.

However, if the Creator window is not the foreground window when it creates the WordPad process and instance, the newly created WordPad instance does not appear on the top of the Z-order. It appears on top of the Creator window, but below whatever other windows were above the Creator process in the Z-order at the time the WordPad process was created.

I have read many posts and have tried many sequences and variations of SetForegroundWindow, SetWindowPos, ShowWindow, WindowRestore, and Focus, but with no success. The newly created WordPad instance is always created above the Creator window but below all the windows on top of the Creator window.

What am I doing wrong?

var proc = Process.Start(WordPadExepath);
if (proc != null) {
    // no permutation of these calls works reliably
    // rarely and randomly, the WordPad instance sometimes appears on top
    ShowWindow (proc.Handle, 1);
    Win32.SetForegroundWindow(proc.Handle);
    WinFuns.WindowRestore(new WindowHandle(proc.Handle));
    SetWindowPos (proc.Handle, new IntPtr(0), 0, 0, 0, 0, 3);
}

I understand the problem of focus-stealing, and one of the commenters below provided a useful link to one of Chen's old posts. Yet, the Dragon voice software somehow does what I want to do. The "DragonBar" app is always on top of all other windows, but allows me to move the focus and switch apps with the mouse. (Is it correct to infer that being on top is independent of being the foreground app?)

For now, I can receive the user's voice request in my app. My question is how can I implement the user's request to start up a new process (and WordPad) and bring them to the foreground from my little Creator app? As long as my Creator app is the foreground/top app when I get the user request, everything is fine. But if I get the voice request while Creator is not the foreground app, I cannot do what the user has requested. Thank you.

1

There are 1 best solutions below

9
On

First, make sure the process has the foreground activation permission by the current foreground window.

According to the document SetForegroundWindow,

A process can set the foreground window only if one of the following conditions is true:

  • The process is the foreground process.
  • The process was started by the foreground process.
  • The process received the last input event.
  • There is no foreground process.
  • The process is being debugged.
  • The foreground process is not a Modern Application or the Start Screen.
  • The foreground is not locked (see LockSetForegroundWindow).
  • The foreground lock time-out has expired (see SPI_GETFOREGROUNDLOCKTIMEOUT in SystemParametersInfo).
  • No menus are active.

Second, Process.Handle is not the window handle, you could try to use Process.MainWindowHandle property.

Third, If you are not sure or do not have a foreground activation permission, you need to generate a Windows notification so that the user himself caould decide to put the window to the foreground.

You can refer to the following document:

Send a local toast notification from desktop C# apps