Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

[How to] Hook on to Shell to receive its messages?


  • Please log in to reply
73 replies to this topic
SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005

How to Hook on to Shell to receive its messages?
http://www.autohotke...p=123323#123323

Foreword: The shell receives messages whenever a Window is being Created/Activated/Detroyed etc. This topic discusses on hooking Shell messages and the possible uses.


RegisterShellHookWindow Function :
The difference with RegisterShellHookWindow is that the messages are received through the specified window's WindowProc and not through a call back procedure.

A basic template would look like:

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 ) {
; Execute a command based on wParam and lParam
}

The documented values for wParam are:


[*:23mbb9rw]HSHELL_WINDOWCREATED
[*:23mbb9rw]HSHELL_WINDOWDESTROYED
[*:23mbb9rw]HSHELL_ACTIVATESHELLWINDOW
[*:23mbb9rw]HSHELL_WINDOWACTIVATED
[*:23mbb9rw]HSHELL_GETMINRECT
[*:23mbb9rw]HSHELL_REDRAW
[*:23mbb9rw]HSHELL_TASKMAN
[*:23mbb9rw]HSHELL_LANGUAGE
[*:23mbb9rw]HSHELL_SYSMENU
[*:23mbb9rw]HSHELL_ENDTASK
[*:23mbb9rw]HSHELL_ACCESSIBILITYSTATE
[*:23mbb9rw]HSHELL_APPCOMMAND
[*:23mbb9rw]HSHELL_WINDOWREPLACED
[*:23mbb9rw]HSHELL_WINDOWREPLACING
[*:23mbb9rw]HSHELL_HIGHBIT
[*:23mbb9rw]HSHELL_FLASH
[*:23mbb9rw]HSHELL_RUDEAPPACTIVATEDlParam differs in type according to the value of wParam received. For most of the wParam values, the lParam is a handle to a window that can be used as ahk_id %lParam% in AHK's Window commands.

Some ideas:
The shell receives HSHELL_GETMINRECT ( with a shellhook structure ) whenever a window is being Minimised/Maximised. A script may monitor it to Minimize a window to the tray.
The shell receives HSHELL_REDRAW when a window is being Redrawn. A script may monitor it to activate a window whenever its contents are changed.

I tried experimenting and here are some examples:

Experiment 1:

In Windows XP, CTRL+ALT+DEL brings the Task Manager. The forum has seen some posts requesting a way to deny the access to Task Manager.
The following scripts detects and closes Windows Task Manager almost instantly when created.

#Persistent
SetBatchLines, -1
Process, Priority,, High

Gui +LastFound
hWnd := WinExist()

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

ShellMessage( wParam,lParam ) {
  If ( wParam = 1 ) ;  HSHELL_WINDOWCREATED := 1
     {
       WinGetTitle, Title, ahk_id %lParam%
       If  ( Title = "Windows Task Manager" ) 
         {
           WinClose, ahk_id %lParam%
         ; Run, Calc.exe              ; instead
         }
     }
}

Experiment 2:

Hooking Shell messages provides a sure shot way of keeping a track on Last Active Window.
See: How to retrieve LAST active window? by r0lZ
The following script toggles the TopMost/TopLevel styles ( Always on Top On/OFF ) of the active window.

#Persistent
Menu, Tray, NoStandard
Menu, Tray, Add, Toggle AOT, ToggleAOT
Menu, Tray, Add,
Menu, Tray, Add, Reload, ExitScript
Menu, Tray, Add, Exit  , ExitScript
Menu, Tray, Tip, Toggle AOT
Menu, Tray, Default, Toggle AOT

Gui +LastFound
DllCall( "RegisterShellHookWindow", UInt,WinExist() )
MsgNum := DllCall( "RegisterWindowMessage", Str,"SHELLHOOK" )
OnMessage( MsgNum, "ShellMessage" )
LastActiveWindowID := WinActive("A")

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

ShellMessage( wParam, lParam )  {
  Global LastActiveWindowID
  If ( wParam = 4 And WinExist( "ahk_id " lParam ) ) { ; HSHELL_WINDOWACTIVATED = 4
       LastActiveWindowID := lParam
  }
}

ToggleAOT:
  WinSet, AlwaysOnTop, Toggle, ahk_id %LastActiveWindowID%
Return

ExitScript:
  DllCall( "DeregisterShellHookWindow", UInt,hWnd ) ; Redundant, I guess!
  IfEqual, A_ThisMenuItem, Reload, Reload
  ExitApp
Return

Run the script.
Click on the target window to focus it.
Double click on the scripts tray icon.
The target window will be toggled between TopMost and TopLevel styles.

Experiment 3:

I have a Logitech multimedia Keyboard and have not installed the software that came with it. I was using my own OSD script which used VOLUME_UP /DN/MUTE keys as hotkeys to trigger adjustment with SoundGet / SoundSet commands.

Now I have found that, the Shell is notified ( HSHELL_APPCOMMAND ) whenever I press a mulitmedia key. The HiWord of lParam contains the value of the MM key pressed. So I have altered my Volume Change OSD script to work without HotKeys or SoundSet command.

Gui, Color, FFFFFF 
Gui, -Caption +Border +AlwaysOnTop +ToolWindow +LastFound

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

Gui, Add, Picture, x5 y5 w32 h32 Icon4 vIcon1, SndVol32.exe
Gui, Add, Picture, x5 y5 w32 h32 Icon5 vIcon2, SndVol32.exe
Loop,25
  Gui, Add, Text, x+3 w5 h32 Hidden Border vText%A_Index% 0x4 ; 

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

ShellMessage( wParam,lParam )   {
  If ( wParam = 12 AND ( (lParam>>16) >= 8 OR (lParam>>16) <= 10) )    {
     Gui, Show
     SoundGet, Volume, MASTER, VOLUME
     SoundGet, Mute  ,       , MUTE

     Loop, 25 
       IfLessOrEqual, A_Index, % Round(Volume/4), GuiControl, Show, Text%A_Index%
       Else                                       GuiControl, Hide, Text%A_Index%

     IfEqual, Mute, On, GuiControl, Hide, Icon2
     Else               GuiControl, Show, Icon2

     SetTimer, GuiEscape, 1234
                                }                                      }
GuiEscape:
  SetTimer, GuiEscape, OFF
  Gui, Hide

Simply put, when wParam is 12 ( HSHELL_APPCOMMAND ), the HiWord of lParam contains one of the following constants:

APPCOMMAND_BROWSER_BACKWARD = 1
APPCOMMAND_BROWSER_FORWARD = 2
APPCOMMAND_BROWSER_REFRESH = 3
APPCOMMAND_BROWSER_STOP = 4
APPCOMMAND_BROWSER_SEARCH = 5
APPCOMMAND_BROWSER_FAVORITES = 6
APPCOMMAND_BROWSER_HOME = 7
APPCOMMAND_VOLUME_MUTE = 8
APPCOMMAND_VOLUME_DOWN = 9
APPCOMMAND_VOLUME_UP = 10
APPCOMMAND_MEDIA_NEXTTRACK = 11
APPCOMMAND_MEDIA_PREVIOUSTRACK = 12
APPCOMMAND_MEDIA_STOP = 13
APPCOMMAND_MEDIA_PLAY_PAUSE = 14
APPCOMMAND_LAUNCH_MAIL = 15
APPCOMMAND_LAUNCH_MEDIA_SELECT = 16
APPCOMMAND_LAUNCH_APP1 = 17
APPCOMMAND_LAUNCH_APP2 = 18
APPCOMMAND_BASS_DOWN = 19
APPCOMMAND_BASS_BOOST = 20
APPCOMMAND_BASS_UP = 21
APPCOMMAND_TREBLE_DOWN = 22
APPCOMMAND_TREBLE_UP = 23
APPCOMMAND_MICROPHONE_VOLUME_MUTE = 24
APPCOMMAND_MICROPHONE_VOLUME_DOWN = 25
APPCOMMAND_MICROPHONE_VOLUME_UP = 26
APPCOMMAND_HELP = 27
APPCOMMAND_FIND = 28
APPCOMMAND_NEW = 29
APPCOMMAND_OPEN = 30
APPCOMMAND_CLOSE = 31
APPCOMMAND_SAVE = 32
APPCOMMAND_PRINT = 33
APPCOMMAND_UNDO = 34
APPCOMMAND_REDO = 35
APPCOMMAND_COPY = 36
APPCOMMAND_CUT = 37
APPCOMMAND_PASTE = 38
APPCOMMAND_REPLY_TO_MAIL = 39
APPCOMMAND_FORWARD_MAIL = 40
APPCOMMAND_SEND_MAIL = 41
APPCOMMAND_SPELL_CHECK = 42
APPCOMMAND_DICTATE_OR_COMMAND_CONTROL_TOGGLE = 43
APPCOMMAND_MIC_ON_OFF_TOGGLE = 44
APPCOMMAND_CORRECTION_LIST = 45
Experiment 4:

While trying to study and understand the messages I wrote this crude Shell spy to monitor the messages being received by the Shell :

#Persistent
Menu,Tray,Add
Menu,Tray,Add, &Show, GuiShow
Menu,Tray,Default, &Show
Gui, Font, s9, Courier New

Gui +ToolWindow +AlwaysOnTop +Resize +LastFound
hWnd := WinExist()
DllCall( "RegisterShellHookWindow", UInt,hWnd )
MsgNum := DllCall( "RegisterWindowMessage", Str,"SHELLHOOK" )
OnMessage( MsgNum, "ShellMessages" )

Gui, Add, Edit, w512 h512 vMsgs hwndEditC +ReadOnly
Gui, Show, x10 y10, Shell Spy

MsgNames =
(
HSHELL_WINDOWCREATED
HSHELL_WINDOWDESTROYED
HSHELL_ACTIVATESHELLWINDOW
HSHELL_WINDOWACTIVATED
HSHELL_GETMINRECT
HSHELL_REDRAW
HSHELL_TASKMAN
HSHELL_LANGUAGE
HSHELL_SYSMENU
HSHELL_ENDTASK
HSHELL_ACCESSIBILITYSTATE
HSHELL_APPCOMMAND
HSHELL_WINDOWREPLACED
HSHELL_WINDOWREPLACING
HSHELL_HIGHBIT
HSHELL_FLASH
HSHELL_RUDEAPPACTIVATED
)

AppCommands =
(
APPCOMMAND_BROWSER_BACKWARD = 1
APPCOMMAND_BROWSER_FORWARD = 2
APPCOMMAND_BROWSER_REFRESH = 3
APPCOMMAND_BROWSER_STOP = 4
APPCOMMAND_BROWSER_SEARCH = 5
APPCOMMAND_BROWSER_FAVORITES = 6
APPCOMMAND_BROWSER_HOME = 7
APPCOMMAND_VOLUME_MUTE = 8
APPCOMMAND_VOLUME_DOWN = 9
APPCOMMAND_VOLUME_UP = 10
APPCOMMAND_MEDIA_NEXTTRACK = 11
APPCOMMAND_MEDIA_PREVIOUSTRACK = 12
APPCOMMAND_MEDIA_STOP = 13
APPCOMMAND_MEDIA_PLAY_PAUSE = 14
APPCOMMAND_LAUNCH_MAIL = 15
APPCOMMAND_LAUNCH_MEDIA_SELECT = 16
APPCOMMAND_LAUNCH_APP1 = 17
APPCOMMAND_LAUNCH_APP2 = 18
APPCOMMAND_BASS_DOWN = 19
APPCOMMAND_BASS_BOOST = 20
APPCOMMAND_BASS_UP = 21
APPCOMMAND_TREBLE_DOWN = 22
APPCOMMAND_TREBLE_UP = 23
APPCOMMAND_MICROPHONE_VOLUME_MUTE = 24
APPCOMMAND_MICROPHONE_VOLUME_DOWN = 25
APPCOMMAND_MICROPHONE_VOLUME_UP = 26
APPCOMMAND_HELP = 27
APPCOMMAND_FIND = 28
APPCOMMAND_NEW = 29
APPCOMMAND_OPEN = 30
APPCOMMAND_CLOSE = 31
APPCOMMAND_SAVE = 32
APPCOMMAND_PRINT = 33
APPCOMMAND_UNDO = 34
APPCOMMAND_REDO = 35
APPCOMMAND_COPY = 36
APPCOMMAND_CUT = 37
APPCOMMAND_PASTE = 38
APPCOMMAND_REPLY_TO_MAIL = 39
APPCOMMAND_FORWARD_MAIL = 40
APPCOMMAND_SEND_MAIL = 41
APPCOMMAND_SPELL_CHECK = 42
APPCOMMAND_DICTATE_OR_COMMAND_CONTROL_TOGGLE = 43
APPCOMMAND_MIC_ON_OFF_TOGGLE = 44
APPCOMMAND_CORRECTION_LIST = 45
)
Return


ShellMessages( wP,lP ) {
  Global EditC
  Global mVal := lP
  GuiControlGet, Msgs
  Routine := GetMessageName( wP )
  IfEqual,Routine,, SetEnv, Routine, UNKNOWN
  GuiControl,, Msgs, % Msgs "`n`n" Routine "  [" wP "]"
  If IsLabel(Routine)
     GoSub, %Routine%
  ControlSend,, ^{End}, ahk_id %EditC%
}


GetMessageName( FieldN=0 ) {
  Global MsgNames
  Loop, Parse, MsgNames, `n
        IfEqual, A_Index, %FieldN%, Return, A_LoopField
}

GetAppCommand( FieldN=0 ) {
  Global AppCommands
  Loop, Parse, AppCommands, `n
        IfEqual, A_Index, %FieldN%, Return, A_LoopField
}

UNKNOWN:
HSHELL_WINDOWCREATED:
HSHELL_WINDOWACTIVATED:
HSHELL_WINDOWDESTROYED:
HSHELL_REDRAW:
HSHELL_FLASH:
HSHELL_ENDTASK:
HSHELL_WINDOWREPLACING:
HSHELL_WINDOWREPLACED:	
HSHELL_RUDEAPPACTIVATED:	

 WinGetTitle, Title, ahk_id %mVal%
 WinGetClass, Class, ahk_id %mVal%
 GuiControlGet, Msgs
 GuiControl,, Msgs
   , % Msgs "`n`nhWnd`t: " WinExist("ahk_id" mVal) "`nTitle`t: " Title "`nClass`t: " Class

Return

HSHELL_GETMINRECT:
Return

HSHELL_APPCOMMAND:
 GuiControlGet, Msgs
 GuiControl,, Msgs, % Msgs " // " GetAppCommand( mVal >> 16 )
Return

GuiClose:
 Gui, Show, Hide
Return
    
GuiShow:
 Gui, Show, 
Return

It is not exhaustive, but useful to understand how the stuff works.

With the above code running, Run Calculator and change it from Standard to Scientific mode.
One will find that the Calculator window is destroyed and created again resulting in the change of handle. ( I did not know it :( )

Interestingly I found that an undocumented ( AFAIK ) value of 0x8006 ( 32774 ) is received by the Shell whenever a Window is Flashing its Titlebar/Taskbar button. The following code activates a window that is flashing: ( not sure whether it will work for everyone ) :

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 = 0x8006 ) ;  0x8006 is 32774 as shown in Spy!
    {
      WinActivate, ahk_id %lParam%
    }
}

Case: problems activating windows by frenchSteve
If you want to contain/restrict the WinActivation to a particular class of Window, then set a condition using WinGetClass command.


Will try to post more examples when I find them ;)



Sean
  • Members
  • 2462 posts
  • Last active: Feb 07 2012 04:00 AM
  • Joined: 12 Feb 2007
Superb! I'm speechless atm. ;)

majkinetor
  • Moderators
  • 4512 posts
  • Last active: May 20 2019 07:41 AM
  • Joined: 24 May 2006
Now, this is a real trick! :idea:

Thx Skan, amazing work.
Posted Image

PhiLho
  • Moderators
  • 6850 posts
  • Last active: Jan 02 2012 10:09 PM
  • Joined: 27 Dec 2005
Wow, this answer lot of recurrent requests at once, superb!
Posted Image vPhiLho := RegExReplace("Philippe Lhoste", "^(\w{3})\w*\s+\b(\w{3})\w*$", "$1$2")

Thalon
  • Members
  • 641 posts
  • Last active: Jan 02 2017 12:17 PM
  • Joined: 12 Jul 2005
Page saved :D

Thx a lot!
Thalon

SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005
Thanks to everybody! :D

shader
  • Members
  • 56 posts
  • Last active: Nov 16 2011 09:05 AM
  • Joined: 29 Oct 2004
Hi Skan, I've found that you wrote:

The shell receives HSHELL_GETMINRECT ( with a shellhook structure ) whenever a window is being Minimised/Maximised. A script may monitor it to Minimize a window to the tray.

How is supposed to detect when a window is being minimized or resized?? I cannot receive any HSHELL_GETMINRECT when minimizing/maximizing a window, only a HSHELL_WINDOWACTIVATED appears to the shell listener. What do you mean with "with a shellhook structure "? Any idea?. Thank you.

SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005

Experiment 5:

The shell receives HSHELL_GETMINRECT ( with a shellhook structure ) whenever a window is being Minimised/Maximised. A script may monitor it to Minimize a window to the tray. Here is a working example:


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 = 5 ) {                  ; HSHELL_GETMINRECT
     wID := NumGet( lParam+0 )         ; first member ( DWord ) of SHELLHOOKINFO structure is hWnd
     WinGet, mState, MinMax, ahk_id %wID%
     If ( mState = -1 ) { ; Window is being minimized
       WinGetTitle, Title, ahk_id %wID%
       TrayTip, Window was minimized!, %Title%
     }
  }
}


@shader: The above example code works for me. Let me know the status so I will update the original post.

Thanks. :)

Edit:

Tested in:
Win XP SP2
Vista Home Premium

shader
  • Members
  • 56 posts
  • Last active: Nov 16 2011 09:05 AM
  • Joined: 29 Oct 2004
Hi Skan, it doesnt work for me. OnMessage never return a 5 wParam message when minimized. I dont know why it doesnt return that message. Thanks.

SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005
What is your OS ? :roll:

shader
  • Members
  • 56 posts
  • Last active: Nov 16 2011 09:05 AM
  • Joined: 29 Oct 2004
XP Pro SP2 spanish

SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005
I am unsure about this, but try replacing

DllCall( "RegisterWindowMessage", Str,"SHELLHOOK" )

with

DllCall( "RegisterWindowMessageW", Str,"SHELLHOOK" )

:roll:

shader
  • Members
  • 56 posts
  • Last active: Nov 16 2011 09:05 AM
  • Joined: 29 Oct 2004
No, it doesnt work. I hope someone can tell if he's having same behaviour or is a fault of my system. Thanks.

Thalon
  • Members
  • 641 posts
  • Last active: Jan 02 2017 12:17 PM
  • Joined: 12 Jul 2005
Does work at XP Prof SP2 German.
Nice thing :)

Thalon

SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005
@Thalon : Thanks for the confirmation :)