Jump to content

WM_ACTIVATEAPP & _WinAPI_EnumThreadWindows


Recommended Posts

Hi everybody :)
I'd like to share with you a little problem I had with the WM_ACTIVATEAPP message when used in AutoIt, because of a possible missing function (maybe it's there but I didn't find it)

WM_ACTIVATEAPP message (msdn) :
Sent when a window belonging to a different application than the active window is about to be activated. The message is sent to the application whose window is being activated and to the application whose window is being deactivated.

 

#include <MsgBoxConstants.au3>
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <WinAPISysWin.au3>

Example()

Func Example()
    Local $hGUI = GUICreate("Testing WM_ACTIVATEAPP", 400, 60, -1, -1, $WS_OVERLAPPEDWINDOW)

    Local $idButton_1 = GUICtrlCreateButton("Button 1", 100, 15, 80, 25)
    Local $idButton_Close = GUICtrlCreateButton("Close", 215, 15, 80, 25)

    ConsoleWrite("$hGUI = " & $hGUI & @crlf & @crlf)

    GUIRegisterMsg($WM_ACTIVATEAPP, "WM_ACTIVATEAPP")

    GUISetState(@SW_SHOW, $hGUI)

    While 1
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE, $idButton_Close
                ExitLoop

            Case $idButton_1
                MsgBox($MB_TOPMOST, "Title", "Button_1 pressed")
        EndSwitch
    WEnd

    GUIDelete($hGUI)
EndFunc   ;==>Example

;=================================================
Func WM_ACTIVATEAPP($hWnd, $iMsg, $wParam, $lParam)

    Local Static $iCount
    $iCount += 1
    ConsoleWrite($iCount & " WM_ACTIVATEAPP  : $hWnd = " & $hWnd & "   $wParam = " & $wParam & "   $lParam = " & $lParam & @crlf)

    Local $aData = _WinAPI_EnumThreadWindows($lParam) ; personal function

    If $lParam <> 0 And IsArray($aData) Then
        For $i = 1 To UBound($aData) - 1 ; no need of row 0 (contains the number of results)
            ConsoleWrite("0x" & Hex($aData[$i][0], 8) & "   " & $aData[$i][1] & _
                "   " & WinGetTitle(HWnd($aData[$i][0])) & @crlf)
        Next
        ConsoleWrite(@crlf)
    EndIf

    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_ACTIVATEAPP

;=================================================
Func _WinAPI_EnumThreadWindows($idThread, $bVisible = True)

    Local $hEnumProc = DllCallbackRegister('__EnumWindowsProc', 'bool', 'dword;lparam')
    ; note: _WinAPI_EnumChildWindows (in WinAPISysWin.au3) uses the same callback function __EnumWindowsProc (in WinAPIInternals.au3)

    Dim $__g_vEnum[101][2] = [[0]]
    DllCall('user32.dll', 'bool', 'EnumThreadWindows', 'dword', $idThread, 'ptr', DllCallbackGetPtr($hEnumProc), 'lparam', $bVisible)
    If @error Or Not $__g_vEnum[0][0] Then
        $__g_vEnum = @error + 10
    EndIf
    DllCallbackFree($hEnumProc)
    If $__g_vEnum Then Return SetError($__g_vEnum, 0, 0)

    __Inc($__g_vEnum, -1) ; function __Inc found in WinAPIInternals.au3
    Return $__g_vEnum
EndFunc   ;==>_WinAPI_EnumThreadWindows

WM_ACTIVATEAPP.png.62c1b582e49c39d5e26556b395c7bb28.png

 

WinLister.png.62fa43326fc7c10f82de9ac34fd1959b.png

Wh en the preceding script is run from Scite, I have 3 visible windows on the desktop as you can see in the 1st pic above :
1) Scite (its console mainly)
2) The GUI created by the script (GUI handle = 0x0062062A)
3) The AutoIt help file (squeezed !)

The 3 clicks which activated the 3 WM_ACTIVATEAPP messages are :
1) Click on Scite menu => Tools => Go (to execute the script) :

1 WM_ACTIVATEAPP  : $hWnd = 0x0062062A   $wParam = 0x00000001   $lParam = 0x00000954
0x000F056A   SciTEWindow   C:\Temp8\329c.au3 - SciTE-Lite

2) Now that the GUI is active, click on AutoIt help file Window :

2 WM_ACTIVATEAPP  : $hWnd = 0x0062062A   $wParam = 0x00000000   $lParam = 0x00000D28
0x007B06CC   HH Parent   AutoIt Help (v3.3.14.5)

3) Now that AutoIt help file Window is active, click back on the GUI and make it active :

3 WM_ACTIVATEAPP  : $hWnd = 0x0062062A   $wParam = 0x00000001   $lParam = 0x00000000

My issue is that $lparam corresponds to a thread ID. But how to get the handle corresponding to this thread ID (and then the corresponding Windows title) in AutoIt ?

As I didn't find an AutoIt function for that, I tried to create one in the script, named _WinAPI_EnumThreadWindows(), that's how we see in AutoIt console the corresponding handle, class name and Window title of the external applications for the first 2 WM_ACTIVATEAPP (when $lParam <> 0)

The 2nd pic above (WinLister) is a freeware from NirSoft (those guys got incredible freeware stuff since ages) and it shows that the results of the function _WinAPI_EnumThreadWindows() seem correct.

So my question is : does AutoIt have already a way to link a thread ID to a handle ?
If yes, then the function _WinAPI_EnumThreadWindows() wasn't needed in the script.

For the record, I used the existing _WinAPI_EnumChildWindows() as a guide to script _WinAPI_EnumThreadWindows(), replacing for example the [in] HWND parameter found in _WinAPI_EnumChildWindows() with [in] DWORD ThreadId as found in the msdn EnumThreadWindows link

Please advise if you think something is wrong in the function _WinAPI_EnumThreadWindows() found in the preceding script.
Thanks for reading !

Link to post
Share on other sites

 How about this?

#include <MsgBoxConstants.au3>
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <WinAPISysWin.au3>

Example()

Func Example()
    Local $hGUI = GUICreate("Testing WM_ACTIVATEAPP", 400, 60, -1, -1, $WS_OVERLAPPEDWINDOW)

    Local $idButton_1 = GUICtrlCreateButton("Button 1", 100, 15, 80, 25)
    Local $idButton_Close = GUICtrlCreateButton("Close", 215, 15, 80, 25)

    ConsoleWrite("$hGUI = " & $hGUI & @CRLF & @CRLF)

    GUIRegisterMsg($WM_ACTIVATEAPP, "WM_ACTIVATEAPP")

    GUISetState(@SW_SHOW, $hGUI)

    While 1
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE, $idButton_Close
                ExitLoop

            Case $idButton_1
                MsgBox($MB_TOPMOST, "Title", "Button_1 pressed")
        EndSwitch
    WEnd

    GUIDelete($hGUI)
EndFunc   ;==>Example

;=================================================
Func WM_ACTIVATEAPP($hWnd, $iMsg, $wParam, $lParam)

    Local Static $iCount
    $iCount += 1
    ConsoleWrite($iCount & " WM_ACTIVATEAPP  : $hWnd = " & $hWnd & "   $wParam = " & $wParam & "   $lParam = " & $lParam & @CRLF)

    Local $hWnd_Active = WinGetHandle("[ACTIVE]", "")
    ConsoleWrite($hWnd_Active & @TAB & WinGetTitle($hWnd_Active) & @CRLF)

    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_ACTIVATEAPP

 

Link to post
Share on other sites

Here's a version that even works if the AutoIt window is not involved.

#include <GuiMenu.au3>
#include <SendMessage.au3>
#include <WinAPIGdi.au3>
#include <WinAPIMisc.au3>
#include <WinAPIProc.au3>
#include <WinAPISys.au3>
#include <WindowsConstants.au3>

Local $hEventProc = DllCallbackRegister('_EventProc', 'none', 'ptr;dword;hwnd;long;long;dword;dword')
Global $g_tRECT, $g_iIndex, $g_hMenu = 0

OnAutoItExitRegister('OnAutoItExit')

Local $hEventHook = _WinAPI_SetWinEventHook($EVENT_SYSTEM_FOREGROUND, $EVENT_SYSTEM_FOREGROUND, DllCallbackGetPtr($hEventProc))

Run(@SystemDir & '\notepad.exe')

While 1
    Sleep(1000)
WEnd

Func OnAutoItExit()
    _WinAPI_UnhookWinEvent($hEventHook)
    DllCallbackFree($hEventProc)
EndFunc   ;==>OnAutoItExit

Func _EventProc($hEventHook, $iEvent, $hWnd, $iObjectID, $iChildID, $iThreadId, $iEventTime)
    #forceref $hEventHook, $iObjectID, $iChildID, $iThreadId, $iEventTime

    Switch $iEvent
        Case $EVENT_SYSTEM_FOREGROUND
            ConsoleWrite(TimerInit() & @CRLF)
            Local $hWnd_Active = WinGetHandle("[ACTIVE]", "")
            ConsoleWrite($hWnd_Active & @TAB & WinGetTitle($hWnd_Active) & @CRLF)

    EndSwitch
EndFunc   ;==>_EventProc

 

Link to post
Share on other sites

@KaFu (or a reader that may have an idea about what follows)
As I'm always curious, there is something I don't understand about this WM_ACTIVATEAPP message, maybe someone got the answer ?

This is what msdn stipulates about the WM_ACTIVATEAPP message, concerning $wParam and $lParam

WM_ACTIVATEAPP
wParam
Indicates whether the window is being activated or deactivated. This parameter is TRUE if the window is being activated; it is FALSE if the window is being deactivated.

lParam
The thread identifier. If the wParam parameter is TRUE, lParam is the identifier of the thread that owns the window being deactivated. If wParam is FALSE, lParam is the identifier of the thread that owns the window being activated.

Let's see if this worked in the 1st pic of my preceding post, where WM_ACTIVATEAPP was triggered 3 times :

1) I run the script from Scite => it creates the AutoIt Gui (which is now the active window, as shown by KaFu's scripts as they use "[ACTIVE]") . What shows WM_ACTIVATEAPP is also correct, let's paste its result again below :

1 WM_ACTIVATEAPP  : $hWnd = 0x0062062A   $wParam = 0x00000001   $lParam = 0x00000954
0x000F056A   SciTEWindow   C:\Temp8\329c.au3 - SciTE-Lite

As $wParam is True (0x0001, indicating the Gui is being activated) then $lParam indicates the thread of the window being deactivated... and it's correctly SciTEWindow (thread id = 0x0954) that was deactivated as we read it 2 lines above. So far so good.

2) Now that the GUI is active, we click on AutoIt Help file window and this is the result of the 2nd WM_ACTIVATEAPP, which is also correct :

2 WM_ACTIVATEAPP  : $hWnd = 0x0062062A   $wParam = 0x00000000   $lParam = 0x00000D28
0x007B06CC   HH Parent   AutoIt Help (v3.3.14.5)

As $wParam is False (0x0000, indicating the Gui is being deactivated) then $lParam indicates the thread of the window being activated and it's correctly AutoIt help window (thread ID = 0x0D28) that is activated as we read it 2 lines above. Again, so far so good.

3) But now that AutoIt Help file window is active, we're clicking back on the GUI, which triggers WM_ACTIVATEAPP for the 3rd and last time :

3 WM_ACTIVATEAPP  : $hWnd = 0x0062062A   $wParam = 0x00000001   $lParam = 0x00000000

As $wParam is True (0x0001, indicating the Gui is being activated) then $lParam indicates the thread of the window being deactivated... but wait a minute : why does $lParam = 0x0000 ?

$lParam should be equal to 0x0D28 (the thread ID of the AutoIt help window being deactivated) but it's now equal to 0 ?

If you compare phase 1) with phase 3) , you'll notice that in both cases the AutoIt GUI is being activated, but when everything looks correct in phase 1) [where $lparam = Scite window thread ID being deactivated] is now becoming incorrect in phase 3) [where $lparam is equal to 0 instead of being equal to AutoIt help file thread ID being deactivated]

Also, in msdn doc concerning WM_ACTIVATEAPP,  it's never mentioned that $lparam can be equal to 0

Lucky me (?) I found 2 msdn web links stipulating that $lparam could be equal to 0, in a function which is not too far from WM_ACTIVATEAPP, here are the 2 links :

WM_ACTIVATE message :

lParam
A handle to the window being activated or deactivated, depending on the value of the wParam parameter [...] This handle can be NULL. [thanks guys for the 'clear' explanation !]

WM_ACTIVATE message (Windows CE 5.0) :

hwndPrevious = (HWND)lParam;
Handle to the window being activated or deactivated, depending on the value of the fActive parameter [...] This handle can be NULL, and is always NULL when the window being activated and the window being deactivated are in separate processes.

I have no idea if this last comment found in WM_ACTIVATE (Windows CE 5.0) can also be applied to WM_ACTIVATEAPP and if it could explain the behavior of phase 3) ... but let's not forget that if it could explain the behavior of phase 3) then it won't maybe explain the behavior of phase 1)  (?)

That was a long post and I'm really thirsty now :D
Thanks for reading

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...