0

Is it possible to WinWaitActive for two (or more) different window titles at the same time? I'm executing a command and there could be a successful VNC Window coming up or a dialog telling me that the connection failed.

run, vnc.cmd, , Hide , PID ;# Launch hidden
WinWait, TurboVNC info, ;# The Warning
MsgBox A
WinWait, VNC manager [Raw] - 99`%,
MsgBox B

So this minimal example obviously doesn't work if the VNC shows correctly, as B is never reached sequentially. So it was tried to combat this with timers, I thought they had separate threads:

run, vnc.cmd, , Hide , PID ;# Launch hidden
SetTimer, wait_for_graphical_vnc_own_thread, -1 ;# previously I had a 0 here, but that was not the problem's source

WinWait, TurboVNC info, ;# The Warning
MsgBox A
wait_for_graphical_vnc_own_thread:
WinWait, VNC manager [Raw] - 99`%, ;# Successful window
MsgBox B

Unfortunately, this does also not work, not even with another (second) timer for the warning. The next thing I tried was taken from this deprecated board:

Gui +LastFound 
hWnd := WinExist() 

DllCall( "RegisterShellHookWindow", UInt, hWnd ) 
MsgNum := DllCall( "RegisterWindowMessage", Str,"SHELLHOOK" ) 
OnMessage( MsgNum, "ShellMessage" ) 

Return ;                                                 // End of Auto-Execute Section // 

ShellMessage( wParam,lParam ) { 
  If ( wParam = 4 ) ;  HSHELL_WINDOWACTIVATED 
  { 
        WinGetTitle, Title, ahk_id %lParam% 
        MsgBox %Title% ;# Not even this is ever shown
    
        If InStr( Title, "TurboVNC info" ) {
            MsgBox A2
        }
        If InStr( Title, "VNC manager [Raw] - 99`%" ) {
            MsgBox B2
        } 
}

While I like the idea, it launches neither of the two. The author points to this board question on how to Hook on to Shell to receive its messages (archive link) for more details. The last idea comes from this answer to a question about AutoIt:

SetTitleMatchMode, RegEx 
WinWait, TurboVNC info|VNC manager [Raw] - 99`%, 
MsgBox C
;# I would then have a condition here to separate the two cases, but not even C gets shown
Cadoiz
  • 1,446
  • 21
  • 31

5 Answers5

2

In normal AHK this is indeed not as easy as just running two WinWaitActive commands, because normal AHK is not multi threaded. You could do this in AHK_H, but that's a whole another thing.

In normal AHK, the shell hook implementation you found, works just fine.
You just need to account for the HSHELL_RUDEAPPACTIVATED message. This is the message that actually gets sent most of the time (I don't know why, it just is lol).

#NoEnv

DllCall("RegisterShellHookWindow", Ptr, A_ScriptHwnd)
MsgNum := DllCall("RegisterWindowMessage", Str, "SHELLHOOK")
OnMessage(MsgNum, "ShellMessage")
Return

ShellMessage(wParam, lParam) 
{ 
    static HSHELL_WINDOWACTIVATED := 0x04
         , HSHELL_HIGHBIT := 0x8000
         , HSHELL_RUDEAPPACTIVATED := HSHELL_WINDOWACTIVATED | HSHELL_HIGHBIT
    
    if (wParam == HSHELL_RUDEAPPACTIVATED || wParam == HSHELL_WINDOWACTIVATED)
    {
        WinGetTitle, title, % "ahk_id " lParam
        ToolTip, % (wParam == HSHELL_WINDOWACTIVATED ? "HSHELL_WINDOWACTIVATED" : "HSHELL_RUDEAPPACTIVATED") " message received!`nWindow activated: " title "`nHWND: " lParam
    }
}

Also, as a side note, this line of yours has incorrect syntax
If InStr( Title, "VNC manager [Raw] - 99`%" ).
You only need to escape such things in legacy syntax. Here you're using a function, which means you're in the modern expression syntax and you're explicitly specifying a string in " ". And the % character doesn't have any special meaning in a string, so there's nothing to escape there. So, long story short, remove `.


Also that RegEx approach would've worked, but this VNC manager [Raw] - 99% is not the RegEx you'd want.
[ ] has special meaning in RegEx (it's a character class), so you need to escape those characters.

SetTitleMatchMode, RegEx 

WinWaitActive, % "TurboVNC info|VNC manager \[Raw\] - 99%"
WinGetTitle, title, A
ToolTip, % title " activated!"
0x464e
  • 5,948
  • 1
  • 12
  • 17
2

Another solution for some one that doesn't feel they are up to learning regex:

#Persistent

 ; hotkey or other stuff...
currentWin := winWaitActives("ahk_id " . winActive("A"), 10
, "some win title"
, "some other wintitle"
, " and yet another win title")
Return ; end auto-execute section
 
 ; FUNCTION DEFINTION BELOW
winWaitActives(waitNotActiveTitle, waitSeconds, winWaitTitles*) { ; THE ASTERISK PROVIDES A VARIABLE NUMBER OF PARAMS TO THE FUNCTION
    
    ; THIS FIRST COMMAND GIVES A POSITIVE POINT TO START THE LOOP.
    winWaitNotActive, %waitNotActiveTitle%,, %waitSeconds%
   
    ; START LOOPING THROUGH THE TITLES UNTIL FOUND.
    for i, winTitle in winWaitTitles {
        if (winActive(winTitle)) {
            winGetActiveTitle, fullTitle
            Break
        }
    }    
    Return fullTitle
}
T_Lube
  • 306
  • 1
  • 6
1

Set SetTitleMatchMode to RegEx [v1.0.45+] changes WinTitle, WinText, ExcludeTitle, and ExcludeText to accept regular expressions.

SetTitleMatchMode RegEx

    WinWaitActive, "TurboVNC info|VNC manager \[Raw\] - 99%", , 3 ; waiting one of two windows

    if ErrorLevel ;
    {
        MsgBox Non one of windows appeared
        return
    }
Cadoiz
  • 1,446
  • 21
  • 31
vadus
  • 21
  • 4
0

I found a workaround. Calling:

run, vnc_close_helper.ahk, PID_ch 

Actually runs in a separate thread. The helper script then contains the second WinWaitActive I need.

Nimantha
  • 6,405
  • 6
  • 28
  • 69
Cadoiz
  • 1,446
  • 21
  • 31
0

Tried this and it worked properly:

#Persistent

Gui +LastFound
hWnd := WinExist()
DetectHiddenWindows, On

DllCall( "RegisterShellHookWindow", UInt,hWnd )
MsgNum := DllCall( "RegisterWindowMessage", Str,"SHELLHOOK" )
OnMessage( MsgNum, "ShellMessage" )
Return

ShellMessage( wParam,lParam )
{
    If ( wParam = 1 )   ; <-- correct wParam value
    {
        WinGetTitle, title, ahk_id %lParam%

        MsgBox, title = %title%     ; this shows properly now
        
        if InStr(title, "calc")
            MsgBox, This is Calculator
            
        if InStr(title, "notepad")
            MsgBox, This is Notepad
            
    }
}
lightproof
  • 101
  • 1
  • Could you maybe include how your code works? – Lucas Urban Jun 10 '23 at 19:56
  • @LucasUrban I grabbed this piece of code from one of the AHK forums, but unlike the example OP posted, this one works properly as it reads the correct shell message. I'm not an expert in AHK, so all I can provide are the links I found myself while researching how to detect a window using shell hooks: – lightproof Jun 15 '23 at 18:56
  • @LucasUrban 1. [An example with almost the same code](https://www.autohotkey.com/board/topic/95834-using-winwait-effectively-or-how-else-should-i-execute-an-action-when-a-pop-up-window-appears/?p=603493), 2. [some general information on how to receive messages from the shell by hooking to it](https://www.autohotkey.com/board/topic/80644-how-to-hook-on-to-shell-to-receive-its-messages/), 3. [Microsoft documentation on shell hooks](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-registershellhookwindow?redirectedfrom=MSDN) – lightproof Jun 15 '23 at 18:56