tatane

Intercepting Close Message on external GUI (hook ?)

13 posts in this topic

Hi,

The title is not totally true.

I'm getting trouble to intercept close message on external GUI before execution.

I mean, I can detect it with _WinAPI_RegisterWindowMessage and $HSHELL_WINDOWDESTROYED (snippet below) but the Message is already gone and proceed.

Because of that, I can't get the ClassName of the external GUI closed. I would like to check the ClassName before executing my code to be sure my function is proceeded on the right GUI.

The GUIs i'm watching are all from the same exe (uvnc.exe) but they can be child of my GUI or not.

How can I get the ClassName of the closing GUI ?

#include <WinAPIProc.au3>
#include <WinAPI.au3>
#include <WinAPISys.au3>
#include <Array.au3>
#include <GUIConstants.au3>
#include <APISysConstants.au3>


$gui = GUICreate("test", 1024, 768, 0, 0, BitOR($WS_MINIMIZEBOX, $WS_SYSMENU, $WS_CAPTION, $WS_EX_COMPOSITED))
$pos_x = 0

GUIRegisterMsg(_WinAPI_RegisterWindowMessage('SHELLHOOK'), 'WM_SHELLHOOK')
_WinAPI_RegisterShellHookWindow($gui)

GUISetState(@SW_SHOW, $gui)


For $i = 1 To 3
    $run = Run("notepad.exe")
    $hwnd_array = _WinAPI_EnumProcessWindows($run, False)

    If Not @error Then
        _WinAPI_SetWindowLong($hwnd_array[1][0], $GWL_EXSTYLE , $WS_EX_MDICHILD)
        _WinAPI_SetParent($hwnd_array[1][0],$gui)
        _WinAPI_SetWindowLong($hwnd_array[1][0], $GWL_HWNDPARENT, $gui)
        _WinAPI_SetWindowPos($hwnd_array[1][0], $HWND_TOP, $pos_x, 0, 200, 200, $SWP_FRAMECHANGED)
        $pos_x = 210 * $i
    EndIf
Next

Run("notepad.exe")


While 1
    $nMsg = GUIGetMsg()
    Select
        Case $nMsg = $GUI_EVENT_CLOSE
            _WinAPI_DeregisterShellHookWindow($gui)
            Exit
    EndSelect

    Sleep(100)
WEnd


Func WM_SHELLHOOK($hWnd, $iMsg, $wParam, $lParam)
    #forceref $iMsg
    Switch $wParam
        Case $HSHELL_WINDOWDESTROYED
            ConsoleWrite('Destroyed: ' & @CRLF & _
                    @TAB & 'PID: ' & WinGetProcess($lParam) & @CRLF & _ ; This will be -1.
                    @TAB & 'ClassName: ' & _WinAPI_GetClassName($lParam) & @CRLF & _ ; This will be empty.
                    @TAB & 'hWnd: ' & $lParam & @CRLF) ; This will be the handle of the window closed.

    EndSwitch
EndFunc   ;==>WM_SHELLHOOK

Share this post


Link to post
Share on other sites



#2 ·  Posted (edited)

Just an idea but you can also intercept $HSHELL_WINDOWCREATED or $HSHELL_WINDOWACTIVATED and store the Class as well as the $lParam in an array.

When the window is destroyed you can do an array lookup based on $lParam of the destroyed window and match to a class.

That's how I would go about it.

Edited by mpower

Share this post


Link to post
Share on other sites

You should not want to use RegisterShellHookWindow because:

  • [This function is not intended for general use. It may be altered or unavailable in subsequent versions of Windows.] - From MSDN
  • The callback function is called when everything with your window is done.
  • Your callback function is called also when the window is hidden but not destroyed.

You may want to use a CBT Hook


99 little bugs in the code

99 little bugs!

Take one down, patch it around

117 little bugs in the code!

Share this post


Link to post
Share on other sites

#4 ·  Posted (edited)

Thanks for your answers. Both are interesting.

I would like to test CBT Hook but it seems after some research on the forum that I have to use a dll (hook.dll).

Is there a way to use this function without a dll (for global hook) ?

 

EDIT : The explication of the required dll : https://msdn.microsoft.com/en-us/library/ms644960%28v=vs.85%29.aspx

Edited by tatane

Share this post


Link to post
Share on other sites

Ah sorry, I forget your question is about an external process so a dll is required.

I have no other idea now. Maybe some kind of WMI query may help, but I don't know anything about those. Maybe a WMI guru in this forum?

btw, if you can accept a dll, then a small one which only catch event and dispatch it to your AutoIt app (send message to your window so you can intercept it) should be the most simple solution


99 little bugs in the code

99 little bugs!

Take one down, patch it around

117 little bugs in the code!

Share this post


Link to post
Share on other sites

Aaaahhhhhh, so great LarsJ. I totally have no idea about this framework! Its just awesome.


99 little bugs in the code

99 little bugs!

Take one down, patch it around

117 little bugs in the code!

Share this post


Link to post
Share on other sites

#8 ·  Posted (edited)

Your work is impressive !!!

It detects the close event but I don't get the handle/pid of the closed window even if I remove the If $iEventId <> $UIA_Window_WindowClosedEventId Then.

I suppose it can't because the window is already closed ?

Edited by tatane

Share this post


Link to post
Share on other sites

You are probably right. Then you simply have to keep track (including ClassName) of your open uvnc-windows. When you receive a windows close event, you have to figure out, if it's one of your uvnc-windows.

Share this post


Link to post
Share on other sites

So do you think it's "better" to use "RegisterShellHookWindow" or your UI Automation Framework ?

Share this post


Link to post
Share on other sites

Basically the best way is: 

Share this post


Link to post
Share on other sites

Actually, I'm already storing the PID and handle of uvnc windows I open in an array. And I check if the PIDs in my array exists as soon as I detect a $HSHELL_WINDOWDESTROYED.

The only (small) problem is that my checking function is called each time a window is destroyed that's why I wanted to check the CLASS before. But it's not a big deal, my array is small.

Anyway, thanks for your time.

Share this post


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