Jump to content

Easy shell hooking example


Siao
 Share

Recommended Posts

Well, forum search on this didn't return anything noteworthy for me, so here we go.

Shell hooking is monitoring window activity, which helps when automating apps. This particular method is pretty similar to installing WH_SHELL hook with SetWindowsHookEx, but instead of callback proc it makes use of window's proc, so GuiRegisterMsg() can be used.

How to set it up:

First we have to register unique message - RegisterWindowMessage("SHELLHOOK")

Then GuiRegisterMsg() the message with a handler function, and of course hook up our GUI with RegisterShellHookWindow (and unregister when not needed anymore).

And that's pretty much it.

What it does:

$HSHELL_WINDOWCREATED, $HSHELL_WINDOWACTIVATED, $SHELL_WINDOWDESTROYED

Notifies when some window is created, activated or destroyed. Gives window handle.

$HSHELL_REDRAW

Notifies when some window (taskbar button?) is redrawed, mainly due to change of its title. Gives window handle.

$HSHELL_FLASH

Notifies when some window is flashing (is followed with $HSHELL_REDRAW). Gives window handle.

$HSHELL_GETMINRECT

Happens when some window is minimized/maximized, and is supposed to give rectangle coords with SHELLHOOKINFO structure, but that part isn't working for me, only window handle is.

$HSHELL_ACCESSIBILITYSTATE

Notifies when some accessibility options change...

$HSHELL_SYSMENU

Notifies when some window's system menu is brought up by right clicking its taskbar button. Gives window handle.

Other codes never triggered for me, but YMMV.

Much of this functionality can be achieved by polling WinActivate, WinExists and etc., but that is so uncool (not to mention CPU usage it may take if you want to monitor all windows). <_<

I threw this together when I was experimenting with various ways to keep other apps from stealing keyboard focus from my text editor.

Would be possible to write some taskbar-like utility using these too.

#include <GuiConstants.au3>
#include <Misc.au3>

#NoTrayIcon
Opt("GUICloseOnESC", 0)
Opt("GUIOnEventMode", 1)
Opt("WinWaitDelay", 0)

Global Const $WM_SYSCOMMAND = 0x0112
Global Const $SC_MOVE = 0xF010
Global Const $SC_SIZE = 0xF000
Global Const $SC_CLOSE = 0xF060

;ShellHook notification codes:
Global Const $HSHELL_WINDOWCREATED = 1;
Global Const $HSHELL_WINDOWDESTROYED = 2;
Global Const $HSHELL_ACTIVATESHELLWINDOW = 3;
Global Const $HSHELL_WINDOWACTIVATED = 4;
Global Const $HSHELL_GETMINRECT = 5;
Global Const $HSHELL_REDRAW = 6;
Global Const $HSHELL_TASKMAN = 7;
Global Const $HSHELL_LANGUAGE = 8;
Global Const $HSHELL_SYSMENU = 9;
Global Const $HSHELL_ENDTASK = 10;
Global Const $HSHELL_ACCESSIBILITYSTATE = 11;
Global Const $HSHELL_APPCOMMAND = 12;
Global Const $HSHELL_WINDOWREPLACED = 13;
Global Const $HSHELL_WINDOWREPLACING = 14;
Global Const $HSHELL_RUDEAPPACTIVATED = 32772;
Global Const $HSHELL_FLASH = 32774;

Global $bHook = 1
;GUI stuff:

Global $iGuiW = 400, $iGuiH = 50, $sTitle = "Shell Hooker", $aBtnText[2] = ["START", "STOP"]
$hGui = GUICreate($sTitle, $iGuiW, $iGuiH, -1, 0, $WS_POPUP+$WS_BORDER, $WS_EX_TOPMOST)
GUISetOnEvent($GUI_EVENT_CLOSE, "SysEvents")
GUISetOnEvent($GUI_EVENT_PRIMARYDOWN, "SysEvents")
GUIRegisterMsg($WM_SYSCOMMAND, "On_WM_SYSCOMMAND")
$cBtnMini = GUICtrlCreateButton("v", $iGuiW-$iGuiH, 0, $iGuiH/2, $iGuiH/2)
GUICtrlSetOnEvent(-1, "CtrlEvents")
GUICtrlSetTip(-1, "Minimize")
$cBtnClose = GUICtrlCreateButton("X", $iGuiW-$iGuiH/2, 0, $iGuiH/2, $iGuiH/2)
GUICtrlSetOnEvent(-1, "CtrlEvents")
GUICtrlSetTip(-1, "Exit")
$cBtnHook = GUICtrlCreateButton("", $iGuiW-$iGuiH, $iGuiH/2, $iGuiH, $iGuiH/2)
GUICtrlSetData(-1, $aBtnText[$bHook])
GUICtrlSetTip(-1, "Hook/Unhook Shell")
GUICtrlSetOnEvent(-1, "CtrlEvents")
$cList = GUICtrlCreateList("", 0, 0, $iGuiW-$iGuiH-1, $iGuiH, $LBS_NOINTEGRALHEIGHT+$WS_VSCROLL)
GUICtrlSetOnEvent(-1, "CtrlEvents")

;Hook stuff:
GUIRegisterMsg(RegisterWindowMessage("SHELLHOOK"), "HShellWndProc")
ShellHookWindow($hGui, $bHook)


GUISetState()

While 1
    Sleep(1000)
WEnd

Func SysEvents()
    Switch @GUI_CtrlId
        Case $GUI_EVENT_CLOSE
            Exit
        Case $GUI_EVENT_PRIMARYDOWN
            ;CTRL + Left click to drag GUI
            If _IsPressed("11") Then
                DllCall("user32.dll", "int", "ReleaseCapture")
                DllCall("user32.dll", "int", "SendMessage", "hWnd", $hGui, "int", 0xA1, "int", 2, "int", 0)
            EndIf
    EndSwitch
EndFunc
Func CtrlEvents()
    Switch @GUI_CtrlId
        Case $cBtnMini
            GUISetState(@SW_MINIMIZE)
        Case $cBtnClose
            _SendMessage($hGui, $WM_SYSCOMMAND, $SC_CLOSE, 0)
        Case $cBtnHook
            $bHook = BitXOR($bHook, 1)
            ShellHookWindow($hGui, $bHook)
            GUICtrlSetData($cBtnHook, $aBtnText[$bHook])
    EndSwitch   
EndFunc
Func HShellWndProc($hWnd, $Msg, $wParam, $lParam)
    Switch $wParam
        Case $HSHELL_WINDOWCREATED
            MsgPrint("Window created: " & $lParam & " (" & WinGetTitle($lParam) & ")")
        Case $HSHELL_WINDOWDESTROYED
            MsgPrint("Window destroyed: " & $lParam)
        Case $HSHELL_ACTIVATESHELLWINDOW
            MsgPrint("HSHELL_ACTIVATESHELLWINDOW: Not used.");
        Case $HSHELL_WINDOWACTIVATED
            MsgPrint("Window activated: " & $lParam & " (" & WinGetTitle($lParam) & ")")
        Case $HSHELL_GETMINRECT
            Local $tSHELLHOOKINFO = DllStructCreate("hwnd hwnd;int left;long top;long right;long bottom", $lParam)
            MsgPrint("HSHELL_GETMINRECT: " & HWnd(DllStructGetData($tSHELLHOOKINFO, "hwnd")) & ' (' & _
                                            DllStructGetData($tSHELLHOOKINFO, "left") & ',' & _
                                            DllStructGetData($tSHELLHOOKINFO, "top") & ',' & _  
                                            DllStructGetData($tSHELLHOOKINFO, "right") & ',' & _    
                                            DllStructGetData($tSHELLHOOKINFO, "bottom") & ')')
        Case $HSHELL_REDRAW
            MsgPrint("Window redraw: " & $lParam & " (" & WinGetTitle($lParam) & ")")
        Case $HSHELL_TASKMAN
            MsgPrint("HSHELL_TASKMAN: Can be ignored.");
        Case $HSHELL_LANGUAGE
            MsgPrint("HSHELL_LANGUAGE: " & $lParam);
        Case $HSHELL_SYSMENU
            MsgPrint("HSHELL_SYSMENU: " & $lParam);
        Case $HSHELL_ENDTASK
            MsgPrint("Window needs to be closed: " & $lParam & " (" & WinGetTitle($lParam) & ")")
        Case $HSHELL_ACCESSIBILITYSTATE
            MsgPrint("HSHELL_ACCESSIBILITYSTATE: " & $lParam);
        Case $HSHELL_APPCOMMAND
            MsgPrint("HSHELL_APPCOMMAND: " & $lParam);
        Case $HSHELL_WINDOWREPLACED
            MsgPrint("Window replaced: " & $lParam & " (" & WinGetTitle($lParam) & ")")
        Case $HSHELL_WINDOWREPLACING
            MsgPrint("Window replacing: " & $lParam & " (" & WinGetTitle($lParam) & ")")
        Case $HSHELL_RUDEAPPACTIVATED
            MsgPrint("HSHELL_RUDEAPPACTIVATED: " & $lParam);
        Case $HSHELL_FLASH
            MsgPrint("Window flash: " & $lParam & " (" & WinGetTitle($lParam) & ")")
        Case Else
            MsgPrint("Unknown ShellHook message: " & $wParam & " , " & $lParam)
    EndSwitch
EndFunc
;register/unregister ShellHook
Func ShellHookWindow($hWnd, $bFlag)
    Local $sFunc = 'DeregisterShellHookWindow'
    If $bFlag Then $sFunc = 'RegisterShellHookWindow'
    Local $aRet = DllCall('user32.dll', 'int', $sFunc, 'hwnd', $hWnd)
    MsgPrint($sFunc & ' = ' & $aRet[0])
    Return $aRet[0]
EndFunc
Func MsgPrint($sText)
    ConsoleWrite($sText & @CRLF)
    GUICtrlSendMsg($cList, $LB_SETCURSEL, GUICtrlSendMsg($cList, $LB_ADDSTRING, 0, $sText), 0)
EndFunc
;register window message
Func RegisterWindowMessage($sText)
    Local $aRet = DllCall('user32.dll', 'int', 'RegisterWindowMessage', 'str', $sText)
    Return $aRet[0]
EndFunc
Func On_WM_SYSCOMMAND($hWnd, $Msg, $wParam, $lParam)
    Switch BitAND($wParam, 0xFFF0)
        Case $SC_MOVE, $SC_SIZE
        Case $SC_CLOSE
            ShellHookWindow($hGui, 0)
            Return $GUI_RUNDEFMSG
            ;Exit
    EndSwitch
EndFunc

ShellHookWindow.au3

Edited by Siao

"be smart, drink your wine"

Link to comment
Share on other sites

Nice!

Good work! <_<

Só o que posso lhe dizer, bom é quando faz mal!My work:Au3Irrlicht - Irrlicht for AutoItMsAgentLib - An UDF for MSAgentAu3GlPlugin T2 - A 3D plugin for AutoIt...OpenGl Plugin - The old version of Au3GlPlugin.MAC Address Changer - Changes the MAC AddressItCopter - A dragonfly R/C helicopter simulator[center]VW Bug user[/center]Pinheiral (Pinewood) city: http://pt.wikipedia.org/wiki/Pinheiral

Link to comment
Share on other sites

Very good work! My only question is how to use this without a GUI. I tried commenting out everything that looked like GUI stuff, but then it didn't work (go figure :P)

I want to use these hooks in a program I'm writing, so I just want your script to alert my program anytime one of these events happens. Thanks for any help!

Also, is there any way to grab the name of a window before it gets closed?

Edited by magician13134
Link to comment
Share on other sites

Very good work! My only question is how to use this without a GUI. I tried commenting out everything that looked like GUI stuff, but then it didn't work (go figure :P)

I want to use these hooks in a program I'm writing, so I just want your script to alert my program anytime one of these events happens. Thanks for any help!

You need a window which will be receiving the messages. It doesn't have to be visible. So you need GUICreate.

Also, is there any way to grab the name of a window before it gets closed?

Using this method?

I assume these notifications are received post factum. So when you receive "window destroyed" notification, there's no such window to get the title from anymore.

Keep a list, array or whatever, of handles and titles from $HSHELL_WINDOWCREATED, $HSHELL_REDRAW (and possibly $HSHELL_WINDOWACTIVATED). This will allow you to get the last known window title by the handle you get from $SHELL_WINDOWDESTROYED.

"be smart, drink your wine"

Link to comment
Share on other sites

  • 2 weeks later...

OVERWHELMING WORK !!!!

p.s - where can i find all the windows msgs ?!

[u]My Au3 Scripts:[/u]____________(E)Lephant, A Share download manager (RS/MU etc)Http1.1 Console, The Ez Way!Internet Reconnection Automation Suite & A Macro Recording Tool.SK's Alarm Clock, Playing '.MP3 & .Wav' Files._________________Is GOD a mistake of the Humanity Or the Humanity is a mistake of GOD ?!

Link to comment
Share on other sites

  • 1 month later...

Would something like this be able to "grab" text from a textbox while the app is minimized or would it require something completely different? Basically intercepting the messages before getting to the textbox.

Agreement is not necessary - thinking for one's self is!

My-Colors.jpg

cuniform2.gif

Link to comment
Share on other sites

  • 2 months later...
  • 3 months later...
  • 2 weeks later...
  • 6 months later...

I have the same question:

Nearly all window creation events of the system control windows are not recognized. Interesting enough, destroying the windows is always recognized...

Any hints?

Link to comment
Share on other sites

  • 9 months later...

Once we add a new menu item (for example, in an external System Menu), how does one get the Control ID of the new clicked MenuItem? I can't seem to get it to send a message back to the AutoIt script/GUI. I tried a few things; a new WM_SYSCOMMAND case, the DummyGUI method, some other random nonsense and experimentation... couldn't get anything to fire as far as Winspector could tell. Maybe it's a problem with On Event mode? Or unique to Windows 7? UAC is off for this machine though.

Any tips or pointers would be totally awesome - I'm not asking for an example, but if someone knows a specific function I should try out let me know :)

For all those wondering, I'm basically trying to create a script like PowerMenu - only better/extensible since it will be open source (the original creator of the amazing PowerMenu prog has disappeared for years and kept it closed source...) - I would of posted what I have so far but it's largely identical to the OP's example with pretty much some extra Menu UDF commands (that part works) and cheap method of hooking any active window (it works for CMD.EXE and Task Manager!).

Cheers!

Link to comment
Share on other sites

You won't like the answer. Here's what I think you'd have to do:

Inject a DLL into the target process, subclass the main window procedure so you can intercept the process's window messages, then set up some interprocess communication with your AutoIt script.

Link to comment
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
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...