2

Our company has application that runs as a task bar icon - there is no UI besides the task bar icon.

Certain events cause the task bar to launch a explorer.exe to show a directory. User interaction does not cause this, so our application does not have focus.

I am able to show the directory in windows explorer using code like this:

Process.Start("explorer.exe", "c:\somedirectory");

The problem is, the folder launches in the background and I can't seem to give it focus.

Part of the problem is that the explorer.exe process exits immediately, launching the explorer.exe process separately. I am able to find the launched window using Process.processes() and looking at the window title and start time of the process.

Once I finally get a handle on the process (and wait for it to open), I'm trying to focus it. Here's what I've tried:

//trying to bring the application to the front
form.TopMost = true;
form.Activate();
form.BringToFront();
form.Focus();

Process process = ...;

ShowWindow(process.Handle, WindowShowStyle.ShowNormal);
SetForegroundWindow(process.Handle);
SwitchToThisWindow(process.Handle, true);  

ShowWindow(process.MainWindowHandle, WindowShowStyle.ShowNormal);
SetForegroundWindow(process.MainWindowHandle);
SwitchToThisWindow(process.MainWindowHandle, true);  

This makes the window blink in the task bar, but it still isn't focused.

How can I get the window to come to the front of the screen?

bendytree
  • 13,095
  • 11
  • 75
  • 91
  • 2
    Question is: do you _want_ to? It's horribly annoying to have a window pop up when you're typing in another window. The blinking icon should tell the application requires interaction. – CodeCaster Nov 26 '13 at 18:12
  • 3
    Did you read the documentation of `SetForegroundWindow`? Specifically the bullet points in the *Remarks* section. The executive summary is that the system is designed to stop you stealing focus. In other words, the correct way to deal with this problem is to design your program so that it does not attempt to steal focus. – David Heffernan Nov 26 '13 at 18:13
  • The interaction is initiated by the user in a web browser - basically they need to launch a folder by clicking a link on an intranet page. There really is no other option (we've already explored browser plugins, etc) and it very much is what the users want. Any ideas how focusing the window might be possible? – bendytree Nov 26 '13 at 18:34
  • 1
    You need to do a search for focus stealing – David Heffernan Nov 26 '13 at 18:56
  • True focus stealing is actually implemented in .NET, it uses the Dirty Trick. In the assembly that's everybody's favorite, call the [AppActivate() method](http://msdn.microsoft.com/en-us/library/x9784w8e%28v=vs.110%29.aspx). – Hans Passant Nov 26 '13 at 22:33

2 Answers2

0

You could use the Shell.Application scripting interface to ask Explorer to create and show a new window. I believe this is also possible using a typed interface, but the exact one escapes me at the moment.

var shellApplication = Type.GetTypeFromProgID("Shell.Application");
dynamic shell = Activator.CreateInstance(shellApplication);
shell.Open(@"C:\drop\");

This seems open the window with focus (Tested on Win 8.1 using a timer which opens after 30 seconds, then navigating around in a focused web browser until the timer fires).

Mitch
  • 21,223
  • 6
  • 63
  • 86
  • Thanks Mitch - I tried a version of this using reflection (.NET 2.0). It launches the folder but doesn't focus it. I think running from a non-debug, non-focused winform app is the difference. – bendytree Nov 26 '13 at 21:28
  • I did make sure to have another window focused (in this case, IE), and the process was not running in debug mode. I am not sure why it would work for me and not for you, what OS are you running on? – Mitch Nov 27 '13 at 00:42
  • I was testing on Windows 7 Ultimate x64 - I'm not sure why either. I would definitely prefer 3 LOC over the dozens of very hackish code I ended up with. – bendytree Nov 27 '13 at 02:32
  • curiouser and curiouser... I can reproduce your issue on W7 and vista, but not on newer. I wonder why it changed... – Mitch Nov 27 '13 at 21:37
0

To focus explorer.exe, the application itself needed focus. WinForms intentionally makes this difficult since it could be abused.

Here's how you can steal focus in WinForms. Keep in mind that it may have bad consequences.

Once your application has focus, you can focus another process:

[DllImport("user32.dll")]
private static extern bool SetForegroundWindow(IntPtr hWnd);

SetForegroundWindow(otherProcess.MainWindowHandle);

Here's how I found the explorer process. Like I said, the explorer.exe seems to launch another process and close, so the best option seemed to be to find the most recently launched explorer.exe process:

public static Process GetExplorerProcess()
{
    var all = Process.GetProcessesByName("explorer");
    Process process = null;
    foreach (var p in all)
        if (process == null || p.StartTime > process.StartTime)
            process = p;
    return process;
}

Another option that wouldn't require stealing focus is to show a message from your tray icon. Then you can setup a click handler to open/focus the folder. The application would naturally have focus from the click.

trayIcon.ShowBalloonTip(3000, "", msg, ToolTipIcon.Info);

This falls more in line with "don't annoy the user" but in my case the user is far more annoyed at having to click the bubble.

Update

Finding the explorer process requires admin privileges for your app. I've found that if you focus your own application first, then launch the folder, then the folder is automatically focused. In other words, there is no need to search through the current processes and call SetForegroundWindow.

Community
  • 1
  • 1
bendytree
  • 13,095
  • 11
  • 75
  • 91
  • This is nothing to do with WinForms. – David Heffernan Nov 26 '13 at 21:54
  • @DavidHeffernan - what? – bendytree Nov 26 '13 at 21:59
  • It's not a WinForms issue. The issue is Win32 focus stealing. Read the docs for SetForegroundWindow. – David Heffernan Nov 26 '13 at 22:00
  • @DavidHeffernan I have read the docs. I'm the first to admit that I don't know much about WinForms - that's why people ask questions, right? The last action of the 'stealing focus' link is `form.Activate();` and I have confirmed that it does not work without that call. So WinForms seems involved. Maybe it's not. I'm not sure what your point is. If you'd like to write an answer, I'd be more than happy vote it up if it works properly. – bendytree Nov 26 '13 at 22:09
  • I won't write an answer. This is essentially a dupe of every other focus stealing question. You even linked to one in your answer. It's Win32 that blocks focus stealing. Not WinForms. WinForms is just a thin wrapper over Win32. It doesn't handle focus, Win32 does. This a 100% a Win32 issue. – David Heffernan Nov 26 '13 at 22:11
  • Form.Activate() calls SetForegroundWindow(). The exact same pinvoke you posted. – Hans Passant Nov 26 '13 at 22:30