C# FormWindowState.Normal not to steal focus - c#

I have a Windows Form Application that will go to system tray when it is minimized. When I received a message to pop-up my application it will call ShowWindowFromTray() function. I do not want to steal focus on the application that has the focus because it might interrupts on what the user is doing.
private void ShowWindowFromTray()
{
this.Show();
this.WindowState = FormWindowState.Normal;
}
BTW this application has option that the users can check if the application will always on top or TopMost on all other windows.

Instead of Show(), use the ShowWindow() API with SW_SHOWNA:
private const int SW_SHOWNA = 4;
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
private void ShowWindowFromTray()
{
ShowWindow(this.Handle, SW_SHOWNA);
}

Related

Restore application from system tray when clicking on desktop shortcut

I've created an application that starts off in the system tray when it is started. I used the below post to achieve this:
How to start WinForm app minimized to tray?
This application also only allows a single instance to run:
http://www.codeproject.com/Articles/32908/C-Single-Instance-App-With-the-Ability-To-Restore
The problem I'm getting is when I first start the application it minmizes to the system tray, but if I click the desktop icon it does not appear. I have to click on the icon in the tray to restore the application. If I then minimize it again and then click on the desktop icon it appears.
This is my second attempt at a winform application, is it something to do with the SetVisibleCore?
Any pointers in the right direction would be great.
If you make your WinForms application a singleton, then it is very easy to make the minimized window restore,
http://www.hanselman.com/blog/TheWeeklySourceCode31SingleInstanceWinFormsAndMicrosoftVisualBasicdll.aspx
It is just another variant of using WindowsFormsApplicationBase from Microsoft.VisualBasic.ApplicationServices namespace. Easier/better than using a Mutex.
You might change
void this_StartupNextInstance(object sender, StartupNextInstanceEventArgs e)
{
Form1 form = MainForm as Form1; //My derived form type
form.LoadFile(e.CommandLine[1]);
}
to
void this_StartupNextInstance(object sender, StartupNextInstanceEventArgs e)
{
Form1 form = MainForm as Form1; //My derived form type
form.Show();
form.WindowState = FormWindowState.Normal;
}
What if you write the restore logic in your main. You can do this by using ShowWindow function and the SW_MAXIMIZE flag.
[DllImport("user32.dll", CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
private const int SW_MAXIMIZE = 9; //Command to restore the window
[STAThread]
static void Main()
{
bool onlyInstance = false;
Mutex mutex = new Mutex(true, "UniqueApplicationName", out onlyInstance);
if (!onlyInstance)
{
Process[] p = Process.GetProcessesByName("UniqueApplicationName");
SetForegroundWindow(p[0].MainWindowHandle);
ShowWindow(p[0].MainWindowHandle, SW_MAXIMIZE);
return;
}
Application.Run(new MainForm);
GC.KeepAlive(mutex);
}

Make all forms associated with a program come to the front when one form is selected

I have two forms in my application MainForm and HexCompare. If I click off of my app to another window then I click back on one of the two forms only one of them comes to the front. How can I make it so if I click either one of the two forms it will bring both to the top of all open forms in the application? Right now I need to select each form individually to get them to the top of my stack of windows (and this can be very annoying due to the fact that HexCompare has ShowInTaskbar set to false
A good example of this working the way I want to is how most Find dialogs work. If the find dialog is clicked it brings the main form to the front if it is hidden by another application, and if the main form is clicked the find dialog will come to the front if it is hidden by another application.
How MainForm is invoked.
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
How HexCompare is invoked
private void lstCaputres_SelectedIndexChanged(object sender, EventArgs e)
{
var selectedItem = (Tuple<DateTime, byte[]>)lstCaputres.SelectedItem;
if (hexCompare == null || hexCompare.IsDisposed)
{
hexCompare = new HexCompare(selectedItem.Item2);
hexCompare.Show();
}
else
hexCompare.ChangeValue(selectedItem.Item2);
}
EDIT:
It seems that HexCompare's value of Parent is Null. If I could somehow set it to MainForm would that solve my issue, and if so how to I set it?
EDIT2:
I have semi-solved it using Tigran's solution but it causes flickering as each form is brought to the front, if there is a better solution I am still interested.
//In MainForm.cs
private void MainForm_Activated(object sender, EventArgs e)
{
hexCompare.BringToFront();
this.BringToFront();
}
//in HexCompare.cs
private void HexCompare_Activated(object sender, EventArgs e)
{
parent.BringToFront();
this.BringToFront();
}
You can use the following API wrapper to bring a form to the front of the z-order without having it steal focus. This function can be called in the Activated event of your main form, just pass it your HexCompare form as the parameter. This is not much different from the other answer but I've never witnessed any flicker as you mention in the comments.
private const int SW_SHOWNOACTIVATE = 4;
private const int HWND_TOPMOST = 0;
private const uint SWP_NOACTIVATE = 0x0010;
[DllImport("user32.dll", EntryPoint = "SetWindowPos")]
static extern bool SetWindowPos(
int hWnd, // window handle
int hWndInsertAfter, // placement-order handle
int X, // horizontal position
int Y, // vertical position
int cx, // width
int cy, // height
uint uFlags); // window positioning flags
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
public void ShowInactiveTopmost(Form frm)
{
ShowWindow(frm.Handle, SW_SHOWNOACTIVATE);
SetWindowPos(frm.Handle.ToInt32(), HWND_TOPMOST,
frm.Left, frm.Top, frm.Width, frm.Height,
SWP_NOACTIVATE);
}
To me seems that should be enough to set TopMost=true and call BringToFront() on both forms.
hexCompare = new HexCompare(selectedItem.Item2);
hexCompare.TopMost = true;
hexCompare.Show();
hexCompare.BringToFront();
Something like this.

How to set focus back to form after opening up a process (Notepad)?

I open up a notepad from my program using Process.Start() but the new opened notepad covers the screen. But I do want my application to maintain its focus.
I similarly (using the same Process.Start) open up MS Excel and Word but to get focus back to my form all I need to write is:
this.Focus();
But quirk with Notepad: I open notepad (and all other processes like this)
Process process = new Process();
process.StartInfo.UseShellExecute = true;
process.EnableRaisingEvents = true;
process.StartInfo.FileName = #"abc.log";
process.Start();
Now notepad takes the focus.
I tried these:
this.Activate(), this.Focus(), needless to mention
[DllImport("user32.dll", CharSet=CharSet.Auto, ExactSpelling=true)]
public static extern IntPtr SetFocus(HandleRef hWnd);
{
IntPtr hWnd = myProcess.Handle;
SetFocus(new HandleRef(null, hWnd));
}
[DllImport("User32")]
private static extern int SetForegroundWindow(IntPtr hwnd);
[DllImportAttribute("User32.DLL")]
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
private const int SW_SHOW = 5;
private const int SW_MINIMIZE = 6;
private const int SW_RESTORE = 9;
{
ShowWindow(Process.GetCurrentProcess().MainWindowHandle, SW_RESTORE);
SetForegroundWindow(Process.GetCurrentProcess().MainWindowHandle);
}
Another lengthier solution got from here.
All which still keeps the focus on notepad. Why is it so difficult to merely get focus to a window, that too application's own window?
EDIT: At best I can open the notepad minimized, but it still wouldn't give the focus to the form after trying all the above codes. Notepad opens minimized, but focus will be still on notepad (something that sometimes we see in windows xp) and form will be out of focused.
I tried almost everything on internet (so sure about it :)). At best I could get my form on top of all other forms, but without focus (going by #Hans Passant's method). Going by heavy blocks of codes all over, I somehow felt this aint gonna be easy. So I always used SetForegroundWindow() with chunks of other code. Never thought merely SetForegroundWindow() would do the trick.
This worked.
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool SetForegroundWindow(IntPtr hWnd);
private void button1_Click(object sender, EventArgs e)
{
Process process = new Process();
process.StartInfo.FileName = #...\abc.log";
process.Start();
process.WaitForInputIdle(); //this is the key!!
SetForegroundWindow(this.Handle);
}
At times this method yields in a focus on the parent form (in cases where my desired form is a modal child form of its parent form); in such cases, just add this.Focus() to the last line..
Even this worked:
Microsoft.VisualBasic.Interaction.Shell(#"notepad.exe D:\abc.log",
Microsoft.VisualBasic.AppWinStyle.NormalNoFocus);
Solution provided by here
I had the same problem, i eventually wound up with programmatically calling alt-tab:
[DllImport("user32.dll")]
static extern bool PostMessage(IntPtr hWnd, UInt32 Msg, int wParam, int lParam);
private void alttab()
{
uint WM_SYSCOMMAND = 0x0112;
int SC_PREVWINDOW = 0xF050;
PostMessage(Process.GetCurrentProcess().MainWindowHandle, WM_SYSCOMMAND, SC_PREVWINDOW, 0);
}
//EDIT: You should use process.MainWindowHandle instead ofcourse
If you want to start a process and focus back to the form, then start that process with minimized state, like this:
Dim psi As New ProcessStartInfo("notepad")
psi.WindowStyle = ProcessWindowStyle.Minimized
Process.Start(psi)
Windows prevents apps from shoving their window into the user's face, you are seeing this at work. The exact rules when an app may steal the focus are documented in the MSDN docs for AllowSetForegroundWindow().
A back-door around this restriction is the AttachThreadInput() function. This code worked well, I did take a shortcut on finding the thread ID for the thread that owns the foreground window. Good enough for Notepad, possibly not for other apps. Do beware that Raymond Chen does not approve of this kind of hack.
private void button1_Click(object sender, EventArgs e) {
var prc = Process.Start("notepad.exe");
prc.WaitForInputIdle();
int tid = GetCurrentThreadId();
int tidTo = prc.Threads[0].Id;
if (!AttachThreadInput(tid, tidTo, true)) throw new Win32Exception();
this.BringToFront();
AttachThreadInput(tid, tidTo, false);
}
[DllImport("user32.dll", SetLastError = true)]
private static extern bool AttachThreadInput(int tid, int tidTo, bool attach);
[DllImport("kernel32.dll")]
private static extern int GetCurrentThreadId();
Try this:
public partial class MainForm : Form
{
private ShowForm showForm;
public MainForm()
{
InitializeComponent();
}
private void showButton_Click(object sender, EventArgs e)
{
this.showForm = new ShowForm();
this.showForm.FormClosed += showForm_FormClosed;
this.showForm.Show(this);
}
private void showForm_FormClosed(object sender, FormClosedEventArgs e)
{
this.Focus();
}
}

Options for closing UI app from windows service in C#

I'd like to hear any options for closing Win UI application from windows service.
My service runs under System account. UI application runs for every logged-in user, so there can be many app instances. I need to close them all. I know UI process name and can bind to each process instance and kill it. BUT UI application has tray icon which stays visible (ghost icon, disappears when hovered by mouse) after the process is killed. I'd like to close the UI application correctly, via managed or unmanaged code. Any ideas would be greatly appreciated.
ADDITION 1:
The UI application has no main window, but only tray icon (NotifyIcon component).
ADDITION 2:
I can modify source code of the UI application. But it is written in a way that prevents it from recieving CUSTOM window messages, only standard ones.
ADDITION 3:
The UI application does not show up any Form, it just creates ApplicationContext and executes NotifyIcon within the context.
Program.cs
public static class Program
{
[STAThread]
private static void Main()
{
ApplicationContext context = new TrayApplicationContext();
Application.Run(context);
}
}
TIA
Ivan
Call Process.CloseMainWindow instead of Kill.
If you have access to the source code of the WinUI app then in your main form (the one you start in Application.Run(mainFormGoesHere)) subscribe to the close event and make the notifyIcon.Visible = false; just before you exit. It is a known issue with the NotifyIcon and the system tray.
If it is a 3rd party app, then hope that they too have something implemented like this to properly clean up after being asked to close through CloseMainWindow()
Another approach would be to attempt to refresh the system tray from your service as described here
public const int WM_PAINT = 0xF;
[DllImport("USER32.DLL")]
public static extern int SendMessage(IntPtr hwnd, int msg, int character, IntPtr lpsText);
//Send WM_PAINT Message to paint System Tray which will refresh it.
SendMessage(traynotifywnd,WM_PAINT,0,IntPtr.Zero);
Try sending a WM_CLOSE message to the application:
const uint WM_CLOSE = 0x10;
[DllImport("user32.dll",EntryPoint="SendMessage", SetLastError=true)]
public static extern int SendMessage(IntPtr hWnd, uint uMsg, int wParam, int
lParam);
SendMessage(hWnd, WM_CLOSE, 0, 0);
hWnd is the window handle of the process' main window you are trying to shut down. You possibly also need to send the same message to the window handle of the notify icon
UPDATE
As you can't receive custom messages, you might try this:
enumerate all windows
iterate over each window and retrieve the processID
if the processID matches the one you want to shutdown, send WM_CLOSE to it
retrieving process ID for hWnd:
[DllImport("user32")]
static extern int GetWindowThreadProcessId(IntPtr hWnd, out int processId);
Maybe silly, and I'm not sure this can work within multiple sessions.
Define a custom message and send it to broadcast using PostMessage; within your app capture incoming messages and if you receive your custom one, close gracefully.
In service:
public const int HWND_BROADCAST = 0xffff;
[DllImport("user32")]
public static extern bool PostMessage(int hwnd, int msg, int wparam, int lparam);
int WM_MYMSG = WM_USER + 1;
When you need to send message:
PostMessage(HWND_BROADCAST,WM_MYMSG,0,0);
In your app:
int WM_MYMSG = WM_USER + 1;
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_MYMSG) Close();
base.WndProc(ref m);
}
EDITED:
If you want to override WndProc you need to have a form, but that doesn't mean you have to show a form: in your app create a form and run it, while in form code you write:
private void Form1_Shown(object sender, EventArgs e)
{
// Show here tray icon
....
....
// Hide form
this.Hide();
}
int WM_MYMSG = WM_USER + 1;
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_MYMSG) Close();
base.WndProc(ref m);
}

Keep window on top and steal focus in WinForms

I realize that this would be COMPLETELY bad practice in normal situations, but this is just for a test app that needs to be taking input from a bar code scanner (emulating a keyboard). The problem is that I need to start up some scripts while scanning, so I need the window to regain focus directly after I click the script to run it. I've tried using Activate(), BringToFront(), Focus() as well as some Win32 calls like SetForegroundWindow(), Setcapture() and SetActiveWindow()... however the best I can get any of them to do is to make the taskbar item start blinking to tell me that it wants to have focus, but something is stopping it. BTW, I'm running this on XP SP2 and using .NET 2.0.
Is this possible?
Edit: To clarify, I am running the scripts by double-clicking on them in explorer. So I need it to steal focus back from explorer and to the test app.
I struggled with a similar problem for quite a while. After much experimentation and guessing, this is how I solved it:
// Get the window to the front.
this.TopMost = true;
this.TopMost = false;
// 'Steal' the focus.
this.Activate();
Visibility
Make the window a "Top-Most" window. This is the way the Task-Manager can remain on top of other windows. This is a property of a Form and you make the form top-most (floating above other windows) by setting the value to true.
You shouldn't need to override any of the "Active window" behaviour with the top-most setting.
Focus
I asked a similar question previously here on StackOverflow and the answer would solve your problem. You can make the application use a low-level input hook and get notification of the key-codes coming from the scanner. This way, your application always gets these keys even though the application does not have focus.
You may need to enhance the solution to squash the key-codes so that they are not transmitted to the "in-focus" application (e.g. notepad).
Since Windows 2000, there is no official mechanism for an application to grab focus without direct intervention of the user. Peeking at the input streams through the RawInputDevices hook is the only sensible way to go.
A number of articles may help (C# implementations)
RawInput article on CodeProject
MSDN documentation of RawInput
I had a similar problem and found the following to do the trick. Adapted to C# from here
// force window to have focus
uint foreThread = GetWindowThreadProcessId(GetForegroundWindow(), IntPtr.Zero);
uint appThread = GetCurrentThreadId();
const uint SW_SHOW = 5;
if (foreThread != appThread)
{
AttachThreadInput(foreThread, appThread, true);
BringWindowToTop(form.Handle);
ShowWindow(form.Handle, SW_SHOW);
AttachThreadInput(foreThread, appThread, false);
}
else
{
BringWindowToTop(form.Handle);
ShowWindow(form.Handle, SW_SHOW);
}
form.Activate();
EDIT: Here are the necessary PInvoke definitions for C#:
[DllImport("user32.dll", SetLastError = true)]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
// When you don't want the ProcessId, use this overload and pass IntPtr.Zero for the second parameter
[DllImport("user32.dll")]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId);
[DllImport("kernel32.dll")]
static extern uint GetCurrentThreadId();
/// <summary>The GetForegroundWindow function returns a handle to the foreground window.</summary>
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);
[DllImport("user32.dll", SetLastError = true)]
static extern bool BringWindowToTop(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
static extern bool BringWindowToTop(HandleRef hWnd);
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, uint nCmdShow);
The way I approached this problem was to spawn another thread whose only purpose was to ensure the Form is TopMost and has focus at all times. This code will make all other applications unusable while it is running, which is what I needed for my specific applications. You can add in a Sleep in keepFocus or have some other event trigger it.
using System.Threading; // be sure to include the System.Threading namespace
//Delegates for safe multi-threading.
delegate void DelegateGetFocus();
private DelegateGetFocus m_getFocus;
//Constructor.
myForm()
{
m_getFocus = new DelegateGetFocus(this.getFocus); // initialise getFocus
InitializeComponent();
spawnThread(keepFocus); // call spawnThread method
}
//Spawns a new Thread.
private void spawnThread(ThreadStart ts)
{
try
{
Thread newThread = new Thread(ts);
newThread.IsBackground = true;
newThread.Start();
}
catch(Exception e)
{
MessageBox.Show(e.Message, "Exception!", MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
}
//Continuously call getFocus.
private void keepFocus()
{
while(true)
{
getFocus();
}
}
//Keeps Form on top and gives focus.
private void getFocus()
{
//If we need to invoke this call from another thread.
if (this.InvokeRequired)
{
try
{
this.Invoke(m_getFocus, new object[] { });
}
catch (System.ObjectDisposedException e)
{
// Window was destroyed. No problem but terminate application.
Application.Exit();
}
}
//Otherwise, we're safe.
else
{
this.TopMost = true;
this.Activate();
}
}
}
You might try focusing on a specific input, or try the setting .TopMost property to true (and then unsetting it again).
But I suspect your problem is that these methods all just place messages in the windows event queue, and your program has to wait for all existing events to finish processing before it will handle that one and focus the app.

Categories

Resources