Wait for a specific window

Windows specific forum
BarryG
Addict
Addict
Posts: 4173
Joined: Thu Apr 18, 2019 8:17 am

Wait for a specific window

Post by BarryG »

I need my app to wait until a specific window opens at desktop position 1692,697 (X/Y). I can't identify the window by any other method, so position is the only matching factor, and it may or may not have the focus at the time (so I can't check with GetForegroundWindow_() or use a callback to wait for a new window to be activated). It also doesn't always start at those positions, but moves there after a moment (due to loading media). With all this in mind, what's the best way to know when it appears at that position?

Here's a loop I'm using, but it's brute-force and sometimes slow to react when the window opens, so is there a quicker/better/cleaner way? Thanks.

Code: Select all

; This is runnable code - just replace the left/right values with any window's position that you open after running it.
hWnd=0
Repeat
  w=GetWindow_(GetDesktopWindow_(),#GW_CHILD)
  Repeat
    If IsWindowVisible_(w)
      GetWindowRect_(w,win.RECT)
      If win\left=1692 And win\top=697
        hWnd=w
        Break 2
      EndIf
    EndIf
    w=GetWindow_(w,#GW_HWNDNEXT)
  Until w=0 Or hWnd
Until hWnd
breeze4me
Enthusiast
Enthusiast
Posts: 633
Joined: Thu Mar 09, 2006 9:24 am
Location: S. Kor

Re: Wait for a specific window

Post by breeze4me »

It works well on Windows 10.
Add the code to exclude windows in your app. Or use #WINEVENT_SKIPOWNPROCESS flag.
BTW, to get the exact location of the window above Windows Vista, it would be better to use the DwmGetWindowAttribute function with #DWMWA_EXTENDED_FRAME_BOUNDS attribute.

Code: Select all

#EVENT_SYSTEM_MOVESIZEEND = $000B
#WINEVENT_OUTOFCONTEXT = 0
#WINEVENT_SKIPOWNPROCESS = 2

Procedure WinEventProc(hWinEventHook, event.l, hwnd, idObject.l, idChild.l, idEventThread.l, dwmsEventTime.l)
  If event = #EVENT_SYSTEM_MOVESIZEEND
    If GetParent_(hwnd) = 0 And IsWindowVisible_(hwnd)
      GetWindowRect_(hwnd,  win.RECT)
      ;If win\left=1692 And win\top=697
      If (win\left > 1600 And win\left < 1692) And (win\top > 600 And win\top < 697)
        Debug hwnd
      EndIf
    EndIf
  EndIf
EndProcedure

hHook = SetWinEventHook_(#EVENT_SYSTEM_MOVESIZEEND, #EVENT_SYSTEM_MOVESIZEEND, 0, @WinEventProc(), 0, 0, #WINEVENT_OUTOFCONTEXT | #WINEVENT_SKIPOWNPROCESS)

OpenWindow(0, 0, 0, 222, 200, "", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
OpenWindow(1, 1600, 600, 92, 97, "", #PB_Window_BorderLess)
RunProgram("notepad")

Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow

If hHook
  UnhookWinEvent_(hHook)
EndIf
BarryG
Addict
Addict
Posts: 4173
Joined: Thu Apr 18, 2019 8:17 am

Re: Wait for a specific window

Post by BarryG »

It only gets visible windows, and my app is hidden during it, so all good there.

As for the position, I don't need to check with DwmGetWindowAttribute because the top-left corner pixel is the same regardless. Doesn't matter if I can't see it; the position is there anyway, if you follow me? The top-left corner is at 1692,697 even if the border is invisible, so a match is made.

Good to know that the speed is fine for you. On my PC, sometimes it's like another 500ms (half-second) before the loop "notices". Strange.

Your code looks good, and avoids a loop. Does it detect an existing window, though? Sometimes the window exists before I need to detect it, because I'm not actually launching the app that opens it.
breeze4me
Enthusiast
Enthusiast
Posts: 633
Joined: Thu Mar 09, 2006 9:24 am
Location: S. Kor

Re: Wait for a specific window

Post by breeze4me »

BarryG wrote: Sat Oct 22, 2022 11:18 am Your code looks good, and avoids a loop. Does it detect an existing window, though? Sometimes the window exists before I need to detect it, because I'm not actually launching the app that opens it.
No, you need the brute-force check once.
BarryG
Addict
Addict
Posts: 4173
Joined: Thu Apr 18, 2019 8:17 am

Re: Wait for a specific window

Post by BarryG »

Dang it. Thanks!
breeze4me
Enthusiast
Enthusiast
Posts: 633
Joined: Thu Mar 09, 2006 9:24 am
Location: S. Kor

Re: Wait for a specific window

Post by breeze4me »

breeze4me wrote: Sat Oct 22, 2022 11:27 am
BarryG wrote: Sat Oct 22, 2022 11:18 am Your code looks good, and avoids a loop. Does it detect an existing window, though? Sometimes the window exists before I need to detect it, because I'm not actually launching the app that opens it.
No, you need the brute-force check once.
Oh, I completely forgot the WindowFromPoint function. :oops:
Try it. If it works, no need the brute-force way.
BarryG
Addict
Addict
Posts: 4173
Joined: Thu Apr 18, 2019 8:17 am

Re: Wait for a specific window

Post by BarryG »

WindowFromPoint? No, the mouse won't be over it. Just literally need to know when the window is at that position on the desktop, no matter which other app I'm using at the time.
breeze4me
Enthusiast
Enthusiast
Posts: 633
Joined: Thu Mar 09, 2006 9:24 am
Location: S. Kor

Re: Wait for a specific window

Post by breeze4me »

BarryG wrote: Sat Oct 22, 2022 12:02 pm WindowFromPoint? No, the mouse won't be over it. Just literally need to know when the window is at that position on the desktop, no matter which other app I'm using at the time.
The function is independent of the mouse position.
You can do it as below.

Code: Select all

;First, check if the existing window is at a specific coordinate.
pt.POINT\x = 1692
pt\y = 697
hwnd = WindowFromPoint_(PeekQ(pt))
If hwnd And GetParent_(hwnd) = 0
  GetWindowRect_(hwnd,  win.RECT)
  If win\left=1692 And win\top=697
    ;Do something...
  EndIf
EndIf

hHook = SetWinEventHook_(#EVENT_SYSTEM_MOVESIZEEND, #EVENT_SYSTEM_MOVESIZEEND, 0, @WinEventProc(), 0, 0, #WINEVENT_OUTOFCONTEXT | #WINEVENT_SKIPOWNPROCESS)
......
Of course, if the windows are overlapped at the position, only the brute-force way is available.
So, use a method that suits your situation.


EDIT:
If you also want to detect the location of a window that is moved automatically (i.e., programmatically), you can do the following

Code: Select all

#EVENT_SYSTEM_MOVESIZESTART = $000A
#EVENT_SYSTEM_MOVESIZEEND = $000B
#EVENT_OBJECT_SHOW = $8002
#EVENT_OBJECT_LOCATIONCHANGE = $800B

#WINEVENT_OUTOFCONTEXT = 0
#WINEVENT_SKIPOWNPROCESS = 2

Procedure WinEventProc(hWinEventHook, event.l, hwnd, idObject.l, idChild.l, idEventThread.l, dwmsEventTime.l)
  Static MoveByUser
  
  If event = #EVENT_SYSTEM_MOVESIZESTART
    MoveByUser = 1
    ProcedureReturn
  EndIf
  
  If event = #EVENT_SYSTEM_MOVESIZEEND Or (MoveByUser = 0 And (event = #EVENT_OBJECT_LOCATIONCHANGE Or event = #EVENT_OBJECT_SHOW))
    MoveByUser = 0
    If GetParent_(hwnd) = 0 And IsWindowVisible_(hwnd)
      GetWindowRect_(hwnd,  win.RECT)
      If win\left=1692 And win\top=697
        Debug hwnd
      EndIf
    EndIf
  EndIf
EndProcedure

;First, check if the existing window is at a specific coordinate.
pt.POINT\x = 1692
pt\y = 697
hwnd = WindowFromPoint_(PeekQ(pt))
If hwnd And GetParent_(hwnd) = 0
  GetWindowRect_(hwnd,  win.RECT)
  If win\left=1692 And win\top=697
    ;Do something...
  EndIf
EndIf

hHook = SetWinEventHook_(#EVENT_SYSTEM_MOVESIZESTART, #EVENT_OBJECT_LOCATIONCHANGE, 0, @WinEventProc(), 0, 0, #WINEVENT_OUTOFCONTEXT | #WINEVENT_SKIPOWNPROCESS)
......
Post Reply