Jump to content

Easy shell hooking example


Siao
 Share

Recommended Posts

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.

Oh wow... that's a little over my head :) but thanks for the reply. I think I will be better off relying on Visual Studio to create this program after all... you can see a detailed subclassing/menu hooking example via this thread - http://www.vbforums.com/showthread.php?t=322261

...Maybe I can use this precompiled C++ DLL to inject in the active window's process and handle the communication with AutoIt? I don't want to use an ugly ActiveX control, plus I think it's a bad idea using anything VB6 in Windows Vista/7! Regardless, the "HookControl" DLL in that thread has an included Readme.htm that is very interesting.

EDIT: Hey wait - couldn't I use _GUICtrlMenu_GetItemState to check for state 16? I know it says 'highlighted' via AutoIt help, but upon a quick test I did just now it only triggers that on click - and not on mouse over. Alas, I can only get that to work in MessageLoop mode and with no sleep (otherwise it misses the fired click) and it obviously eats the CPU. Ah, maybe not... that's a sloppy method I guess. Hmm...

Edited by DanielC
Link to comment
Share on other sites

#include <GuiMenu.au3>
#include <WindowsConstants.au3>
#include "WinEventHook.au3"
HotKeySet('{ESC}', '_EXIT')
If Not IsDeclared("WM_LBUTTONDOWN") Then Global Const $WM_LBUTTONDOWN = 0x0201

Global Const $tagMSLLHOOKSTRUCT = $tagPOINT & ";uint mouseData;uint flags;uint time;uint_ptr dwExtraInfo;"
Global Const $w00t = 0x8765
Global $hHookFunc, $hMouseFunc, $pFunc
Global $hWinHook, $hMouseHook
Global $hgMenu

$hMouseFunc = DllCallbackRegister("_MouseHook", "int", "int;wparam;lparam")
$pFunc = DllCallbackGetPtr($hMouseFunc)
$hMouseHook = _WinAPI_SetWindowsHookEx($WH_MOUSE_LL, $pFunc, _WinAPI_GetModuleHandle(0))

$hHookFunc = DllCallbackRegister('_WinEventProc', 'none', 'ptr;uint;hwnd;int;int;uint;uint')
$pFunc = DllCallbackGetPtr($hHookFunc)

$hWinHook = _SetWinEventHook($EVENT_MIN, $EVENT_MAX, 0, $pFunc, 0, 0, _
                BitOR($WINEVENT_SKIPOWNPROCESS, $WINEVENT_OUTOFCONTEXT))
                
If $hWinHook = 0 Then Exit MsgBox(0x10, 'Error', 'Could not register callback procedure')

While 1
    Sleep(20)
WEnd

Func _EXIT()
    Exit
EndFunc

Func OnAutoItExit()
    _WinAPI_UnhookWindowsHookEx($hMouseHook)
    DllCallbackFree($hMouseFunc)
    _UnhookWinEvent($hWinHook)
    DllCallbackFree($hHookFunc)
EndFunc

Func _WinEventProc($hHook, $iEvent, $hWnd, $iObjectID, $iChildID, $iEventThread, $imsEventTime)
    Local $hMenu
    
    If $iEvent =  $EVENT_SYSTEM_MENUPOPUPSTART Then
        $hMenu = _SendMessage($hWnd, 0x01E1)
        $hgMenu = $hMenu
        If _GUICtrlMenu_IsMenu($hMenu) Then
            _GUICtrlMenu_InsertMenuItem($hMenu, _GUICtrlMenu_GetItemCount($hMenu), "w00t", $w00t)
        EndIf
        
    ElseIf $iEvent = $EVENT_SYSTEM_MENUPOPUPEND Then
        $hgMenu = 0
    EndIf
EndFunc

Func _MouseHook($iCode, $iwParam, $ilParam)
    Local $tMouseInfo
    
    If $iCode >= 0 Then
        If $hgMenu And $iwParam = $WM_LBUTTONDOWN Then
            If _GUICtrlMenu_GetItemHighlighted($hgMenu, $w00t, False) Then ConsoleWrite("W00t :)" & @CRLF)
        EndIf
    EndIf

    Return _WinAPI_CallNextHookEx($hMouseHook, $iCode, $iwParam, $ilParam)
EndFunc

WinEventHook.au3

Link to comment
Share on other sites

Mmm, not true for menu bar menus (File, View, etc). For context menus, no, you don't need it. I had to add it or I was getting 5..6..7 w00t's added to the bottom of all my menus. And it is removed correctly as well.

Edited by wraithdu
Link to comment
Share on other sites

Wow very nice use of a mouse hook, thanks a lot Authenticity! wraithdu too, that's a much better way of cleaning up than I was doing it (keeping an array of previous $hWnd vars and comparing to the active window) ;)

That WinEventHook include is full of candy, and this is a great example of hooking all those notifications... ooohhhoohooo crack for coders right there; much respect to the god who created such art for the AutoIt people...

Further reading for other users/thread watchers:

Mouse Input Notifications on MSDN (Use of others apart from WM_LBUTTONDOWN/Left-Click for added menu item triggers/fired events... many possibilities!) [EDIT: :D @ WM_NCHITTEST]

Windows Event Constants on MSDN (Helpful descriptions of all the Constants in WinEventHook.au3)

Yes indeed it adds a menu item to every single popup menu :) but referencing the original post it shouldn't be too hard to trigger it for only $WM_SYSCOMMAND events. Thanks again guys B)

Edited by DanielC
Link to comment
Share on other sites

Thought this is also worth mentioning. Authenticity's method of injecting Menu Items into the Window Menu (System Menu) not only applies to a wide range of other menu's (like the Taskbar context for example), but if you're on a 64-bit Windows host then that alternative method only seems to insert the menu item for 64-bit processes. The OP's Shell Hook only works for 32-bit processes, the opposite.

This is very very good.... one of the flaws of the original PowerMenu was that it only worked for 32-bit processes.

EDIT: Thanks again guys, apologies for hijacking the thread :)

Edited by DanielC
Link to comment
Share on other sites

  • 6 months later...

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

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.

According to msdn SHELL_WINDOWDESTROYED should return the handle of the closed window through the wparameter, this in theory works because the message is sent before the window is actually closed.

Although, i'll grant you, for reasons unknown to me the wparam (or lparam) do not return the handle of the closed window.

Any thoughts?

I'm currently writing a shell replacement for windows so this type of information would be endlessly useful.

Link to comment
Share on other sites

According to msdn SHELL_WINDOWDESTROYED should return the handle of the closed window through the wparameter, this in theory works because the message is sent before the window is actually closed.

Although, i'll grant you, for reasons unknown to me the wparam (or lparam) do not return the handle of the closed window.

Any thoughts?

I'm currently writing a shell replacement for windows so this type of information would be endlessly useful.

I actually did get this working. I have a hge question though. Can this in anyway possible work without explorer.exe? I'm making a shell and everything was going great. Until a recent routine check to see how it'd operate independantly of explorer.exe. It seems the shell hooking doesn't work without it. =(

Any thoughts?

Link to comment
Share on other sites

You're hooking messages from the windows shell... that IS explorer.exe. If explorer is not running, you can't use this method. Your shell replacement is what should actually be generating and broadcasting these messages.

Link to comment
Share on other sites

You're hooking messages from the windows shell... that IS explorer.exe. If explorer is not running, you can't use this method. Your shell replacement is what should actually be generating and broadcasting these messages.

Is that possible? How could that be done?
Link to comment
Share on other sites

You're the one creating the shell replacement... you tell us. I don't think you quite understand all that the 'shell' is supposed to do. It's window management, messaging, everything that you see.

Edited by wraithdu
Link to comment
Share on other sites

You're the one creating the shell replacement... you tell us. I don't think you quite understand all that the 'shell' is supposed to do. It's window management, messaging, everything that you see.

Yes, I don't think "Shell Replacement" puts such a fine point on what I mean. I'm using it more as the term it has become, since what I'm creating isn't an entire shell replacement. It's simply an interface similar to the once provided by the windows shell (ie. desktop, taskbar, file browser, etc.) that runs independantly of explorer.exe, that is the so explorer.exe is intended to not be running.

With that said is there any way I can incorporate the functionality of explorer's shell hooking with explorer.exe running? Or is there maybe even a way to run explorer.exe without anything superfluous running? Like the taskbar, and start menu, desktop, and etc?

Also, for what it's worth, bear in mind Window's windows can still exist without explorer.exe. My trick now will be in detecting how the are interacted with, without explorer.exe running if that's possible.

Edited by JohnnyThrash
Link to comment
Share on other sites

Probably best to take this discussion off the examples forum and reconvene in General help.

See my taskbar in signature for an example

Perhaps this script will also help. Should hide taskbar, start menu and desktop for 3 seconds (works on XP and Vista)

#include <Winapi.au3>

_ShowTaskBar(1)
WinSetTrans("[CLASS:Progman]","",0)
WinSetTrans("Start","",0)
sleep(2000)
WinSetTrans("[CLASS:Progman]","",255)
WinSetTrans("Start","",255)
_ShowTaskBar(0)

Func _ShowTaskBar($fShow)
    Local $hTaskBar
    If @OSVersion = "WIN_VISTA" Then _ShowStartButton($fShow)
    $hTaskBar = _WinAPI_FindWindow("Shell_TrayWnd", "")
    If $fShow=0 Then
        _WinAPI_ShowWindow($hTaskBar, @SW_SHOW)
    Else
        _WinAPI_ShowWindow($hTaskBar, @SW_HIDE)
    EndIf
EndFunc   ;==>_ShowTaskBar

Func _ShowStartButton($fShow )
    Local $hTaskBar, $hStartButton
    If @OSVersion = "WIN_VISTA" Then
        $hStartButton = _WinAPI_FindWindow("Button", "Start")
    Else
        $hTaskBar = _WinAPI_FindWindow("Shell_TrayWnd", "")
        $hStartButton = ControlGetHandle($hTaskBar, "", "Button1")
    EndIf
    If $fShow=0 Then
        _WinAPI_ShowWindow($hStartButton, @SW_SHOW)
    Else
        _WinAPI_ShowWindow($hStartButton, @SW_HIDE)
    EndIf
EndFunc   ;==>_ShowStartButton
Link to comment
Share on other sites

Probably best to take this discussion off the examples forum and reconvene in General help.

See my taskbar in signature for an example

Perhaps this script will also help. Should hide taskbar, start menu and desktop for 3 seconds (works on XP and Vista)

#include <Winapi.au3>

_ShowTaskBar(1)
WinSetTrans("[CLASS:Progman]","",0)
WinSetTrans("Start","",0)
sleep(2000)
WinSetTrans("[CLASS:Progman]","",255)
WinSetTrans("Start","",255)
_ShowTaskBar(0)

Func _ShowTaskBar($fShow)
    Local $hTaskBar
    If @OSVersion = "WIN_VISTA" Then _ShowStartButton($fShow)
    $hTaskBar = _WinAPI_FindWindow("Shell_TrayWnd", "")
    If $fShow=0 Then
        _WinAPI_ShowWindow($hTaskBar, @SW_SHOW)
    Else
        _WinAPI_ShowWindow($hTaskBar, @SW_HIDE)
    EndIf
EndFunc   ;==>_ShowTaskBar

Func _ShowStartButton($fShow )
    Local $hTaskBar, $hStartButton
    If @OSVersion = "WIN_VISTA" Then
        $hStartButton = _WinAPI_FindWindow("Button", "Start")
    Else
        $hTaskBar = _WinAPI_FindWindow("Shell_TrayWnd", "")
        $hStartButton = ControlGetHandle($hTaskBar, "", "Button1")
    EndIf
    If $fShow=0 Then
        _WinAPI_ShowWindow($hStartButton, @SW_SHOW)
    Else
        _WinAPI_ShowWindow($hStartButton, @SW_HIDE)
    EndIf
EndFunc   ;==>_ShowStartButton

I'm sorry, you're right I should not have hijacked the topic, that wasn't my intention. Thanks for your script above. That should help if it becomes necessary to run my shell along side explorer.exe. Your taskbar example suffers from the same problem as mine unfortunately. Shell Hooking, like checking to see if a window is created or destroyed only works if explorer.exe is running, near as I can tell.

I'm starting another topic for this it can be found here:

http://www.autoitscript.com/forum/index.php?showtopic=115520

Link to comment
Share on other sites

  • 2 years later...

Sorry about posting in a two year old topic but I am not sure what else to do. The original script is an X-L-Ant piece of work. It is exactly what I am interested in but there is a problem. When I have a tray icon (for example TeamView) and I right mouse click on it and do an "about" a windows pops up in the middle of the screen with the "about" information yet the Hooks don't pick up anything being created or activated. It seems like all the tray icons are like that. Is there any way to detect Windows opened from tray icon items like the problem I stated?

Link to comment
Share on other sites

  • 11 years later...
Global Const $HSHELL_MONITORCHANGED = 16 ; https://chromium.googlesource.com/chromium/deps/perl/+/master/c/i686-w64-mingw32/include/winuser.h
Global Const $HSHELL_WINDOW_FullScreenEnter = 0x35
Global Const $HSHELL_WINDOW_FullScreenExit = 0x36 ; https://github.com/dechamps/RudeWindowFixer

I think the best place to have these, are on this post.
These are to have a few less "Unknown ShellHook message".

Edited by argumentum
added reference

Follow the link to my code contribution ( and other things too ).
FAQ - Please Read Before Posting.
autoit_scripter_blue_userbar.png

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...