* * *

Author Topic: Setting Focus on own Application  (Read 1769 times)

Nimral

  • Jr. Member
  • **
  • Posts: 78
Setting Focus on own Application
« on: December 30, 2016, 05:28:53 pm »
Hi experts,

I am seeking your help to overcome the following problem: I used the UniqueInstance library to ensure that only one instance of my application runs. In my experience, the main reason for a user starting a second instance is that the program is either hidden behind something else, or minimized to the taskbar. So to make uniqueinstance work like one can expect, I used the OnOtherInstance Event to bring the running instance back on screen.

Unfortunately, Microsoft has made "stealing" the focus from an application hard, even if, in my case, this is the best solution for a common user problem. Without a little hack with formstyle my application would neither come back from the Taskbar, nor would it get into the foreground, instead the Taskbar icon would start to blink.

The code I have now brings my application to the foregound, may it be minimized or just hidden behind another application, but it won't get the focus.

This problem has been widely described for Delphi, and there were two solutions suggested, using "SetActiveWindow" or "SetForegroundWindow" (Unit: LCLIntf). Both of them don't seem to do anything.

Question: how can I get my application window focused after I restored it and moved it to the foreground?

Any ideas welcome,

Armin.

Code: Pascal  [Select]
  1. procedure TForm1.Form1_OnCreate(Sender: TObject);
  2.  
  3. begin
  4.   // Application.restore hack #1
  5.   Self.formstyle := fsStayOnTop;
  6. end;
  7.  
  8. procedure TForm1.UniqueInstance1_OnOtherInstance(Sender: TObject;
  9.   ParamCount: Integer; Parameters: array of String);
  10.  
  11. begin
  12.   // Revert Application.restore hack #1
  13.   Self.formstyle := fsNormal;
  14.   // Restore and bring to front - works
  15.   Application.Restore;
  16.   Application.BringToFront;
  17.   // move focus - neither call does do anything
  18.   SetActiveWindow(Application.MainForm.Handle);
  19.   SetForegroundWindow(Application.MainForm.Handle);
  20. end;


Using Lazarus 1.6.2 on Windows 10/7/XP, VMWare Workstation 12

Cyrax

  • Hero Member
  • *****
  • Posts: 514
Re: Setting Focus on own Application
« Reply #1 on: January 01, 2017, 04:28:11 pm »
How about change
Code: Pascal  [Select]
  1. SetActiveWindow(Application.MainForm.Handle);
to
Code: Pascal  [Select]
  1. SetActiveWindow(Self.Handle);
?

Your TForm1 class instance is normally the main form.

Nimral

  • Jr. Member
  • **
  • Posts: 78
Re: Setting Focus on own Application
« Reply #2 on: January 08, 2017, 07:00:43 pm »
Hm, seems this doesn't do anything. If the Application is minimized, it stays minimized, if it is hidden behind some other application, it does not come to the foreground.

Tested with and without the "Formstyle" Hack, just to make sure.

Did you ever test this with Windows 10, or any other Windows Version, and find your code working?

Armin
« Last Edit: January 08, 2017, 07:02:16 pm by Nimral »
Using Lazarus 1.6.2 on Windows 10/7/XP, VMWare Workstation 12

Nimral

  • Jr. Member
  • **
  • Posts: 78
Re: Setting Focus on own Application
« Reply #3 on: January 08, 2017, 07:38:05 pm »
Finally, I got a working solution (Windows 7 and Windows 10 tested OK).

If one looks at the documenations for the SetForegroundWindow in Microsft MSDN, there is a "Remarks" section with a list of restrictions, when the command would work, and when it would not. There is a note that SetForegroundWindow would work, if "The process received the last input event." So I send my own process a dummy input, and gee, now everything works like expected, when OnOtherInstance fires, my application will either restore itself from the Taskbar, and/or come to the foreground and grab the input focus.

Here is the code:

Code: Pascal  [Select]
  1. procedure TForm1.Form1_OnCreate(Sender: TObject);
  2.  
  3. begin
  4.     // Application.restore hack #1
  5.   Self.formstyle := fsStayOnTop;
  6. end;
  7.  
  8. procedure TForm1.UniqueInstance1_OnOtherInstance(Sender: TObject;
  9.   ParamCount: Integer; Parameters: array of String);
  10.  
  11. var
  12.   I: LPInput;
  13.  
  14. begin
  15.   // Revert Application.restore hack #1
  16.   Self.formstyle := fsNormal;
  17.   // Restore and bring to front
  18.   Application.Restore;
  19.   Application.BringToFront;
  20.   // Grab focus
  21.   I := nil;
  22.   try
  23.     // Hack #2: send myself some dummy input
  24.     GetMem(I,SizeOf(Input));
  25.     FillChar(I^, SizeOf(Input),$00); // empty input structure
  26.     SendInput(1, I, SizeOf(Input));
  27.     SetForegroundWindow(Application.MainForm.Handle);
  28.   finally
  29.     if I <> nil then FreeMem(I,SizeOf(Input));
  30.   end;
  31. end;
  32.  

Summary: Windows does not honor the formstyle attribute, so at first glance it is pointless to set it to any value. Application.Restore will just flash the application icon but not restore it, and Application.BringToFront and SetForegroundWindow (grab Focus) won't work at all either.

Hack #1: If the formstyle attribute is set to fsStayOnTop initally, the Window won't stay on top, but if the attribute is set to fsNormal later, Application.Restore and Application.BringToFront work like expected.

Hack #2: the "dummy input" hack entitles my App to call SetForegroundWindow to grab the focus.

Tested and found working with Windows 10 x86 and Windows 7 x64, Lazarus 1.6.

Armin.
« Last Edit: January 09, 2017, 01:57:51 pm by Nimral »
Using Lazarus 1.6.2 on Windows 10/7/XP, VMWare Workstation 12

ASerge

  • Sr. Member
  • ****
  • Posts: 340
Re: Setting Focus on own Application
« Reply #4 on: January 10, 2017, 01:33:19 am »
According to the documentation https://msdn.microsoft.com/en-us/library/windows/desktop/ms633539%28v=vs.85%29.aspx
Quote
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 process is being debugged.
    The foreground process is not a Modern Application or the Start Screen.
    The foreground is not locked (see LockSetForegroundWindow).
    The foreground lock time-out has expired (see SPI_GETFOREGROUNDLOCKTIMEOUT in SystemParametersInfo).
    No menus are active.
You do third case.
Better, in my opinion, to fix UniqueInstance:
Code: Pascal  [Select]
  1. {$IFDEF WINDOWS}
  2. function AllowSetForegroundWindow(dwProcessId: LongInt): BOOL; stdcall;
  3.   external user32;
  4. {$ENDIF}
  5. //...
  6. procedure TUniqueInstance.Loaded;
  7. //...
  8.         IPCClient.Active := True;
  9.         {$IFDEF WINDOWS} AllowSetForegroundWindow(-1); {$ENDIF} // Lines added
  10. //...
Because the second instance corresponds to the requirements of case 1, it allows any app to make SetForegroundWindow, for example first instance.

Nimral

  • Jr. Member
  • **
  • Posts: 78
Re: Setting Focus on own Application
« Reply #5 on: January 10, 2017, 11:27:40 pm »
Hi Serge,

thanks for that nice piece of elegant code ... I modified UniqueInstance like you suggested, and it works like a charm, without all that "hack" stuff I had before.

Unfortunately I spent today having another weird WIndows problem (did I mention that at times I really hate this time wasting, bloated, cryptic, awful, ill documented,  >:( >:( >:().

A problem occurs if the first instance has a modal MessageBox open, neither I can bring it to the front, not can I change focus to the messagebox. there are the variants I have tried:

Code: Pascal  [Select]
  1.      // restore from taskbar
  2.      Application.Restore;
  3.      // bring to front
  4.      Application.BringToFront;
  5.      // give focus to topmost window
  6.      // hMyWindow := GetForegroundWindow(); returns 0 if messagebox on top
  7.      // hMyWindow := GetActiveWindow(); returns other instance handle
  8.      hTopWindow := GetTopWindow(hMyWindow); // returns handle of explorer
  9.      if hTopWindow <> NULL then SetForegroundWindow(hTopWindow);
  10.  

Do you have an idea what I can do? Think it should be possible to code this, since an application having a modal dialog box open can be brought to the foreground using Task Manager (or Alt-Tab), so there must be a way to find the Messagebox and pass the focus to it.

Any clues welcome,

Armin.
« Last Edit: January 10, 2017, 11:30:37 pm by Nimral »
Using Lazarus 1.6.2 on Windows 10/7/XP, VMWare Workstation 12

ASerge

  • Sr. Member
  • ****
  • Posts: 340
Re: Setting Focus on own Application
« Reply #6 on: January 11, 2017, 08:47:12 pm »
A problem occurs if the first instance has a modal MessageBox open, neither I can bring it to the front, not can I change focus to the messagebox. there are the variants I have tried:
Try this code:
Code: Pascal  [Select]
  1. uses InterfaceBase;
  2. //...
  3. var
  4.   AppWnd: HWND;
  5. //...
  6.   AppWnd := Widgetset.AppHandle;
  7.   if IsIconic(AppWnd) then
  8.     Application.Restore;
  9.   SetForegroundWindow(AppWnd);
  10.  

Nimral

  • Jr. Member
  • **
  • Posts: 78
Re: Setting Focus on own Application
« Reply #7 on: January 17, 2017, 09:00:22 pm »
Hi Serge,

putting all your valuable input together, I did, indeed, get my perfect version of UniqueInstance running.

Thanks very much for your help,

Armin.
Using Lazarus 1.6.2 on Windows 10/7/XP, VMWare Workstation 12

 

Recent

Get Lazarus at SourceForge.net. Fast, secure and Free Open Source software downloads Open Hub project report for Lazarus