Focus Windows Explorer From C# App In Task Bar - c#

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?

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).

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.

Related

Remove Notification icon on WPF application on process exit

There should be a pretty straightforward answer to this question but I can´t seem to find a solution. I have seen similar questions on SO (like NotifyIcon remains in Tray even after application closing but disappears on Mouse Hover) but they don´t cover my case.
I have a WPF application with a Notification Icon that is set up inside the MainWindow method:
InitializeComponent();
myIcon = new System.Windows.Forms.NotifyIcon();
myIcon.Icon = someFancyIcon;
myIcon.Visible = true;
On the xaml I have defined the Window attribute Closing="Window_Closing" so that when the window is closed it calls:
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
Log.Info("Closing application");
myIcon.Icon = null;
Environment.Exit(0);
}
If I ran the application and close it in the normal way, Window_Closing is called and the notification icon disappears as expected (and info is output to the log).
Problem is: this application is monitored by another process (out of my control), and that other process sometimes kills the WPF application process so that Window_Closing is never called and my application notification icon keeps on lingering on the notification area until you hover your mouse over it. Since the monitoring process can restart my application and close it again several times, the notification area is soon filled with copies of the notification icon.
How can I remove the notification icon so that even if my application is finished in some abnormal way the icon disappears?
In common case, you can't do that due how Windows Tray is designed. All the tray icons are driven by their apps: they're created by and removed by the corresponding app who owns that icon(s).
If the app is terminated abnormally the tray has no clue about that. The only case is to move mouse pointer over the orphaned icon and then it will be removed from tray as no corresponding app found alive.
What can we do then? Do not kill the app. Instead, send it a message causing it to close (so, all handlers would be called then and all resources are to be freed, including tray icons as well).

C# control 3rd party program in system tray

I'm currently writing a C# program which basically functions as a watchdog for a 3rd party program: If the traffic falls below a certain value, the process is killed and then restarted [using a simple taskkill /im].
This is working perfectly fine, with one exception - if the program is minimized to the system tray, it will not respond to taskkill /im and you have to force-terminate it with taskkill /f /im.
As one can guess, this is by far not the best way to end a program. So I'm currently searching for a way to access the Windows system tray and basically perform a click on the program/call a method to maximize it back to normal.
I had thought about literally simulating a click on the tray, but found this method to be way to inaccurate and unpredictable.
Is there another way to accomplish this [by using a system api, for example]?
Thanks!
You can try using the user32.dll to maximize the processes window.
// Pinvoke declaration for ShowWindow
private const int SW_SHOWMAXIMIZED = 3;
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
// Sample usage
ShowWindow(proc.MainWindowHandle, SW_SHOWMAXIMIZED);
Taken from this SO answer.
For a complete list of window states visit this MSDN Page
You can get the WindowHandle by doing something like this
var processes = Process.GetProcessesByName("nameOfYourProcess");
foreach(var process in processes)
{
var windowHandle = process.getMainWindowHandle;
}
Though this will only work if the process still has a Window.
The only way you can iteract with the tray icon of another application in windows is by simulation the mouse click. This has the mayor flaw that if the user is interacting with the mouse at the same time you "steel" hes cursor and he cannot work.
If you know you will not end up with a memory leek (if its a managed app you've won) and the application you are killing isn't doing some important stuff when it's exiting a process kill is the better option.

Process does not kill on form close

I have just built version one of my testing application using Windows Forms. I have noticed that when running the application, it runs completely fine no hitches, exactly like the debug view. When it comes to closing the application I have noticed that the actual executable/process name hangs within Task manager and does not correctly close.
Upon further inspection I have noticed that when calling another form without hiding the previous form, a new process is spawned (kinda expected). When closing the new form (containing a few text boxes, labels and a DataGridView) the newly spawned process does not kill it's self, but remains. Then closing the main window the window disappears from the taskbar/view, but still, the processes remain active using 8,268k - 8,308k Memory
private void ClientSearch_Click(object sender, EventArgs e)
{
ClientSearch Clientsearch = new ClientSearch();
Clientsearch.Show();
}
Standard explanations for this behavior:
Hiding your main window when you display another window and forgetting to unhide it. There is no visible window anymore, nor can the user do anything to unhide it, but your app keeps motoring of course.
Starting a thread and not making sure that it is terminated when the main window closes. Setting the thread's IsBackground property to true is a workaround for that.
Calling Application.DoEvents() in your code. A very dangerous method that permits you to close the user interface but doesn't stop the loop in which it was called so the main thread of your app does not exit either.
This kind of problem is readily visible as well when you debug your app. You might have gotten in the habit of using the red rectangle on the toolbar (aka Debug + Stop Debugging) to force the debugger to quit. The Debug + Windows + Threads debugger window can provide insight into the cause of the last two bullets. Or you can use Tools + Attach to Process to attach the debugger to a zombie process.
Call
Application.Exit();
on form close/closing.
Your applications should only be creating one process per run. A new form should not be creating a new process.

Right way to run, kill and keep process running

In my application, which using another application (run in tray) to print receipts I need to do those three things:
Open process when on mainApplication startup
Close process when mainApplication closing or changing any information about printer
Keep process alive, if it get any error
First point is quiet easy, I just simply
Process.Start("_ReceiptPrinter.exe");
And process working ;)
But now, the two other issues:
Closing process. I've tried this code:
Process[] allProcs = Process.GetProcesses();
foreach (Process proc in allProcs)
{
ProcessThreadCollection myThreads = proc.Threads;
if (proc.ProcessName == "_ReceiptPrinter")
{
proc.Close();
}
}
Unfortunately, I can still see icon in tray, and process is still running.
Keep process alive. My main application is in WPF, that one from tray is written on WinForms. Maybe there is any way to handle ANY WinForm application exit event (well, any, but not this one, which just simply close it from another application), and reopen it?
proc.Close() asks it to close but there is no guarantee. Use:
proc.Kill();
The reason you still see a tray icon is that the icons are cached by an external process (windows explorer.)
The reason process.Close() does not close the application is because the application is not processing window messages (as this call simulates a WM_CLOSE request, per classic Windows API.)
The proper way to close the application is process.Close, not process.Kill(), further, as part of app/window close you need to unregister any tray icons you've registered with the system. This way any normal closure of your application will properly clean-up the tray.
Further, you can use a "critical finalizer" which would be guaranteed to run before application exit, except in total catastrophe scenarios.

How do I get the active ChildWindow of an application?

I have this problem: I have an handler to the mainWindow of a certain application, and I want to simulate a keypress on that application...
I'm using sendMessage/postMessage api calls to do this. The reason why I don't use the .Net SendKeys function or the keybd_event of the win32 api, is that they simulate the keypress at a global level. In my case, the target application is not the top-active one (other application may be running in a higher z-level, hence covering the target app).
The problem with sendMessage and postMessage is that you must pass the handler of the exact childwindow where you want the key to be pressed. For example, in notepad, if I send the key to the handler of the mainWindow, nothing happens, I have to send the key to the handler of the child window that basically consists of the white canvas where you can write.
Obtaining the handler to the active child window is the problem. In the beginning, I was using the GetTopWindow or GetWindow(GW_CHILD) api calls, as it returns the most active child window. What I was doing was to keep calling the GetWindow(GW_CHILD) until I got a childwindow that had no more childWindows. This works ok for some applications like notepad or paint. However, in some cases (like firefox for example), it doesn't work. The reason for that is that the parent window has the whole firefox area, and its childwindow has the opened WebPage (like google). So, when I ask for the most active child window of the mainWindow, it returns the only child window it has, which is the one corresponding to the Webpage area. It only works if the active window is that one (like if the user is writing something on a textbox of a certain page). But if what is active is, let's say, the address bar, it doesn't work because the active window is not the child window but actually the parent... and I can't get this information programatically.
I actually found a way of doing this, using the GetGUIThreadInfo api call, using the following code:
// get thread of the main window handle of the process
var threadId = GetWindowThreadProcessId(firefox.MainWindowHandle, IntPtr.Zero);
// get gui info
var info = new GUITHREADINFO();
info.cbSize = (uint)Marshal.SizeOf(info);
if (!GetGUIThreadInfo(threadId, out info))
throw new Win32Exception();
// send the letter W to the active window
PostMessage(info.hwndActive, WM_KEYDOWN, (IntPtr)Keys.W, IntPtr.Zero);
And it works very well: If the address bar is active, it sends a "W" letter to the address bar. If the search textBox of google is active, it sends the "W" letter to it... Perfect! However, this method can't be used by me for a simple reason:
If the target application is not the active window of the operating system, the ThreadInfo structure comes empty. For example, if I'm targetting firefox, it works if firefox is active (the top-most application, the focused/active one), but if, let's say, notepad is on top of firefox, it doesn't work, it is unable to get the active window handler.
I know I can solve this by using the setForegroundWindow api call to activate the target application and then capture the handler of the active child-window but I didn't want to have to bring the target app to the foreground.
I've also tried other techniques like AttachThreadInput() and GetFocus() api calls, which also works, but has the same problem: If the target application is not the active windows application, it doesn't work.
So basically I need to find some way of getting the handler to the active childwindow of an application even if that application is not the top-active one.
Any ideas?
Thanks
You might want to check out the EnumChildWindows function.
If everything else fails, here is another idea: You might want to consider using a WH_CBT or a WH_CALLWNDPROC hooks to monitor which child window of the target thread has been focused last.
Install a CBT hook (WH_CBT) and listen for the HCBT_SETFOCUS notification.
Or use a WH_CALLWNDPROC hook and listen for the WM_SETFOCUS message.
Don't do much in the hook proc or you'll hog down the system resources. Just save the needed information and post yourself a custom message to process it later.

Categories

Resources