Page 1 of 2

SetForegroundWindow from own app

Posted: Fri Apr 06, 2012 12:33 pm
by TI-994A
Hi guys! I've been trying unsuccessfully to get SetForegroundWindow() to work, and noticed from previous threads that it is a known problem. Can anyone please confirm if it is confined to any particular version of Windows; I've tried only on Windows XP and 7. Also, most of the posts seem to be referring to problems calling SetForegroundWindow() from another application; is there any way to make an application bring itself to the foreground?

Thank you.

Re: SetForegroundWindow from own app

Posted: Fri Apr 06, 2012 12:42 pm
by dobro
StickyWindow(#Fenetre, Etat) ??

or

Code: Select all

 Procedure   SetWindowPos(   WindowID   , State)  ; Permet de mettre la fenêtre en arrière, au premier plan,...
    ; #HWND_BOTTOM
    ; #HWND_TOPMOST
    ; #HWND_NOTOPMOST
    ; HWND_TOP
    SetWindowPos_ ( WindowID , State, 0, 0, 0, 0, #SWP_NOSIZE | #SWP_NOMOVE )
EndProcedure 

Re: SetForegroundWindow from own app

Posted: Fri Apr 06, 2012 12:58 pm
by infratec
Hi,

this works for me at XP

Code: Select all

WindowName.s = "Rechner"

hWnd = FindWindow_(#Null, WindowName)
If hWnd
  ShowWindow_(hWnd, #SW_RESTORE)
  SetForegroundWindow_(hWnd)
EndIf
Bernd

P.S.: You have to change the WindowName of course.

Oh, sorry, I didn't read enough.
For your own PB program use StickyWindow(#Window, Status).

Re: SetForegroundWindow from own app

Posted: Fri Apr 06, 2012 1:12 pm
by TI-994A
dobro wrote:StickyWindow(#Fenetre, Etat) ??
or

Code: Select all

SetWindowPos_(WindowID, State, 0, 0, 0, 0, #SWP_NOSIZE | #SWP_NOMOVE)
infratec wrote:For your own PB program use StickyWindow(#Window, Status).
Hi guys! Thanks for the quick replies.

StickyWindow() & SetWindowPos() with the #HWND_TOPMOST parameter works well, but I do not wish to have the window permanently topmost. I tried to make a double call to SetWindowPos(), once with #HWND_TOPMOST and then with #HWND_TOP and even #HWND_NOTOPMOST, but I can't seem to undo the Z-order.

Is it a Windows restriction, perhaps?

Re: SetForegroundWindow from own app

Posted: Fri Apr 06, 2012 1:58 pm
by RASHAD
I think that the next snippet is the best to bring any window to top
Feel free to test :)

Code: Select all

;***************** Find the Handle of The External Window ******************

Repeat
  hWnd = FindWindow_(0, "PureBasic Window")
Until hWnd

;***************************************************************************

OpenWindow(0,50,50,100,100,"Test 1",#PB_Window_SystemMenu)
ButtonGadget(0,10,10,50,25,"TEST 1")

Repeat
  Select WaitWindowEvent()
      
       Case #PB_Event_CloseWindow
             Q = 1
      
      Case #PB_Event_Gadget
          Select EventGadget()
            Case 0
               SendMessage_(WindowID(0), #WM_SYSCOMMAND, #SC_HOTKEY, hWnd)
                       
          EndSelect
             
  EndSelect 
Until Q = 1
End
Edit :Sorry I did not notice that you want to Bring a Window to top from that window itself
DoubleClick the SystemTray icon

Code: Select all

OpenWindow(0,50,50,100,100,"Test 1",#PB_Window_SystemMenu)
AddSysTrayIcon(0, WindowID(0),LoadImage(0, "g:\projects\icon\CdPlayer.ico"))


Repeat
  Select WaitWindowEvent()
      
      Case #PB_Event_CloseWindow
             Q = 1
             
      Case #PB_Event_SysTray
            Select EventType()
               Case #PB_EventType_LeftDoubleClick
                  SendMessage_(WindowID(0), #WM_SYSCOMMAND, #SC_HOTKEY, WindowID(0))
            EndSelect
  EndSelect

Until Q = 1
End

Re: SetForegroundWindow from own app

Posted: Fri Apr 06, 2012 2:10 pm
by J. Baker
Just enable StickyWindow() then disable it. Recently had to do the same myself. ;)

Re: SetForegroundWindow from own app

Posted: Fri Apr 06, 2012 2:35 pm
by TI-994A
RASHAD wrote:I think that the next snippet is the best to bring any window to top
Feel free to test :)

Code: Select all

SendMessage_(WindowID(0), #WM_SYSCOMMAND, #SC_HOTKEY, hWnd)
Hi RASHAD! Thanks for the code example. It works well when calling another window to the foreground, but it doesn't work when calling itself. Same problem with SetWindowPos()/#HWND_TOP and SetForegroundWindow().

So far, this is the only workaround that does the job:
J. Baker wrote:Just enable StickyWindow() then disable it. Recently had to do the same myself. ;)
Thanks a lot J. Baker! I've unsuccessfully tried this trick by calling SetWindowPos() first with #HWND_TOPMOST then with #HWND_NOTOPMOST, but didn't think to try it with PurBasic's StickyWindow(). Works perfectly.

If anyone knows the answer to this mystery, I'd still like to know what causes this.

Thank you, and everyone else for all your help!

Re: SetForegroundWindow from own app

Posted: Fri Apr 06, 2012 2:41 pm
by RASHAD
I just updated the previous post while you was posting :)

Re: SetForegroundWindow from own app

Posted: Fri Apr 06, 2012 3:08 pm
by TI-994A
Hello again, RASHAD. Thanks for the new example, which for some reason works when we click on the SysTray icon, but not programatically:

Code: Select all

OpenWindow(0, 50, 50, 100, 100, "Test 1", #PB_Window_SystemMenu)
Delay(3000)
SendMessage_(WindowID(0), #WM_SYSCOMMAND, #SC_HOTKEY, WindowID(0))
While WaitWindowEvent() ! #PB_Event_CloseWindow : CloseWindow : Wend
When we bring another window to the front, this window does not come back to the top after the timeout. This method works:

Code: Select all

OpenWindow(0, 50, 50, 100, 100, "Test 1", #PB_Window_SystemMenu)
Delay(3000)
StickyWindow(0, 1)
StickyWindow(0, 0)
While WaitWindowEvent() ! #PB_Event_CloseWindow : CloseWindow : Wend
However, I've noticed that even after disabling the StickyWindow(), the window doesn't get pushed back by certain applications, like NotePad, Windows File Explorer, and even the PureBasic IDE. If this window pops-up on any one of these application windows, it will not be pushed behind by clicking on them. It has to be clicked on, or moved, before losing its topmost Z-order.

Re: SetForegroundWindow from own app

Posted: Fri Apr 06, 2012 4:11 pm
by RASHAD

Code: Select all

Procedure ProcedureName(Parameter)
OpenWindow(1, 0, 0, 0, 0, "",#PB_Window_Invisible, WindowID(0))
AddWindowTimer(1,123,Parameter)
Repeat

     Event = WaitWindowEvent()
     
      If Event = #PB_Event_Timer
             BringWindowToTop_(WindowID(0))

      EndIf

Until Quit = 1

EndProcedure 

OpenWindow(0, 50, 50, 100, 100, "Test 1", #PB_Window_SystemMenu)
Thread = CreateThread(@ProcedureName(), 5000)

Repeat
  Select WaitWindowEvent()
      
      Case #PB_Event_CloseWindow
             Q = 1            
             
  EndSelect

Until Q = 1

Re: SetForegroundWindow from own app

Posted: Fri Apr 06, 2012 6:12 pm
by TI-994A
RASHAD wrote:

Code: Select all

Thread = CreateThread(@ProcedureName(), 5000)
Hi RASHAD. Ingenious workaround, calling it from another thread. For some reason a window is just not able to bring itself to the foreground. Thanks for that.

Earlier, I said that calling SetWindowPos() first with #HWND_TOPMOST then with #HWND_NOTOPMOST didn't work; actually it worked exactly as a double call to StickyWindow(). It too demonstrated the same erratic behaviour, maintaining its topmost Z-order even when the window immediately below it is clicked (except if Google Chrome is the window below it!).

Re: SetForegroundWindow from own app

Posted: Sat Apr 07, 2012 1:49 am
by Danilo
TI-994A wrote:is there any way to make an application bring itself to the foreground?
If another process is active, SetForegroundWindow_() does not disturb the user by just switching
windows. Instead it notifies the user with blinking taskbar entry.
So SetForegroundWindow_() only works if your own process and its threads is already active.

If you really want to disturb the user experience, you can use AttachThreadInput_() if another
process is currently in the foreground. Use SetForegroundWindowInternal().

Code: Select all

EnableExplicit
; http://msdn.microsoft.com/de-de/library/bb979463.aspx
;
; Verwenden Sie die Funktion SetForegroundWindowEx statt der 
; API Funktion SetForegroundWindow ausschließlich, wenn das 
; betroffene Fenster tatsächlich in den Vordergrund gebracht 
; werden muss. 
Procedure SetForegroundWindowEx(hWndWindow)
    ; Dient dem Setzen des Vordergrundfensters mit der Funktion 
    ; SetForegroundWindow, die sich unter neueren Windows-Versionen 
    ; anders verhält als unter Windows 95 und Windows NT 4.0. 
    ; Der Rückgabewert ist True, wenn das Fenster erfolgreich in den 
    ; Vordergrund gebracht werden konnte. 
    Protected SetForegroundWindowEx
    Protected lThreadForeWin.l  ; Thread-ID für das aktuelle Vordergrundfenster 
    Protected lThreadWindow.l   ; Thread-ID für das in hWndWindow spezifizierte 
                                ; Fenster, das in den Vordergrund des Desktops 
                                ; gebracht werden soll. 
    ; Falls das Fenster dem gleichen Thread wie das aktuelle 
    ; Vordergrundfenster angehört, ist kein Workaround erforderlich: 
    lThreadWindow  = GetWindowThreadProcessId_(hWndWindow, 0) 
    lThreadForeWin = GetWindowThreadProcessId_(GetForegroundWindow_(), 0) 
    If lThreadWindow = lThreadForeWin
        ; Vordergrundfenster und zu aktivierendes Fenster gehören zum 
        ; gleichen Thread. SteForegroundWindow allein reicht aus: 
        SetForegroundWindowEx = SetForegroundWindow_(hWndWindow)
    Else 
        ; Das Vordergrundfenster gehört zu einem anderen Thread als das 
        ; Fenster, das neues Vordergrundfenster werden soll. Mittels 
        ; AttachThreadInput erhaten wir kurzzeitig Zugriff auf die 
        ; Eingabeverarbeitung des Threads des Vordergrundfensters, 
        ; so dass SetForegroundWindow wie erwartet arbeitet: 
        AttachThreadInput_(lThreadForeWin, lThreadWindow, #True)
        SetForegroundWindowEx = SetForegroundWindow_(hWndWindow) 
        AttachThreadInput_(lThreadForeWin, lThreadWindow, #False) 
    EndIf
    ProcedureReturn SetForegroundWindowEx
EndProcedure




; http://www.codeproject.com/Tips/76427/How-to-bring-window-to-top-with-SetForegroundWindo
;
Procedure SetForegroundWindowInternal(hWnd)
    If Not IsWindow_(hWnd) : ProcedureReturn : EndIf
     
    ; relation time of SetForegroundWindow lock
    Protected lockTimeOut.l = 0;
    Protected hCurrWnd    = GetForegroundWindow_()
    Protected dwThisTID.l = GetCurrentThreadId_()
    Protected dwCurrTID.l = GetWindowThreadProcessId_(hCurrWnd,0)
    
    ; we need To bypass some limitations from Microsoft :)
    If (dwThisTID <> dwCurrTID)
        AttachThreadInput_(dwThisTID, dwCurrTID, #True);
         
        SystemParametersInfo_(#SPI_GETFOREGROUNDLOCKTIMEOUT,0,@lockTimeOut,0);
        SystemParametersInfo_(#SPI_SETFOREGROUNDLOCKTIMEOUT,0,0,#SPIF_SENDWININICHANGE | #SPIF_UPDATEINIFILE);
         
        #ASFW_ANY = -1
        AllowSetForegroundWindow_(#ASFW_ANY);
    EndIf
     
    SetForegroundWindow_(hWnd);
    ;SetFocus_(hWnd)
     
    If (dwThisTID <> dwCurrTID)
        SystemParametersInfo_(#SPI_SETFOREGROUNDLOCKTIMEOUT,0,lockTimeOut,#SPIF_SENDWININICHANGE | #SPIF_UPDATEINIFILE);
        AttachThreadInput_(dwThisTID, dwCurrTID, #False);
    EndIf
EndProcedure

Global myMsg = RegisterWindowMessage_("MyWindowMessage")

Procedure run(v.i)
    Protected program
    program = RunProgram("calc.exe","","",#PB_Program_Open)
    
    While ProgramRunning(program)
      Delay(100)
    Wend
    CloseProgram(program)
    PostMessage_(v,myMsg,0,0)
EndProcedure

Procedure winthread(v)
    Protected win
    win = OpenWindow(#PB_Any,100,100,500,300,"main")
    CreateThread(@run(),WindowID(win))
    Repeat
        Select WaitWindowEvent()
            Case #PB_Event_CloseWindow
               Break
            Case myMsg
               ;SetForegroundWindowEx(WindowID(win))
               SetForegroundWindowInternal(WindowID(win))
        EndSelect
    ForEver
    CloseWindow(win)
EndProcedure

Define t
t = CreateThread(@winthread(),0)
WaitThread(t)
The example starts calc.exe and when it is finished, it activates your own window.

For your own application just use the procedure SetForegroundWindowInternal().
Please use this only if you really need it. It is very disturbing for the user if (s)he is
doing some work and other windows bring itself into the foreground all the time.
That's the reason Microsoft changed the function - SetForegroundWindow was misused too often
and disturbed the user experience.

Re: SetForegroundWindow from own app

Posted: Sat Apr 07, 2012 5:51 am
by TI-994A
Danilo wrote:
TI-994A wrote:is there any way to make an application bring itself to the foreground?
If another process is active, SetForegroundWindow_() does not disturb the user by just switching windows. Instead it notifies the user with blinking taskbar entry.
Hi Danilo! Thanks for the great code. I've not had a chance to study it yet, but my initial tests had these results:

1. the code executed perfectly when compiled, but crashed on Windows XP Professional if run from the PureBasic IDE

2. SetForegroundWindow() works perfectly on Windows XP Professional, with no annoying blinking taskbar button.

Could anyone else please confirm this?

Re: SetForegroundWindow from own app

Posted: Sat Apr 07, 2012 6:35 am
by Danilo
TI-994A wrote:1. the code executed perfectly when compiled, but crashed on Windows XP Professional if run from the PureBasic IDE
Known problem with the IDE, we discussed it a hundred times with freak, search the forum for it.
Do not use the IDE as foreground window when testing, use anything but the PB IDE.
Alternatively out-comment the first AttachThreadInput_(dwThisTID, dwCurrTID, #True) that causes the IDE crash.
TI-994A wrote:2. SetForegroundWindow() works perfectly on Windows XP Professional, with no annoying blinking taskbar button.
It flashes only if the current foreground window is another process, see SetForegroundWindow
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.

Re: SetForegroundWindow from own app

Posted: Sat Apr 07, 2012 7:01 am
by TI-994A
Danilo wrote:Known problem with the IDE, we discussed it a hundred times with freak, search the forum for it. Do not use the IDE as foreground window when testing, use anything but the PB IDE.
You're right; very perculiar behaviour. Thanks for pointing that out.
Danilo wrote:It flashes only if the current foreground window is another process, see SetForegroundWindow
That's what it says, but here on my work computer, running Windows XP Professional SP3, I can't seem to get that behaviour. Even with this simple routine, the window pops right up to the front and gets the focus, regardless of what application window may be in the foreground; no flashing whatsoever.

Code: Select all

wFlags = #PB_Window_SystemMenu | #PB_Window_ScreenCentered
OpenWindow(0, 50, 50, 300, 100, "Set Foreground Test", wFlags)
Delay(3000)
SetForegroundWindow_(WindowID(0))
While WaitWindowEvent() ! #PB_Event_CloseWindow : CloseWindow : Wend
At first, I suspected that it might have something to do with the registry settings, ForegroundLockTimeout & ForegroundFlashCount; but even after setting them to their default values, SetForegroundWindow() still works every time.

It'd be really great if someone else can confirm this.