1

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.

Kevin
  • 1,548
  • 2
  • 19
  • 34
  • 1
    "Foreground activation permission is like love: You can’t steal it, it has to be given to you": https://devblogs.microsoft.com/oldnewthing/?p=19083 – Simon Mourier Feb 10 '20 at 07:00
  • Thank you for the reference to Chen's article. (I've read others of his, but not that one.) Are you saying that he is saying there is no legitimate way for me to accomplish my goal? (Because obviously, his "second program" is WordPad in my case, and I can't get WordPad to do anything.) Thank you – Kevin Feb 10 '20 at 07:58
  • Focus stealing (in the general term, not specially talking about keyboard) is an end-user annoyance. There are strict rules defined in SetForegroundWindow official doc. You must find other ways, for example, Windows 10 notifications that the end-user can configure. – Simon Mourier Feb 10 '20 at 08:46
  • 1
    What's the specific reason you want Word Pad to become the foreground window, even when the user isn't working with the application that launches it? – IInspectable Feb 10 '20 at 08:59
  • @IInspectable, thank you for prodding me to add more context information. I edited the original post to give you and others more scenario information. One of the key ideas is that user voice input can be received by my app when my app is not the foreground app. Voice input goes to apps that are not technically the foreground keyboard app, I think. – Kevin Feb 10 '20 at 14:42

1 Answers1

0

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

Drake Wu
  • 6,927
  • 1
  • 7
  • 30
  • thank you for your helpful post. Chen's post says that my app cannot make the started Wordpad process/app the top z-order/foreground window unless my app is already in the foreground and top z-position. The issue seems to be how voice input is treated. Do you know if voice input through the microphone automatically gives Dragon the foreground / top z-order position? If so, then I must figure out a way to get Dragon to move my Wordpad instance to the foreground. My Creator app is a normal Forms app. – Kevin Feb 13 '20 at 01:06
  • The key thing is that the user (me) is already issuing a voice command that embodies user intent. In a functional way, the voice command is the same as using the keyboard or mouse to start Wordpad from the start menu or task bar (except that I'm using my voice instead of my typing or mouse fingers). – Kevin Feb 13 '20 at 01:09
  • It seems that in my test, if I put a delay before the `Process.Start()`, and then bring the windows of other processes to the foreground, after delay, the wordpad instance will appear to the foreground. Did you start the process in a service? – Drake Wu Feb 14 '20 at 08:05
  • Hi Drake, thank you so much for your continued interest! I have tried 2 ways with many variations of them: 1) Dragon->COM object->Process.Start, and 2) Dragon->WindowsApp->Process.Start. None of the variations works reliably, even though I thought the COM method would put the Process.Start on the (foreground?) thread of Dragon. (Is Dragon the foreground/thread or window while it is running?) Some variations worked better than others, but most would not make WordPad the foreground window, and most would not give it the focus reliably. I don't know what else to try. – Kevin Feb 14 '20 at 21:29
  • Would starting the Process.Start in a service enable the Wordpad instance to be brought to the foreground and given the focus? I can do that with some effort, if you think it is a good bet (Dragon->COM->Service->Process.Start). I keep thinking I need to figure out 1) who is foreground with focus at the time of the voice command (so they can give foreground/focus away, per the rules), and 2) a mechanism for somehow having them start my process reliably. – Kevin Feb 14 '20 at 21:35
  • I suppose that it is worth saying that for people who cannot use a keyboard or mouse, being able to start programs and bring them to the foreground is critically important. Issuing a voice command is a form of direct user intent, so Windows rules should allow the intent to be implemented somehow. But Dragon is the middleman (interpreting the voice command), and it seems when it gives me the command, it's too late for me to bring a Wordpad instance to the foreground with focus. *Especially* if the Desktop has the focus after closing an app or something like that. – Kevin Feb 14 '20 at 21:38
  • Drake, one other thing I tried was to issue a MessageBox.Show before/after the Process.Start line. The message box brought the Creator parent process (and the Wordpad window above in z-order) to the foreground and usually gave Wordpad the focus. But it was an ugly solution because the messagebox would flash until it timed out and disappeared. What I need is a `MessageBox.Visible = false` form, but nothing like that exists. I tried a normal visible=false form, but it could not bring the Creator process or Wordpad to the foreground. MessageBox has special rights for that, I think. – Kevin Feb 14 '20 at 21:43
  • Whether it is feasible to set `ProcessStartInfo` parameter for `Process.Start()`, and specify `ProcessWindowStyle` as `Maximized` or `Normal`(although the default value is `Normal`) – Drake Wu Feb 18 '20 at 02:25
  • The `Process.Start` call (with pathname parameter) does create the window in the normal size, so I expect that the Normal setting is working correctly. The problem is that the Wordpad instance gets created behind Chrome or whatever is in the foreground. I need to figure out how to get the Wordpad instance to 1) be on top and 2) have the input focus. Dragon does it somehow by owning the voice input channel, I think. Because at the time Dragon brings up an app, Word or Excel could be receiving the keyboard input. And no app "owns" the mouse input channel. – Kevin Feb 19 '20 at 03:07