Jump to content

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

[LIB] Hook winevents to catch windows creation/destruction


  • Please log in to reply
4 replies to this topic
ABCza
  • Members
  • 132 posts
  • Last active: Jan 04 2015 01:02 AM
  • Joined: 03 Jun 2008
Hola!

The following library is useful to catch windows creation and destruction. It can be used to monitor process creation in usermode as long as a process creates a window (for example, autohotkey scripts trigger the events).

A simple test code is provided inside the library.

; LIB :: ewinhook.ahk
; ~~~~~~~~~~~~~~~~~~~
; This library implements a hook on EVENT_OBJECT_CREATE and EVENT_OBJECT_DESTROY windows events. It makes it possible to catch
; these events being notified through a callback procedure for each required window.
; ------------------------------------------------------------------------------------------------------------------------------
; PUBLIC  FUNCTIONS: ewinhook_CheckForWindowsEvents | ewinhook_StopChecking
; PRIVATE FUNCTIONS: prv_ewinhook_HookProc
; ------------------------------------------------------------------------------------------------------------------------------
; cyrusza   - http://ciroprincipe.info

/*
--------------------------------------------------------------------------------------------------------------------------------
INSTRUCTIONS:
--------------------------------------------------------------------------------------------------------------------------------
To use this library, create a function to receive and manage notifications and pass it to the ewinhook_CheckForWindowsEvents as
a string, together with the list of windows classes/titles (newline separated) that you need to catch. The function must accept
2 parameters: a string containing the event description and the handle of the involved window. At the end of the operations, 
the ewinhook_StopChecking function must be called to unregister the hook.

This is an example of the code needed to be notified of the followings:
1. All AutoHotkey main windows.
2. All Notepad2 windows.
3. Windows that have the words "Firefox, Windows and Paint.NET" in their title.

[code]
#Persistent
DetectHiddenWindows, On
OnExit, QUIT

sSearchList =
(
ahk_class AutoHotkey
ahk_class Notepad2
Firefox
Windows
Paint.NET
)

hWinEventHook := ewinhook_CheckForWindowsEvents("WINEVENT", sSearchList)
Return

QUIT:
ewinhook_StopChecking(hWinEventHook)
ExitApp

WINEVENT(sEvent, hWnd) {
    MsgBox, %sEvent%: %hWnd%
}
[/code]
--------------------------------------------------------------------------------------------------------------------------------
*/

/*
--------------------------------------------------------------------------------------------------------------------------------
FUNCTION: ewinhook_CheckForWindowsEvents
--------------------------------------------------------------------------------------------------------------------------------
This function initializes the hook and returns a handle to it. Can be called at any times to update the windows search list.

PARAMETERS:
~~~~~~~~~~~
sCallback       - Name of the function that will receive the notifications.
sSearchList     - Newline separated list that will contain windows classes or titles to be searched by the hooking procedure.
                  To search for a class, use "ahk_class CLASSNAME"
                  To search for a title, use "TITLE"
                  Window titles search will behave like "SetTitleMatchMode, 2"
--------------------------------------------------------------------------------------------------------------------------------
SYSTEM CALLS AND STRUCTURES:
--------------------------------------------------------------------------------------------------------------------------------
CoInitialize                                - http://msdn.microsoft.com/en-us/library/windows/desktop/ms678543%28v=vs.85%29.aspx
SetWinEventHook                             - http://msdn.microsoft.com/en-us/library/windows/desktop/dd373640%28v=vs.85%29.aspx
--------------------------------------------------------------------------------------------------------------------------------
*/
ewinhook_CheckForWindowsEvents(sCallback, sSearchList="") {
    Global ewinhook_sCallback  := sCallback
         , ewinhook_sClassList := ""
         , ewinhook_sTitleList := ""
         
    Static ewinhook_hWinEventHook
    If (!ewinhook_hWinEventHook)
        DllCall( "CoInitialize", UInt, 0 )
      , ewinhook_hWinEventHook := DllCall( "SetWinEventHook"
                                         ,  UInt, 0x8000            ; EVENT_OBJECT_CREATE
                                         ,  UInt, 0x8001            ; EVENT_OBJECT_DESTROY
                                         ,  UInt, 0
                                         ,  UInt, RegisterCallback("prv_ewinhook_HookProc")
                                         ,  UInt, 0
                                         ,  UInt, 0
                                         ,  UInt, 0                 ; WINEVENT_OUTOFCONTEXT
                                         ,  UInt )
    Loop, PARSE, sSearchList, `n
    {
        If (InStr(A_LoopField, "ahk_class"))
            ewinhook_sClassList .= RegExReplace(A_LoopField, "ahk_class ") . "`n"
        Else
            ewinhook_sTitleList .= A_LoopField . "`n"
    }
    Return ewinhook_hWinEventHook
}

/*
--------------------------------------------------------------------------------------------------------------------------------
FUNCTION: ewinhook_StopChecking
--------------------------------------------------------------------------------------------------------------------------------
This function deinitializes the hook.

PARAMETERS:
~~~~~~~~~~~
hWinEventHook   - Name of the function that will receive the notifications.
--------------------------------------------------------------------------------------------------------------------------------
SYSTEM CALLS AND STRUCTURES:
--------------------------------------------------------------------------------------------------------------------------------
UnhookWinEvent                              - http://msdn.microsoft.com/en-us/library/windows/desktop/dd373671%28v=vs.85%29.aspx
CoUninitialize                              - http://msdn.microsoft.com/en-us/library/windows/desktop/ms688715%28v=vs.85%29.aspx
--------------------------------------------------------------------------------------------------------------------------------
*/
ewinhook_StopChecking(hWinEventHook) {
    DllCall( "UnhookWinEvent", UInt, hWinEventHook )
    DllCall( "CoUninitialize" )
}

/*
--------------------------------------------------------------------------------------------------------------------------------
FUNCTION: prv_ewinhook_HookProc
--------------------------------------------------------------------------------------------------------------------------------
This is the hooking procedure that will be called by the system when the events are triggered. It checks the windows objects and
calls the callback if it finds a match.

PARAMETERS:
~~~~~~~~~~~
hWinEventHook   - Handle to the current hook.
event           - Event type. Can be EVENT_OBJECT_CREATE = 0x8000 or EVENT_OBJECT_DESTROY = 0x8001.
hWnd            - Handle to the window that triggered the event.
idObject        - Id of the object associated with the event.
idChild         - Identifies if the event was triggered by the object or by a child element of the object.
dwEventThread   - Identifies the thread that generated the event or that owns the window.
dwmsEventTime   - Times in ms that the event was generated.
--------------------------------------------------------------------------------------------------------------------------------
*/
prv_ewinhook_HookProc(hWinEventHook, event, hWnd, idObject, idChild, dwEventThread, dwmsEventTime) {
    Global ewinhook_sCallback
         , ewinhook_sClassList
         , ewinhook_sTitleList
         
    WinGetClass, sClass, ahk_id %hWnd%
    WinGetTitle, sTitle, ahk_id %hWnd%

    bFound := 0
    Loop, PARSE, ewinhook_sClassList, `n
        If (A_LoopField && InStr(sClass, A_LoopField))
            bFound := 1
    Loop, PARSE, ewinhook_sTitleList, `n
        If (A_LoopField && InStr(sTitle, A_LoopField))
            bFound := 1
            
    If (bFound)
        %ewinhook_sCallback%((event == 0x8000) ? "Created" : "Destroyed", hWnd)
}

Changelog:
Oct. 6, 2012 - Fixed some wrong behaviours.
All my scripts/snippets are released under the WTFPL: http://sam.zoy.org/wtfpl/COPYING

  • Guests
  • Last active:
  • Joined: --
Interesting. I didn't look too deep, but is it possible to suppress window creation/destruction with this? Or is it only notification purposed like a shell hook?

ABCza
  • Members
  • 132 posts
  • Last active: Jan 04 2015 01:02 AM
  • Joined: 03 Jun 2008

Interesting. I didn't look too deep, but is it possible to suppress window creation/destruction with this? Or is it only notification purposed like a shell hook?


You get the window handle, so you can do a lot with it. Of course it's a shell hook, so it doesn't intercept windows creation/destruction, it's based on notifications.
All my scripts/snippets are released under the WTFPL: http://sam.zoy.org/wtfpl/COPYING

evilc
  • Members
  • 340 posts
  • Last active: Oct 27 2015 11:07 PM
  • Joined: 17 Nov 2005

I could do with something like this and started having a play, but something does not seem quite right.

 

prv_ewinhook_HookProc gets called a lot. Like when I open a notepad window by double clicking on a shortcut, it gets called about 10 times. Just opening the start menu calls it 20 times.

 

MSDN EVENT_OBJECT_CREATE:

An object has been created. The system sends this event for the following user interface elements: caret, header controllist-view controltab controltoolbar controltree view control, and window object. 

 

Uh huh. That looks to be the reason. It's calling for all kinds of gui elements, not just applications.

Also, prv_ewinhook_HookProc seems inefficient. The Loop, PARSE blocks don't break when a match is found.

 

The loop inefficiencies can be solved, but does anyone know how to reduce the number of calls, or a quick way to filter out the ones we are not interested in?

 

 



Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006
I'd guess that events for a window would have idObject=OBJID_WINDOW and idChild=CHILDID_SELF, so check those. Then if you only want top-level windows, check for the WS_CHILD style.

Rather than loop parse, you could change the delimiter from `n to , and use just if sClass contains %ewinhook_sClassList%...

Better yet, use a window group rather than lists of titles and classes. e.g. if WinExist("ahk_id " hWnd " ahk_group ewinhook"). Use GroupAdd ewinhook, ... to add titles or classes to the group.