Author Topic: Persistence won't work more than once. (SOLVED)  (Read 3877 times)

0 Members and 1 Guest are viewing this topic.

This topic contains a post which is marked as Best Answer. Press here if you would like to see it.

Offline Pete

  • Forum Resident
  • Posts: 2361
  • Cuz I sez so, varmint!
    • View Profile
Persistence won't work more than once. (SOLVED)
« on: May 19, 2019, 02:28:50 pm »
I'm experimenting with API windows persistence routine. Funny thing, it works, but only once. I slowed the loop down to see the handles returned. It brings the QB64 window back one time, but if you click on anther window a second time, the QB64 window can no longer be brought to the front by the program.

1) Start program.

2) Click the IDE or some other program to put something else in focus or over the QB64 app.

3) Wait a second, and the app will beep and come back in front, in focus.

4) Redo step 2, but this time the app just beeps every second, but does not set the foreground, and therefore the QB64 app will not come back into focus, unless you manually click on it.

It's not a bug in the new statement, _WINDOWHANDLE, as I tried it the old way, with API call FindWindowA(), and it acts exactly the same. So is there some clear call or some reset that is missing in my code?

 - Pete

Code: QB64: [Select]
  1. title$ = "Persistence Test" 'Title of program window
  2. _TITLE title$ 'Set program title
  3.  
  4.     FUNCTION FindWindowA& (BYVAL ClassName AS _OFFSET, WindowName$) 'find process handle by title
  5.     FUNCTION ShowWindow& (BYVAL hwnd AS _OFFSET, BYVAL nCmdShow AS LONG) 'maximize process
  6.     FUNCTION GetForegroundWindow& 'find currently focused process handle
  7.     FUNCTION SetForegroundWindow& (BYVAL hwnd AS _OFFSET) 'set foreground window process(focus)
  8.  
  9. DIM Myhwnd AS LONG ' Get hWnd value
  10. Myhwnd = _WINDOWHANDLE
  11.  
  12.  
  13.     _LIMIT 30
  14.     PRINT FGwin&, Myhwnd
  15.     FGwin& = GetForegroundWindow&
  16.     _DELAY 1
  17.     IF Myhwnd <> FGwin& THEN ' QB64 no longer in focus.
  18.         BEEP
  19.         x& = SetForegroundWindow&(Myhwnd) ' Bring to QB64 to front and in focus.
  20.         _DELAY 1
  21.     END IF
  22.     IF INKEY$ = CHR$(27) THEN SYSTEM
  23.  
« Last Edit: May 22, 2019, 01:19:21 pm by Pete »
Want to learn how to write code on cave walls? https://www.tapatalk.com/groups/qbasic/qbasic-f1/

FellippeHeitor

  • Guest
Re: Persistence won't work more than once.
« Reply #1 on: May 19, 2019, 02:59:31 pm »
Does x& return something different when focus is properly set after line 24? You could make that a loop and call the api until x& is the proper expected value.

Offline Pete

  • Forum Resident
  • Posts: 2361
  • Cuz I sez so, varmint!
    • View Profile
Re: Persistence won't work more than once.
« Reply #2 on: May 19, 2019, 05:31:06 pm »
x& is zero when false and 1 when true. It return 1 for the first time it is recalled to the front, but... it fails to do anything but return zero if you try to recall it more than once. So even when I put it in a second dedicated loop, it just kept coming up zero. When I minimized the app window in front of it, x& returned 1 and exited the loop. I shouldn't have to do that though. It should just resume focus.

The icon also flashes in my system tray, while it is misbehaving.

A way around it is to minimize the window, and maximize it. That automatically restores focus every time, but by default, the window is always restored at desktop position 0, 0. You have to put about a 1/2 second delay to _Screen move it back to center, and that looks like a bug, so no to that method.

Can anyone please run the code I posted, just to verify it acts the same on some other computer? Just use these steps:

1) Open your browser and make it full screen.
2) Minimize browser to the system tray.
3) Run my QB64 code
4) Click your browser system tray icon and make your browser full screen again, overlapping the Qb64 app.

The QB64 program should beep, and then appear in front of your browser.

Now...

5) Click your browser to place it in front of the QB64 window.

If it misbehaves, like mine, the QB64 app will NOT reappear again, but will slowly beep in a loop an flash in your system tray. However, if it reappears in front of your full screen browser again, please try and click the browser to focus it, and see if the QB64 app beeps and regains focus. Please try this a few times. If it keeps reappearing on your computer, then please let me know. Maybe I have to reboot my system or something.

Pete

Want to learn how to write code on cave walls? https://www.tapatalk.com/groups/qbasic/qbasic-f1/

Offline Pete

  • Forum Resident
  • Posts: 2361
  • Cuz I sez so, varmint!
    • View Profile
Re: Persistence won't work more than once.
« Reply #3 on: May 19, 2019, 06:00:31 pm »
It looks like some quirky API built in function I'm not going to like. From my API research I see...

"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."

That is exactly what I'm experiencing.

So if this other call below can be integrated, maybe I can still end up getting the persistency I want for this app...

"A process that can set the foreground window can enable another process to set the foreground window by calling the AllowSetForegroundWindow function. The process specified by dwProcessId loses the ability to set the foreground window the next time the user generates input, unless the input is directed at that process, or the next time a process calls AllowSetForegroundWindow, unless that process is specified."

It's always something!

Pete
Want to learn how to write code on cave walls? https://www.tapatalk.com/groups/qbasic/qbasic-f1/

Offline Pete

  • Forum Resident
  • Posts: 2361
  • Cuz I sez so, varmint!
    • View Profile
Re: Persistence won't work more than once.
« Reply #4 on: May 19, 2019, 07:26:59 pm »
Well no love here. :( I think I have the parameters correct, because the variable for the process id returns 1 for true, but it still won't do more than the one focus to foreground. Anyway, here is the code I put together to try. If anyone an find some error, let me know. I was a little suspicious about needing the kernel lib. You would think it would all be processed in user32, but whatever...

If you try it, follow the steps in the previous post. No instructions on screen.

Code: QB64: [Select]
  1. 101
  2.     FUNCTION GetCurrentProcessId& ()
  3.  
  4.     FUNCTION FindWindowA& (BYVAL ClassName AS _OFFSET, WindowName$) 'find process handle by title
  5.     FUNCTION ShowWindow& (BYVAL hwnd AS _OFFSET, BYVAL nCmdShow AS LONG) 'maximize process
  6.     FUNCTION AllowSetForegroundWindow& (BYVAL dwProcessId AS LONG)
  7.     FUNCTION GetForegroundWindow& 'find currently focused process handle
  8.     FUNCTION SetForegroundWindow& (BYVAL hwnd AS _OFFSET) 'set foreground window process(focus)
  9.  
  10. title$ = "Persistence Test" 'Title of program window
  11. _TITLE title$ 'Set program title
  12.  
  13. DIM Myhwnd AS LONG ' Get hWnd value
  14. Myhwnd = _WINDOWHANDLE
  15.  
  16. WIDTH 110, 25
  17. _DELAY .1 ' Delay needed to process middle on width statement.
  18. dwProcessId& = GetCurrentProcessId&
  19.  
  20.     _LIMIT 30
  21.     FGwin& = GetForegroundWindow&
  22.  
  23.     _DELAY .75
  24.  
  25.     IF Myhwnd <> FGwin& THEN ' QB64 no longer in focus.
  26.         BEEP
  27.         'x& = ShowWindow&(Myhwnd, 2): _DELAY .1:x& = ShowWindow&(Myhwnd, 3): _DELAY .1
  28.         'LOCATE -1, -1 ' Sets focus if an error occurs!
  29.         x& = SetForegroundWindow&(Myhwnd) ' Bring to QB64 to front and in focus.
  30.         _DELAY .1
  31.         y& = AllowSetForegroundWindow&(dwProcessId&): _DELAY .1 ' Doesn't work.
  32.         PRINT "x& ="; x&, "y& ="; y&, "dwProcessId& ="; dwProcessId&, "Myhwnd ="; Myhwnd, "FGwin& ="; FGwin&
  33.         _DELAY .1
  34.     END IF
  35.     IF INKEY$ = CHR$(27) THEN SYSTEM
  36.  

Pete
Want to learn how to write code on cave walls? https://www.tapatalk.com/groups/qbasic/qbasic-f1/

FellippeHeitor

  • Guest
Re: Persistence won't work more than once.
« Reply #5 on: May 20, 2019, 08:16:27 am »
You may be misinterpreting your results. Most API calls return a 0 when the operation is successful.

Offline Pete

  • Forum Resident
  • Posts: 2361
  • Cuz I sez so, varmint!
    • View Profile
Re: Persistence won't work more than once.
« Reply #6 on: May 20, 2019, 12:13:41 pm »
Well, that's contrary to the API examples I've seen. Anyway, here...

Form: https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-allowsetforegroundwindow

Return Value

Type: Type: BOOL

If the function succeeds, the return value is nonzero.

If the function fails, the return value is zero. The function will fail if the calling process cannot set the foreground window. To get extended error information, call GetLastError.

Besides, if you run the code I posted, you will see the "1" returned when it works. So unfortunately, it looks like we are getting nowhere here, but hey, this isn't an API forum, so...

I just don't know enough about API calls to figure this one out on my own, but I took a pretty good stab at it. Maybe it's a different call, maybe the other windows lock out the focus, too many maybe's. There must be a way to get around that one and done API rule for placing a program window in the foreground. I think Rob figured it out, as if you throw an error, it will regain focus every time an error is thrown, and no, you can't just error trap it, it has to present the QB64 error continue message.  Maybe I'll revisit this sometime and dig into the QB64 source code. Microsoft is just exasperating in the way they try to over-control just about everything. I remember when Solitaire talked me into downloading the free .NET suite. I was so disappointed, I uninstalled it before the "Click OK to begin..." message hit the screen. Well, that's what I like to tell myself.

Pete
Want to learn how to write code on cave walls? https://www.tapatalk.com/groups/qbasic/qbasic-f1/

Offline Pete

  • Forum Resident
  • Posts: 2361
  • Cuz I sez so, varmint!
    • View Profile
Re: Persistence won't work more than once.
« Reply #7 on: May 20, 2019, 09:29:40 pm »
Well no luck with  LockSetForegroundWindow or AllowSetForegroundWindow. I've seen two other posts from users who claimed these fail to work, as well. I get both to return the correct value, but it does not re-focus the window, or change the behavior one bit, so maybe something is missing or these are not the right choices.

So for now it looks like I'm stuck with the Mickey Mouse method that minimizes and then maximizes a window, and sticks it in the upper left corner regardless of where I want it. (Sure, it could be re-positioned with _SCREENMOVE, but that takes on considerable delay after an API call...) That's one thing I wish QB64 didn't have in common with QBasic, Mickey Mouse solutions. Oh well...

Pete
« Last Edit: May 21, 2019, 04:46:51 pm by Pete »
Want to learn how to write code on cave walls? https://www.tapatalk.com/groups/qbasic/qbasic-f1/

Offline Pete

  • Forum Resident
  • Posts: 2361
  • Cuz I sez so, varmint!
    • View Profile
Re: Persistence won't work more than once.
« Reply #8 on: May 21, 2019, 04:15:47 pm »
Persistence pays off, well, sort of...

M-i-c... k-e-y... M-o-u-s-e

Code: QB64: [Select]
  1. DEFINT A-Z
  2.  
  3.     FUNCTION ShowWindow& (BYVAL hwnd AS _OFFSET, BYVAL nCmdShow AS LONG) 'maximize process
  4.     FUNCTION GetForegroundWindow& 'find currently focused process handle
  5.     FUNCTION SetForegroundWindow& (BYVAL hwnd AS _OFFSET) 'set foreground window process(focus)
  6.     FUNCTION SetLayeredWindowAttributes& (BYVAL hwnd AS LONG, BYVAL crKey AS LONG, BYVAL bAlpha AS _UNSIGNED _BYTE, BYVAL dwFlags AS LONG)
  7.     FUNCTION GetWindowLong& ALIAS "GetWindowLongA" (BYVAL hwnd AS LONG, BYVAL nIndex AS LONG)
  8.     FUNCTION SetWindowLong& ALIAS "SetWindowLongA" (BYVAL hwnd AS LONG, BYVAL nIndex AS LONG, BYVAL dwNewLong AS LONG)
  9.  
  10. ' Needed for acquiring the hWnd of the window
  11. DIM Myhwnd AS LONG ' Get hWnd value
  12. _TITLE "Translucent window test"
  13. Myhwnd = _WINDOWHANDLE
  14.  
  15. ' Set screen
  16. s& = _NEWIMAGE(300, 400, 32)
  17.  
  18. _SCREENMOVE 800, 50
  19. ' Main loop
  20. Level = 175
  21. SetWindowOpacity Myhwnd, Level
  22. LOCATE 1, 1: PRINT "Press F1/F2 to change opacity:"; Level;
  23. LOCATE 2, 1: PRINT "Drag to change window size:"; wdth; hght
  24. LOCATE 4, 1
  25. y = 3: x = 1
  26.     _LIMIT 30
  27.         IF oldimage& <> 0 THEN
  28.             oldimage& = s&
  29.             s& = _NEWIMAGE(_RESIZEWIDTH, _RESIZEHEIGHT, 32)
  30.             SCREEN s&
  31.             _FREEIMAGE oldimage&
  32.             wdth = _RESIZEWIDTH: hght = _RESIZEHEIGHT
  33.             LOCATE 1, 1: PRINT "Press F1/F2 to change opacity:"; Level;
  34.             LOCATE 2, 1: PRINT "Drag to change window size:"; wdth; hght
  35.             LOCATE 4, 1
  36.             PRINT mystring$;
  37.         ELSE
  38.             oldimage& = s&
  39.         END IF
  40.     END IF
  41.  
  42.     FGwin& = GetForegroundWindow&
  43.  
  44.     IF Myhwnd <> FGwin& THEN ' QB64 no longer in focus.
  45.         WHILE _MOUSEINPUT: WEND
  46.         SetWindowOpacity Myhwnd, 0
  47.         _DELAY .02
  48.         x& = ShowWindow&(Myhwnd, 2)
  49.         _DELAY .02
  50.         x& = ShowWindow&(Myhwnd, 10)
  51.         DO: _LIMIT 30: LOOP UNTIL Myhwnd = GetForegroundWindow&
  52.         SetWindowOpacity Myhwnd, Level
  53.         oldimage& = 0
  54.     END IF
  55.  
  56.     b$ = INKEY$
  57.     IF LEN(b$) THEN
  58.         IF b$ = CHR$(27) THEN SYSTEM
  59.         IF LEN(b$) = 2 THEN
  60.             IF b$ = CHR$(0) + CHR$(59) AND Level < 255 THEN Level = Level + 1: SetWindowOpacity Myhwnd, Level
  61.             IF b$ = CHR$(0) + CHR$(60) AND Level > 0 THEN Level = Level - 1: SetWindowOpacity Myhwnd, Level
  62.             yy = CSRLIN: xx = POS(0): LOCATE 1, 1: PRINT "Press F1/F2 to change opacity"; Level;: LOCATE yy, xx
  63.         ELSE
  64.             PRINT b$;
  65.             mystring$ = mystring$ + b$
  66.         END IF
  67.     END IF
  68. '====================================================================\
  69. SUB SetWindowOpacity (hwnd AS LONG, Level)
  70. DIM Msg AS LONG
  71. CONST G = -20
  72. CONST LWA_ALPHA = &H2
  73. CONST WS_EX_LAYERED = &H80000
  74. Msg = GetWindowLong(hwnd, G)
  75. Msg = Msg OR WS_EX_LAYERED
  76. action = SetWindowLong(hwnd, G, Msg)
  77. action = SetLayeredWindowAttributes(hwnd, 0, Level, LWA_ALPHA)
  78.  

Now what I don't like is the fact it isn't bulletproof, and I don't know if I can get it bulletproof. If you left click another app, like some sort of crazed idiot, it will error out. The window size gets messed up, even though you do nothing to resize it. At the same time, it fails to come to the front. Maybe I just need to find a way to clear or control the Mouse buffer? Well, that's for another day.

Pete
« Last Edit: May 21, 2019, 04:53:50 pm by Pete »
Want to learn how to write code on cave walls? https://www.tapatalk.com/groups/qbasic/qbasic-f1/

Marked as best answer by Pete on May 22, 2019, 09:18:37 am

Offline visionmercer

  • Newbie
  • Posts: 8
    • View Profile
Re: Persistence won't work more than once.
« Reply #9 on: May 22, 2019, 03:49:00 am »

Offline Pete

  • Forum Resident
  • Posts: 2361
  • Cuz I sez so, varmint!
    • View Profile
Re: Persistence won't work more than once.
« Reply #10 on: May 22, 2019, 01:06:21 pm »
Well, it's like the old saying goes... Give a man a fish, and you feed him for a day. Give Pete a fish, and it takes him 2 hours to figure out it's a fish!

In other word, woooooohoooo! it worked, but I had to grind it out a bit to figure out the correct way to pass and dim the parameters. Here is the cut down code to just identify the window and keep it persistent. Notice by calling the setwinpos function, the coordinates don't have to be assigned when you literally add in the hwndinsertafter _OFFEST values. It simply recalls the last size and position of the window, and the show parameter displays it in that relationship with the hwnd_topmost _OFFSET with a value of -1, keeping it always on top. Way cool!

Persistence pays _OFFSET

Thanks a metric ton, VM!

 - Pete

Code: QB64: [Select]
  1. ' Run the code and click on any other open windows. The QB64 app will stay on top. Click "x" or press Esc to terminate demo.
  2. CONST HWND_TOPMOST%& = -1
  3. CONST SWP_NOSIZE%& = &H1
  4. CONST SWP_NOMOVE%& = &H2
  5. CONST SWP_SHOWWINDOW%& = &H40
  6.  
  7.     FUNCTION SetWindowPos& (BYVAL hWnd AS LONG, BYVAL hWndInsertAfter AS _OFFSET, BYVAL X AS INTEGER, BYVAL Y AS INTEGER, BYVAL cx AS INTEGER, BYVAL cy AS INTEGER, BYVAL uFlags AS _OFFSET)
  8.     FUNCTION GetForegroundWindow& 'find currently focused process handle
  9.  
  10. ' Needed for acquiring the hWnd of the window
  11. DIM Myhwnd AS LONG ' Get hWnd value
  12. _TITLE "Translucent window test"
  13. Myhwnd = _WINDOWHANDLE
  14. wdth = 300: hght = 400
  15.  
  16. ' Set screen
  17. s& = _NEWIMAGE(wdth, hght, 32)
  18.  
  19. _SCREENMOVE 800, 50
  20. ' Main loop
  21. Level = 175
  22.  
  23.     _LIMIT 30
  24.     FGwin& = GetForegroundWindow&
  25.  
  26.     IF Myhwnd <> FGwin& THEN ' QB64 no longer in focus.
  27.         WHILE _MOUSEINPUT: WEND
  28.         y& = SetWindowPos&(Myhwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE + SWP_NOSIZE + SWP_SHOWWINDOW)
  29.         PRINT y&, Myhwnd, HWND_TOPMOST%&, SWP_NOSIZE%& + SWP_NOMOVE%& + SWP_SHOWWINDOW%&
  30.         DO: _LIMIT 30: LOOP UNTIL Myhwnd = GetForegroundWindow&
  31.     END IF
  32.     IF INKEY$ = CHR$(27) THEN SYSTEM
  33.  
Want to learn how to write code on cave walls? https://www.tapatalk.com/groups/qbasic/qbasic-f1/

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Persistence won't work more than once. (SOLVED)
« Reply #11 on: May 22, 2019, 01:41:45 pm »
Just curious, but couldn’t you just use _SCREENMOVE?

http://qb64.org/wiki/SCREENMOVE

Get the original x/y coordinates, save them....
Check for focus...
If not in focus, screen move to original coordinates.

(Basically just swap screenmove for SetWindowPos...)
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline Pete

  • Forum Resident
  • Posts: 2361
  • Cuz I sez so, varmint!
    • View Profile
Re: Persistence won't work more than once. (SOLVED)
« Reply #12 on: May 22, 2019, 04:25:09 pm »
Ah, thinking INSIDE the box, are we now? Well, I wish. The problem with using _SCREENMOVE in place of the API call is that it won't return focus or place the QB64 window back on top. Now you can mess with QB64 statements some more, add _SCREENHIDE and _SCREENSHOW and although it looks like it works on the first off-app click, well, bad news, all subsequent clicks fail. so this is a case of if you want to beat an API, you have to think like an API and while I can't say I'm comfortable with API programming, at least I've figured out how to get a few routines to work. Thanks for taking a POKE at it, and if you were thinking of some other way of using it, other than subbing it in for the API position call in my code example, feel free to post it and I'll give it a try. It's always nicer to have any app written in the box (straight QB64 code)... that's for sure.

Pete :)
Want to learn how to write code on cave walls? https://www.tapatalk.com/groups/qbasic/qbasic-f1/