121

I have a C# winforms app that runs a macro in another program. The other program will continually pop up windows and generally make things look, for lack of a better word, crazy. I want to implement a cancel button that will stop the process from running, but I cannot seem to get the window to stay on top. How do I do this in C#?

Edit: I have tried TopMost = true; , but the other program keeps popping up its own windows over top. Is there a way to send my window to the top every n milliseconds?

Edit: The way I solved this was by adding a system tray icon that will cancel the process by double-clicking on it. The system tray icon does no get covered up. Thank you to all who responded. I read the article on why there is not a 'super-on-top' window... it logically does not work.

Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
jle
  • 9,316
  • 5
  • 48
  • 67
  • 77
    Yes, set a timer for every few milliseconds that will set your Form.TopMost to true. Then, just to make it interesting, when the "crazy" program loads, play the audio clip from Mortal Kombat "FIGHT!" :-P – BFree Mar 25 '09 at 20:46
  • 2
    You might thought your comment was hilarious, you might thought you could ridicule bad practice. My problem was creating a context menu that floats over a form with a flowlayoutpanel. A flowlayoutpanel can only be scrolled if you call it's Activate() method, Focus() is NOT enough in certain circumstances. You just won't be able to scroll it. That steals focus from the contextmenu even if it has exclusive topmost = true! As any sensible person knows it's godly practive to let your winform applications run in MTAThread mode and give every form it's own thread which makes the solution simple: – ASA Feb 18 '14 at 09:57
  • 1
    Behold, a devil: http://pastebin.com/sMJX0Yav It works flawlessly without flickering and the sleep(1) is enough to keep it from draining serious performance. Who keeps looking in his taskmanager anyways while he focuses on a context menu? Once the context menu closes it hopefully runs into the empty exception handler and dies. You might build in a isDisposed break though. – ASA Feb 18 '14 at 09:59
  • @Traubenfuchs [That](https://pastebin.com/sMJX0Yav) will fail because of Cross-thread operation exception. [This](https://pastebin.com/3MMuJp1X) should work. – mekb Jul 21 '19 at 02:29

15 Answers15

204

Form.TopMost will work unless the other program is creating topmost windows.

There is no way to create a window that is not covered by new topmost windows of another process. Raymond Chen explained why.

Peter Duniho
  • 68,759
  • 7
  • 102
  • 136
RossFabricant
  • 12,364
  • 3
  • 41
  • 50
  • 15
    In case any other complete newbies see this in 2016 and beyond, try `Form.ActiveForm.TopMost` –  Sep 06 '16 at 13:33
  • 1
    In my case I also had to give the Window a Start Position, otherwise it was not staying at the top `topMostForm.StartPosition = FormStartPosition.CenterScreen` see also: https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.form.topmost?redirectedfrom=MSDN&view=net-5.0#System_Windows_Forms_Form_TopMost – Thomas Feb 18 '21 at 11:51
  • ..."because window is garbage" got it! – aydunno Sep 18 '22 at 14:16
69

I was searching to make my WinForms application "Always on Top" but setting "TopMost" did not do anything for me. I knew it was possible because WinAmp does this (along with a host of other applications).

What I did was make a call to "user32.dll." I had no qualms about doing so and it works great. It's an option, anyway.

First, import the following namespace:

using System.Runtime.InteropServices;

Add a few variables to your class declaration:

private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
private const UInt32 SWP_NOSIZE = 0x0001;
private const UInt32 SWP_NOMOVE = 0x0002;
private const UInt32 TOPMOST_FLAGS = SWP_NOMOVE | SWP_NOSIZE;

Add prototype for user32.dll function:

[DllImport("user32.dll")] 
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);

Then in your code (I added the call in Form_Load()), add the call:

SetWindowPos(this.Handle, HWND_TOPMOST, 0, 0, 0, 0, TOPMOST_FLAGS);

[Reference][1] [1]: http://www.c-sharpcorner.com/uploadfile/kirtan007/make-form-stay-always-on-top-of-every-window/

starball
  • 20,030
  • 7
  • 43
  • 238
clamum
  • 1,237
  • 10
  • 18
  • 2
    This works not only for WinForms applications, but also for [console windows](http://stackoverflow.com/a/37912693/1683264). Nice find! – rojo Jun 19 '16 at 23:09
  • 1
    Nice, can confirm this works. But how would I be able to change it back to not be the top most? is there a HWND_BOTTOMMOST flag that you can share? – Mark Apr 26 '18 at 14:35
  • Good question, if you wanted to swap between this top-most ability and the default behavior (for example, you have an "Always on top" checkbox for window behavior). I'd guess that maybe with the proper flags it would be possible. If you had the right flags that described default window behavior (say SWP_DEFAULT = 0x0003), then you could just call "SetWindowPos()" again with these flags. I'm just not sure; I haven't looked into it. Good luck if you do, and if someone does please add it here! – clamum Apr 26 '18 at 18:36
  • This is not working on full screen game mode as usual – Sajitha Rathnayake May 05 '18 at 21:58
  • 1
    @Mitra M, that's not real at all. Windows works entirely with handles, and any window have one. Check out my edit in this answer: I've added a way to use it in WPF, too. – Davide Cannizzo Nov 09 '18 at 19:15
  • 3
    @Mark Yes, there is a flag HWND_NOTOPMOST (= -2). See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowpos – Kevin Vuilleumier Jul 17 '19 at 07:02
  • 1
    this works even for IE.hwnd! great – Allanckw Jan 02 '22 at 14:28
  • 1
    this answer is perfect to those who write: don't do that ,it's not good,your user will hate you. Oh, come on, don't tell me what is good what is bad,just tell me how to solve my problem. and thanks again, good job clamum – Wang Mar 27 '22 at 06:47
  • @Wang Yeah that sort of response is totally useless. I might kinda agree if you're forcing that upon a user with no option to turn it off, but really that depends on the case/context. Plenty of apps have that feature, like Winamp as I mentioned in the original post. Glad you found it useful! – clamum Apr 01 '22 at 13:36
  • I tried this with visual studio 2019, it worked good in the IDE but not in generated exe it does not work. The form is not top most when running app with exe. I have tried to disable optimize code stuff but it did not help. – zacoder Nov 05 '22 at 17:47
  • @zacoder Well that's interesting. I will look into that, thanks for posting. – clamum Dec 02 '22 at 14:45
24

If by "going crazy" you mean that each window keeps stealing focus from the other, TopMost will not solve the problem.

Instead, try:

CalledForm.Owner = CallerForm;
CalledForm.Show();

This will show the 'child' form without it stealing focus. The child form will also stay on top of its parent even if the parent is activated or focused. This code only works easily if you've created an instance of the child form from within the owner form. Otherwise, you might have to set the owner using the API.

Victor Stoddard
  • 3,582
  • 2
  • 27
  • 27
  • Setting `CalledForm.Owner` to itself (`CalledForm`) will cause a `System.ArgumentException`: 'A circular control reference has been made. A control cannot be owned by or parented to itself.' – mekb Jul 21 '19 at 02:16
  • 2
    Which is why you use CallerForm instead of CalledForm :) – Jesper Sep 09 '19 at 09:52
18

Set Form.TopMost

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
  • I tried, this... do I need to be continually doing it? The 'crazy program' takes over immediately... – jle Mar 25 '09 at 20:40
  • 2
    No - if you set your form.TopMost = true, it should work. The "crazy" program must have it's dialogs set to TopMost as well, in which case, you can't override it. – Reed Copsey Mar 25 '09 at 20:48
15

I had a momentary 5 minute lapse and I forgot to specify the form in full like this:

  myformName.ActiveForm.TopMost = true;

But what I really wanted was THIS!

  this.TopMost = true;
Dave
  • 3,093
  • 35
  • 32
  • Worked perfect for me. if (checkBox1.Checked == true) { this.TopMost = true; } else { this.TopMost = false; } – yosh Feb 07 '20 at 12:05
  • @Yosh, you could just do: this.TopMost = checkBox1.Checked; And I should have replied 2 years ago :D – Dave Mar 02 '23 at 09:53
9

Set the form's .TopMost property to true.

You probably don't want to leave it this way all the time: set it when your external process starts and put it back when it finishes.

Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
6

Why not making your form a dialogue box:

myForm.ShowDialog();
Salim
  • 495
  • 3
  • 20
  • 1
    Yes! This is what I wanted. Setting `TopMost = true` forced my form on top of everything, including chrome, when it reality it's just a settings box and I needed it on top of the main form. Kudos to you internet person. – MDMoore313 Oct 18 '16 at 00:29
5

The following code makes the window always stay on top as well as make it frameless.

using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace StayOnTop
{
    public partial class Form1 : Form
    {
        private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
        private const UInt32 SWP_NOSIZE = 0x0001;
        private const UInt32 SWP_NOMOVE = 0x0002;
        private const UInt32 TOPMOST_FLAGS = SWP_NOMOVE | SWP_NOSIZE;

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);

        public Form1()
        {
            InitializeComponent();
            FormBorderStyle = FormBorderStyle.None;
            TopMost = true;
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            SetWindowPos(this.Handle, HWND_TOPMOST, 100, 100, 300, 300, TOPMOST_FLAGS);
        }

        protected override void WndProc(ref Message m)
        {
            const int RESIZE_HANDLE_SIZE = 10;

            switch (m.Msg)
            {
                case 0x0084/*NCHITTEST*/ :
                    base.WndProc(ref m);

                    if ((int)m.Result == 0x01/*HTCLIENT*/)
                    {
                        Point screenPoint = new Point(m.LParam.ToInt32());
                        Point clientPoint = this.PointToClient(screenPoint);
                        if (clientPoint.Y <= RESIZE_HANDLE_SIZE)
                        {
                            if (clientPoint.X <= RESIZE_HANDLE_SIZE)
                                m.Result = (IntPtr)13/*HTTOPLEFT*/ ;
                            else if (clientPoint.X < (Size.Width - RESIZE_HANDLE_SIZE))
                                m.Result = (IntPtr)12/*HTTOP*/ ;
                            else
                                m.Result = (IntPtr)14/*HTTOPRIGHT*/ ;
                        }
                        else if (clientPoint.Y <= (Size.Height - RESIZE_HANDLE_SIZE))
                        {
                            if (clientPoint.X <= RESIZE_HANDLE_SIZE)
                                m.Result = (IntPtr)10/*HTLEFT*/ ;
                            else if (clientPoint.X < (Size.Width - RESIZE_HANDLE_SIZE))
                                m.Result = (IntPtr)2/*HTCAPTION*/ ;
                            else
                                m.Result = (IntPtr)11/*HTRIGHT*/ ;
                        }
                        else
                        {
                            if (clientPoint.X <= RESIZE_HANDLE_SIZE)
                                m.Result = (IntPtr)16/*HTBOTTOMLEFT*/ ;
                            else if (clientPoint.X < (Size.Width - RESIZE_HANDLE_SIZE))
                                m.Result = (IntPtr)15/*HTBOTTOM*/ ;
                            else
                                m.Result = (IntPtr)17/*HTBOTTOMRIGHT*/ ;
                        }
                    }
                    return;
            }
            base.WndProc(ref m);
        }

        protected override CreateParams CreateParams
        {
            get
            {
                CreateParams cp = base.CreateParams;
                cp.Style |= 0x20000; // <--- use 0x20000
                return cp;
            }
        }
    }
}
mrid
  • 5,782
  • 5
  • 28
  • 71
BK Krish
  • 129
  • 1
  • 4
  • Agree with Alexan - what makes your program topmost? It seems like it's actually just the "topmost = true" statement, which isn't working in a lot of cases. All the rest of the code doesn't really answer the problem. – Fhaab Jul 13 '17 at 22:33
5

The way i solved this was by making a system tray icon that had a cancel option.

jle
  • 9,316
  • 5
  • 48
  • 67
3

Here is the SetForegroundWindow equivalent:

form.Activate();

I have seen people doing weird things like:

this.TopMost = true;
this.Focus();
this.BringToFront();
this.TopMost = false;

http://blog.jorgearimany.com/2010/10/win32-setforegroundwindow-equivalent-in.html

timbre timbre
  • 12,648
  • 10
  • 46
  • 77
  • What if I don't want my window to be active, I just want it topmost (informative, not interactive)? I ask only because actually issuing a "topmost=True" is not working in my case (it works on systems, not on others). – Fhaab Jul 13 '17 at 22:36
  • Found this to work for us: `this.Show(); this.Activate(); this.BringToFront();` But this answer got us to that solution. Thanks! – jibbs Oct 31 '17 at 14:22
3

What is the other application you are trying to suppress the visibility of? Have you investigated other ways of achieving your desired effect? Please do so before subjecting your users to such rogue behaviour as you are describing: what you are trying to do sound rather like what certain naughty sites do with browser windows...

At least try to adhere to the rule of Least Surprise. Users expect to be able to determine the z-order of most applications themselves. You don't know what is most important to them, so if you change anything, you should focus on pushing the other application behind everything rather than promoting your own.

This is of course trickier, since Windows doesn't have a particularly sophisticated window manager. Two approaches suggest themselves:

  1. enumerating top-level windows and checking which process they belong to, dropping their z-order if so. (I'm not sure if there are framework methods for these WinAPI functions.)
  2. Fiddling with child process permissions to prevent it from accessing the desktop... but I wouldn't try this until the othe approach failed, as the child process might end up in a zombie state while requiring user interaction.
Pontus Gagge
  • 17,166
  • 1
  • 38
  • 51
1

I know this is old, but I did not see this response.

In the window (xaml) add:

Deactivated="Window_Deactivated"

In the code behind for Window_Deactivated:

private void Window_Deactivated(object sender, EventArgs e)
    {
        Window window = (Window)sender;
        window.Activate();
    }

This will keep your window on top.

waitWhat
  • 11
  • 4
1

Based on clamum's answer, and Kevin Vuilleumier's comment about the other flag responsible for the behavior, I made this toggle that switches between on-top and not on-top with a button press.

private void button1_Click(object sender, EventArgs e)
    {
        if (on)
        {
            button1.Text = "yes on top";
            IntPtr HwndTopmost = new IntPtr(-1);
            SetWindowPos(this.Handle, HwndTopmost, 0, 0, 0, 0, TopmostFlags);
            on = false;
        }
        else
        {
            button1.Text = "not on top";
            IntPtr HwndTopmost = new IntPtr(-2);
            SetWindowPos(this.Handle, HwndTopmost, 0, 0, 0, 0, TopmostFlags);
            on = true;
        }
    }
Basel Issmail
  • 3,847
  • 7
  • 20
  • 36
1

If you just want to Have Window Forms on top of a Game:(CSGOYour game needs to be in Fulscreen WINDOWED mode) or while you open different programs. Just use TopMost = true; In the:

private void Forms1_Load(object sender, EventArgs e)
        {
            TopMost = true;
        }

In the Latest Version of .Net 6 Visual studio 2022 Just wanted to point this one if you are trying to do this for games :D

0

I did something i little bit differnt kinda found it much easier

so first on Form Load

private void Form1_Load(object sender, EventArgs e)
{       
   this.Shown += new EventHandler(Form1_Shown);//let your form show up here
}

private void Form1_Shown(Object sender, EventArgs e)
{
  Form1.ActiveForm.TopMost = true //and then do your TopMost logic 
}
kunz
  • 1,063
  • 1
  • 11
  • 27