2

I think this might be impossible. Please proof me wrong.

Following setup:

  • My .NET C# Application with GUI (lets call it gui) opens another application (lets call it server) by creating a new Process()
  • The server (developed by somebody else) is started with a parameter to hide its GUI
  • The gui waits for the user to make some inputs
  • The gui then commands the server to perform some tasks
  • Those tasks are defined in an assembly/DLL which i provide to the server
  • One of those tasks is to open a Form/Dialog and ask the user some more questions

Now because the whole userexperience needs to be optimized for repeated operation, the GUI elements (windows/forms/dialogs) that open need to be pre-selected/focused/active.

The first problem arises as i did not find a clear explanation of what the difference between those properties (Focus, Active, Selected, TopMost) is.

Now the real question is, how can i ensure that all GUI elements are active and selected, regardless of whether they are started by my gui process or the server process?

Using the WINAPI can be more powerful i read so i defined the following

// required to use WINAPI for RegainFocus();
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern int SetForegroundWindow(IntPtr hwnd);
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern IntPtr SetActiveWindow(IntPtr hwnd);
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

static private void RegainFocus()
{
    // set focus back to and show application for faster input
    // SW_RESTORE = 9
    ShowWindow(Process.GetCurrentProcess().MainWindowHandle, 9);
    SetForegroundWindow(Process.GetCurrentProcess().MainWindowHandle);
    SetActiveWindow(Process.GetCurrentProcess().MainWindowHandle);
}

Then what i have tried so far is:

  • Set the StartInfo of the server process like this (so the new process does not steal the focus of gui)
    myProcess.StartInfo.UseShellExecute = false;
    myProcess.StartInfo.CreateNoWindow = true;
    myProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
    
  • Start the server process and WaitForInputIdle (like described here) to assure that the server is really ready
  • Also use RegainFocus() in the gui app
  • In the DLL for the server i create a new Form() and try (to bring the window to front and have it selected)
    myForm = new Form();
    myForm.Activated += dialog_Activated;
    myForm.PerformLayout();
    myForm.TopMost = true;
    myForm.Activate();
    myForm.BringToFront();
    myForm.Focus();
    myForm.Select();
    DialogResult result = myForm.ShowDialog();
    
  • The TopMost=true works in bringing the dialog in front of gui
  • The dialog_Activated() method sets the Focus on the first input control using FocusControl(). This works.

The result of this is a window on top of my gui which has the cursor blinking in the first input control but is deselected/inactive. When i hit <TAB> i can see a different control gets selected in the gui which is in the background.

I tried spraying RegainFocus() calls in the Form as well, didnt work.

Further ideas that i have but no way of achieving them:

Target Framework is .NET 4.5, target operating systems are Windows 7 and Windows 10.

Thank you for any help and inputs/tipps!

BNT
  • 936
  • 10
  • 27
  • See [Window Features](https://learn.microsoft.com/en-us/windows/desktop/winmsg/window-features). Also relevant: [Foreground activation permission is like love: You can’t steal it, it has to be given to you](https://blogs.msdn.microsoft.com/oldnewthing/20090220-00/?p=19083). – IInspectable Dec 10 '18 at 12:55
  • 1
    Note that `Form.BringToFront` etc has no effect while the form is not shown yet. – dymanoid Dec 10 '18 at 12:57

1 Answers1

5

I got a solution which seems to work on Windows 10 and Windows 7.

After my focus is lost i use the RegainFocus() function to regain focus (and have the window activated), see question. Attention: dont mix process HANDLE and MainWindowHandle. When an object is hidden, its MainWindowHandle might very well be 0.

I removed

    myProcess.StartInfo.UseShellExecute = false;
    myProcess.StartInfo.CreateNoWindow = true;
    myProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;

when starting the server process in order of not messing with the visibility state. I kept [WaitForInputIdle][1] though.

On Windows 7, this was still not enough, and AllowSetForegroundWindow on the created process did not work (thanks for the comments though), instead of bringing the window to the front, the taskbar was flashing. The solution here was to set the registry value of

\HKEY_CURRENT_USER\Control Panel\Desktop\ForegroundLockTimeout 

to 0 and restarting the machine. Then the code behaved the same for Windows 7 and 10. With the right permissions, this could even be done programatically.

Since this problem has so many different answers and solutions, i assume that it depends on many factors (like windows version, registry setup, probably UAC and theme settings, framework and runtime, some more conditions, ...) and this is why i want to mention the other approaches i found as well.

BNT
  • 936
  • 10
  • 27