24

I know all the reasons why it is a bad idea. I dislike it if an application steals input focus, but this is for purely personal use and I want it to happen; it will not disturb anything.

(for the curious: I am running unit tests in NetBeans, which generate a log file. When my background application sees the log file's timestamp change I want it to parse the log file and come to the front to show the results).

This question did not help, nor did googling. It seems that BringToFront() hasn't worked for a long time and I can't find any alternative that does.

Any ideas?

Mawg says reinstate Monica
  • 38,334
  • 103
  • 306
  • 551
  • 1
    You can't do this. Windows XP had a workaround that would allow it; versions since then prohibit it. – Ken White Oct 18 '12 at 02:21
  • +1 @ken If you are 100% sure, then post an answer and I will award. I was hoping for some obscure trick like shrinking to system tray & then restoring, or hiding then showing again ... – Mawg says reinstate Monica Oct 18 '12 at 02:34
  • if you just want to bring your application forward, but not steal input focus then you could use: if IsIconic(Application.MainForm.Handle) then ShowWindow(Handle, SW_RESTORE); FormStyle := fsStayOnTop; FormStyle := fsNormal; First it check if the app is minimized and restores then sets the form style to stay on top and resets back to normal. – A Lombardo Oct 18 '12 at 04:34
  • 2
    @Mawg did you test my solution? – whosrdaddy Oct 18 '12 at 11:23
  • @whosrdaddy Yes, your solution does work - for me (and that's enough for me). As Ken states, it may not work for others. I wish that I could award you the answer, but I think that Ken ansered the question it was actually asked, even if you did give me the solution that I, personally, wanted. I hope that you don't mind. I will +1 everything you posted to this question. – Mawg says reinstate Monica Oct 20 '12 at 03:46
  • 1
    @Mawg - Out of curiosity, have you ever tested the solution in my post? – Sertac Akyuz Oct 24 '12 at 10:03
  • +1 for giving me -1, @SertacAkyuz. I do aplogize, that I "wasted your time" (actually, what you posted did work), but I have already awarded the answer when you posted and it was correct ("You can't do this reliably"). How would Ken feel if I took away the answer? It was a tough call and I was going to annoy someone. I am sorry that was you. You have been extremely helpful to me on other questions. Sorry about this one. – Mawg says reinstate Monica Oct 30 '12 at 05:57
  • 2
    @Mawg - It's not about accept (I wouldn't post an answer to an already answered question if it was), it's about feedback - just "yeah, it works" or "no, this doesn't work". The above comment/question was not the first, its predecessor stayed there for days, I then deleted it and asked another one when I was sure you were online (and this one stayed there for more than five days too). – Sertac Akyuz Oct 30 '12 at 08:59

8 Answers8

39

Here's something simple that seems to work, tested with multiple boxes consisting of XP, Server2003, Vista, Server2008, W7. Test application ran with a standard (or admin) account, stole the input focus from notepad while writing in the foreground.

var
  Input: TInput;
begin
  ZeroMemory(@Input, SizeOf(Input));
  SendInput(1, Input, SizeOf(Input)); // don't send anyting actually to another app..
  SetForegroundWindow(Handle);

You can tweak it further f.i. for a minimized app or such if required.

Sertac Akyuz
  • 54,131
  • 4
  • 102
  • 169
20

You can't do this reliably. Windows XP had a workaround that would allow it, but versions since then prohibit it for the most part (see note below). This is expressly stated in the remarks section of the MSDN documentation on SetForegroundWindow:

The system restricts which processes can set the foreground window. A process can set the foreground window only if one of the following conditions is true:

  • The process is the foreground process.
  • The process was started by the foreground process.
  • The process received the last input event.
  • There is no foreground process.
  • The foreground process is being debugged.
  • The foreground is not locked (see LockSetForegroundWindow).
  • The foreground lock time-out has expired (see SPI_GETFOREGROUNDLOCKTIMEOUT in SystemParametersInfo).
  • No menus are active.

An application cannot force a window to the foreground while the user is working with another window. Instead, Windows flashes the taskbar button of the window to notify the user.

Note the final paragraph of the quoted documentation, especially the first sentence.

The problem with

this is for purely personal use and I want it to happen; it will not disturb anything.

is that any application could try to do the same thing. How does "purely personal use" tell it's you personally and not just any application? :-)

Note: I've tried everything I can think of to get the IDE's documentation to show in the foreground when I click the help toolbutton, but all I can get is the flashing taskbar button (and I as the user want it to do so).

Ken White
  • 123,280
  • 14
  • 225
  • 444
  • +1 (and for your comment above ;-) Obviously, I will live in hope and leave this open for a few hours before awarding. As I said, I was hoping for some obscure trick like shrinking to system tray & then restoring, or hiding then showing again - but nothing that I tried works :-( It's a lot of work, just to avoid Alt-Tab (hmm, maybe if I had a "launcher" app, which then launched a "process" app. Would the new app then launch in foreground?) – Mawg says reinstate Monica Oct 18 '12 at 02:56
  • You can use AlwaysOnTop style, not that it is friendly to other apps, yet it would draw usaer attention. Though maybe some special alerting services like Growl/Snarl are better for it. – Arioch 'The Oct 18 '12 at 06:28
15

I am doing something similar in one of my applications and this function works for me in xp/vista/w7:

function ForceForegroundWindow(hwnd: THandle): Boolean;
const
  SPI_GETFOREGROUNDLOCKTIMEOUT = $2000;
  SPI_SETFOREGROUNDLOCKTIMEOUT = $2001;
var
  ForegroundThreadID: DWORD;
  ThisThreadID: DWORD;
  timeout: DWORD;
begin
  if IsIconic(hwnd) then ShowWindow(hwnd, SW_RESTORE);

  if GetForegroundWindow = hwnd then Result := True
  else
  begin
    // Windows 98/2000 doesn't want to foreground a window when some other
    // window has keyboard focus

    if ((Win32Platform = VER_PLATFORM_WIN32_NT) and (Win32MajorVersion > 4)) or
      ((Win32Platform = VER_PLATFORM_WIN32_WINDOWS) and
      ((Win32MajorVersion > 4) or ((Win32MajorVersion = 4) and
      (Win32MinorVersion > 0)))) then
    begin
      Result := False;
      ForegroundThreadID := GetWindowThreadProcessID(GetForegroundWindow, nil);
      ThisThreadID := GetWindowThreadPRocessId(hwnd, nil);
      if AttachThreadInput(ThisThreadID, ForegroundThreadID, True) then
      begin
        BringWindowToTop(hwnd); // IE 5.5 related hack
        SetForegroundWindow(hwnd);
        AttachThreadInput(ThisThreadID, ForegroundThreadID, False);
        Result := (GetForegroundWindow = hwnd);
      end;
      if not Result then
      begin
        // Code by Daniel P. Stasinski
        SystemParametersInfo(SPI_GETFOREGROUNDLOCKTIMEOUT, 0, @timeout, 0);
        SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, TObject(0),
          SPIF_SENDCHANGE);
        BringWindowToTop(hwnd); // IE 5.5 related hack
        SetForegroundWindow(hWnd);
        SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, TObject(timeout), SPIF_SENDCHANGE);
      end;
    end
    else
    begin
      BringWindowToTop(hwnd); // IE 5.5 related hack
      SetForegroundWindow(hwnd);
    end;

    Result := (GetForegroundWindow = hwnd);
  end;
end;

Another solution is not to steal focus and just put the window TOPMOST in the z buffer:

procedure ForceForegroundNoActivate(hWnd : THandle);

begin
 if IsIconic(Application.Handle) then
  ShowWindow(Application.Handle, SW_SHOWNOACTIVATE);
 SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE or SWP_NOACTIVATE or SWP_NOMOVE);
 SetWindowPos(hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE or SWP_NOACTIVATE or SWP_NOMOVE);
end;
whosrdaddy
  • 11,720
  • 4
  • 50
  • 99
  • 1
    This does not work on Win7 running as a non-admin, nor does it work properly on a machine running on a Win2008 domain, so it doesn't *always* work, which is why I gave the answer I did. :-) Of course, what do I (and the makers of the operating system itself) know? – Ken White Oct 18 '12 at 11:31
  • This, and similar approaches, never worked for me when my app was not active. – Erik Virtel Oct 18 '12 at 11:45
  • 2
    @KenWhite, This function is used in a enduser application and they have a mix of XP and W7 (UAC ON) and I haven't had any problems in restricted environments – whosrdaddy Oct 18 '12 at 11:56
  • 1
    one thing bugs me here: `SystemParametersInfo(SPI_SET...`. is it a good idea to set a system-wide parameters? will that even work for standard user in a UAC environment? – kobik Oct 18 '12 at 14:06
  • @kobik: `SPI_SETFOREGROUNDLOCKTIMEOUT` will work as long as the calling thread has rights to set the foreground window. I don't think UAC affects that. – Remy Lebeau Oct 18 '12 at 18:14
  • 2
    On a side note, the third parameter of `SystemParametersInfo()` is a `Pointer`, so the code should be using `nil` instead of `TObject(0)`. – Remy Lebeau Oct 18 '12 at 18:16
  • 1
    I'm up-voting this answer, unless someone else can *prove* this code does not work (I haven't tested this myself). @Ken, did you in fact tested this code with Win7 running as a non-admin? – kobik Oct 18 '12 at 22:58
  • @kobik: Yes, and it doesn't work **reliably**, as I said in the first five words of my own answer. I did *not* downvote this answer; I simply commented that it won't work properly on some Windows versions with some access restrictions, and that MS says it can't be done in the API documentation (which I both linked and quoted in my answer as well). – Ken White Oct 18 '12 at 23:03
  • @ken, I'm sorry to nag, but you stated before that "This *does not work* on Win7 running as a non-admin". now you say "reliably". what do you mean by "reliably"? does it not bring the window to the foreground? or is it not consistent or what? – kobik Oct 18 '12 at 23:12
  • @kobik: "Not reliably" means exactly that - it may or may not work. It does not work on mine. In addition (as I've now said three separate times), **Microsoft** says it will not work except under specific conditions. – Ken White Oct 18 '12 at 23:17
  • BIG Like for you! – SnowStorm May 01 '20 at 06:24
  • Unfortunately, this does not work on Windows 8.1 and Windows 10...see the latest documentation linked to in Ken White's answer. – AlainD Dec 15 '20 at 01:58
8

Assuming that there are no other StayOnTop applications running you can temporarily set your forms FormStyle to be fsStayOnTop and then do an Application Restore and BringToFront and then change the formstyle back to whatever it was.

tmpFormStyle := Application.MainForm.FormStyle;
Application.MainForm.FormStyle := fsStayOnTop;
Application.Restore; // optional
Application.BringToFront;
Application.MainForm.FormStyle := tmpFormStyle;

Again, this only works if there are no other stayontop applications.

Toby
  • 355
  • 6
  • 14
5

This should work even in Windows7+8 OS. Only requirement is an application itself can call functions. It's trickier to use an external app to set another process's window front.

Application.Restore; // unminimize window, makes no harm always call it
SetWindowPos(self.Handle, HWND_NOTOPMOST,0,0,0,0, SWP_NOMOVE or SWP_NOSIZE);
SetWindowPos(self.Handle, HWND_TOPMOST,0,0,0,0, SWP_NOMOVE or SWP_NOSIZE);
SetWindowPos(self.Handle, HWND_NOTOPMOST,0,0,0,0, SWP_SHOWWINDOW or SWP_NOMOVE or SWP_NOSIZE);

Edit Ok I found out one problem with this. Application is brought to front but focus is kept in an original application. Use this answer to fix the problem, its quite complex method but copypaste does the trick. Before calling ForceForegroundWindow(wnd) you may have to call Application.Restore unminimizing a window. https://stackoverflow.com/a/5885318/185565

Whome
  • 10,181
  • 6
  • 53
  • 65
4
function WinActivate(const AWinTitle: string): boolean;
var
  _WindowHandle: HWND;
begin
  Result := false;
  _WindowHandle := FindWindow(nil, PWideChar(AWinTitle));
  if _WindowHandle <> 0 then
  begin
    if IsIconic(_WindowHandle) then
      Result := ShowWindow(_WindowHandle, SW_RESTORE)
    else
      Result := SetForegroundWindow(_WindowHandle);
  end;
end;

if WinActivate(self.Caption) then
  ShowMessage('This works for me in Windows 10');
Edijs Kolesnikovičs
  • 1,627
  • 3
  • 18
  • 34
3

You could write an Executable to workaround that limitation. Take for example a simple (non-visual) application (basically a console app but without the {$APPTYPE CONSOLE}) with the only purpose to bring your desired window to front.

Your monitoring background application calls the helper app via command line call (e.g. ShellExecute) whenever your bringtofront action is needed. Since this app is becoming the foreground process, it is then able to bring other windows to front (SetForeGroundWindow). You could use FindWindow to get a handle on your target window or pass appropriate parameters when starting your helper app.

Erik Virtel
  • 810
  • 2
  • 9
  • 27
-2

Form.bringtofront; ?

Should work. If you put it in a timer it will stay at front.

Rudi
  • 43
  • 3