QB64 Phoenix Edition
For Pete -- Keeping a window topmost and active - Printable Version

+- QB64 Phoenix Edition (https://qb64phoenix.com/forum)
+-- Forum: QB64 Rising (https://qb64phoenix.com/forum/forumdisplay.php?fid=1)
+--- Forum: Code and Stuff (https://qb64phoenix.com/forum/forumdisplay.php?fid=3)
+---- Forum: Help Me! (https://qb64phoenix.com/forum/forumdisplay.php?fid=10)
+---- Thread: For Pete -- Keeping a window topmost and active (/showthread.php?tid=1091)



For Pete -- Keeping a window topmost and active - SMcNeill - 11-10-2022

So this is what I've came up with, and it works...

Code: (Select All)
'public domain

Const SWP_NOSIZE = &H0001 'ignores cx and cy size parameters
Const SWP_NOMOVE = &H0002 'ignores x and y position parameters
Const SWP_NOZORDER = &H0004 'keeps z order and ignores hWndInsertAfter parameter
Const SWP_NOREDRAW = &H0008 'does not redraw window changes
Const SWP_NOACTIVATE = &H0010 'does not activate window
Const SWP_FRAMECHANGED = &H0020
Const SWP_SHOWWINDOW = &H0040
Const SWP_HIDEWINDOW = &H0080
Const SWP_NOCOPYBITS = &H0100
Const SWP_NOOWNERZORDER = &H0200
Const SWP_NOSENDCHANGING = &H0400
Const SWP_DRAWFRAME = SWP_FRAMECHANGED
Const SWP_NOREPOSITION = SWP_NOOWNERZORDER
Const SWP_DEFERERASE = &H2000
Const SWP_ASYNCWINDOWPOS = &H4000
Const HWND_TOP = 0 'window at top of z order no focus
Const HWND_BOTTOM = 1 'window at bottom of z order no focus
Const HWND_TOPMOST = -1 'window above all others no focus unless active
Const HWND_NOTOPMOST = -2 'window below active no focus

Declare Dynamic Library "user32"
    '    Function FindWindowA%& (ByVal lpClassName%&, Byval lpWindowName%&)
    Function SetWindowPos& (ByVal hWnd%&, Byval hWndInsertAfter%&, Byval X&, Byval Y&, Byval cx&, Byval cy&, Byval uFlags~&)
    Function GetForegroundWindow%&
    Function SetForegroundWindow%& (ByVal hwnd As _Offset) 'set foreground window process(focus)
End Declare

Declare Dynamic Library "kernel32"
    Function GetLastError~& ()
End Declare

Dim hWnd As _Offset





_Title "This Window will always be on Top" 'any title

_Delay .5 'delay allows user to click focus on other windows
hWnd = _WindowHandle 'FindWindowA(0, _OFFSET(t))
If 0 = SetWindowPos(hWnd, HWND_TOPMOST, 200, 200, 0, 0, SWP_NOSIZE Or SWP_NOACTIVATE) Then
    Print "SetWindowPos failed. 0x" + LCase$(Hex$(GetLastError))
End If



Do
    Cls
    x%& = GetForegroundWindow%& 'find currently focused process handle
    Locate 3, 1
    Print "Program handle:"; hWnd; "Focus handle:"; x%&
    If x%& <> hWnd Then
        Print "Not Active"
        junk%& = SetForegroundWindow(hWnd)
    Else
        Print "Active"
    End If
    _Limit 30
Loop Until _KeyDown(27)



Now, the problem that I'm having is that this works -- but it only works *ONCE*.   Start it up, click off to a different window, it jerks focus back to itself as expected.   Click to a different window again, and...   it detects it's not active, but it won't jerk focus back to itself like it's supposed to.

Any of you windows API gurus out there see something obvious that I'm missing here?  (@Spriggsy)  Why's this work once and then decide, "Nah!  I don't wanna do that again??"  Any ideas at all on this one would be appreciated.  Wink


RE: For Pete -- Keeping a window topmost and active - Pete - 11-10-2022

You are getting the same type of results Spriggsy and I have experienced. The only consistent method is to minimize and restore. You would think whatever is working behind the scenes to accomplish that should have a code sequence available for use.

Pete


RE: For Pete -- Keeping a window topmost and active - SMcNeill - 11-10-2022

Try this @Pete:
Code: (Select All)
'public domain

Const SWP_NOSIZE = &H0001 'ignores cx and cy size parameters
Const SWP_NOMOVE = &H0002 'ignores x and y position parameters
Const SWP_NOZORDER = &H0004 'keeps z order and ignores hWndInsertAfter parameter
Const SWP_NOREDRAW = &H0008 'does not redraw window changes
Const SWP_NOACTIVATE = &H0010 'does not activate window
Const SWP_FRAMECHANGED = &H0020
Const SWP_SHOWWINDOW = &H0040
Const SWP_HIDEWINDOW = &H0080
Const SWP_NOCOPYBITS = &H0100
Const SWP_NOOWNERZORDER = &H0200
Const SWP_NOSENDCHANGING = &H0400
Const SWP_DRAWFRAME = SWP_FRAMECHANGED
Const SWP_NOREPOSITION = SWP_NOOWNERZORDER
Const SWP_DEFERERASE = &H2000
Const SWP_ASYNCWINDOWPOS = &H4000
Const HWND_TOP = 0 'window at top of z order no focus
Const HWND_BOTTOM = 1 'window at bottom of z order no focus
Const HWND_TOPMOST = -1 'window above all others no focus unless active
Const HWND_NOTOPMOST = -2 'window below active no focus

Declare Dynamic Library "user32"
    '    Function FindWindowA%& (ByVal lpClassName%&, Byval lpWindowName%&)
    Function SetWindowPos& (ByVal hWnd%&, Byval hWndInsertAfter%&, Byval X&, Byval Y&, Byval cx&, Byval cy&, Byval uFlags~&)
    Function GetForegroundWindow%&
    Function SetForegroundWindow%& (ByVal hwnd As _Offset) 'set foreground window process(focus)
End Declare

Declare Dynamic Library "kernel32"
    Function GetLastError~& ()
End Declare

Dim hWnd As _Offset





_Title "This Window will always be on Top" 'any title

_Delay .5 'delay allows user to click focus on other windows
hWnd = _WindowHandle 'FindWindowA(0, _OFFSET(t))
If 0 = SetWindowPos(hWnd, HWND_TOPMOST, 200, 200, 0, 0, SWP_NOSIZE Or SWP_NOACTIVATE) Then
    Print "SetWindowPos failed. 0x" + LCase$(Hex$(GetLastError))
End If



Do
    Cls
    x%& = GetForegroundWindow%& 'find currently focused process handle
    Locate 3, 1
    Print "Program handle:"; hWnd; "Focus handle:"; x%&
    If x%& <> hWnd Then
        Print "Not Active"
        _ScreenClick -32000, -32000
        junk%& = SetForegroundWindow(hWnd)
    Else
        Print "Active"
    End If
    _Limit 30
Loop Until _KeyDown(27)

A mouseclick offscreen does a good job of solving the issue (it's a limitation on setforegroundwindow and how often it works and under what conditions). So does a press of the ALT key before the function call, but _SCREENPRINT doesn't seem to easily accept one of those, and I didn't want to toss in any more API calls than were necessary.


RE: For Pete -- Keeping a window topmost and active - Pete - 11-10-2022

Yep, that's another trick, but _SCREENCLICK has lot of drawbacks. In your example, it hijacks the mouse pointer. I tried to open Notepad from the taskbar, but it wouldn't let me. It also highlighted parts on my open IDE. Now we could return the mouse cursor to the previous position, but that would still interrupt and grab control from the user on a fairly regular basis.

I have thought about using API to send an Alt + F4 and blocking that with a _EXIT routine, but that might not be enough to produce an active window effect, either. Sometimes recursive calls like that just result in an app crash, anyway.

Pete