AutoHotkey Homepage AutoHotkey Community
Let's help each other out
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

WinEventHook example...
Goto page Previous  1, 2, 3, 4, 5, 6  Next
 
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions
View previous topic :: View next topic  
Author Message
majkinetor



Joined: 24 May 2006
Posts: 3626
Location: Belgrade

PostPosted: Mon May 21, 2007 10:27 am    Post subject: Reply with quote

Try this one. Tooltip will show info, not the DebugView

Code:
CoordMode, tooltip, screen
DetectHiddenWindows, On
   event :=  0x00008000   ;EVENT_OBJECT_CREATE
   hwnd:=WinExist("ahk_pid " ` DllCall("GetCurrentProcessId","Uint"))
   msg := 0x550
   hook_dll = wineventhook\wineventhook.dll

   hHookModule := API_LoadLibrary("wineventhook\wineventhook.dll")
   hHook := Hook(hwnd, msg, event, event, "HookHandler")
return

!e::
   Msgbox % "Unhook: " Unhook(hHook,msg)
return
   
HookHandler(wParam, lParam, msg, hwnd) {
   static ss
   GetHookParams(lparam, event, hwnd, idObject, idChild, dwEventThread, dwmsEventTime)      

   WinGetTitle, text, ahk_id %hwnd%
   WinGetClass, class, ah`k_id %hwnd%

   s = %text% | C:%class% | HWND: %hwnd% | Obj: %idObject% | Child:%idChild% | Thrd:%dwEventThread% | Time:%dwmsEventTime%
   
   if StrLen(ss) > 2000
      ss := s "`n"
   else ss .= s  "`n"`

   Tooltip %ss%, 0, 0`
}

GetHookParams(lparam, ByRef event, ByRef hwnd="", ByRef idObject="", ByRef idChild="", ByRef dwEventThread="", ByRef dwmsEventTime="") {
   event         :=GetDeRefInteger(lParam+4)
   hwnd         :=GetDeRefInteger(lParam+8)
   idObject      :=GetDeRefInteger(lParam+12)
   idChild         :=GetDeRefInteger(lParam+16)
   dwEventThread   :=GetDeRefInteger(lParam+20)
   dwmsEventTime   :=GetDeRefInteger(lParam+24)
}

Hook(comm_hwnd, comm_msg, s_event, e_event, function, wparam=0) {
   global hook_dll
   
   r := DllCall(hook_dll "\reghook", "UInt", comm_hwnd, "UInt", COMM_MSG, "UInt", s_event, "UInt", e_event, "UInt", wparam)
   if !r
      return 0

   OnMessage(COMM_MSG, function)
   return r
}

Unhook(handle, com_msg) {
   OnMessage(com_msg)
   return DllCall("UnhookWinEvent", "UInt", handle)
}

API_LoadLibrary( dll ) {
   return DllCall("LoadLibrary", "str", dll)
}

InsertInteger(pInteger, ByRef pDest, pOffset = 0, pSize = 4)
; The caller must ensure that pDest has sufficient capacity.  To preserve any existing contents in pDest,
; only pSize number of bytes starting at pOffset are altered in it.
{
   Loop %pSize%  ; Copy each byte in the integer into the structure as raw binary data.
      DllCall("RtlFillMemory", UInt, &pDest + pOffset + A_Index-1, UInt, 1, UChar, pInteger >> 8*(A_Index-1) & 0xFF)
}

ExtractInteger(ByRef pSource, pOffset = 0, pIsSigned = false, pSize = 4)
; pSource is a string (buffer) whose memory area contains a raw/binary integer at pOffset.
; The caller should pass true for pSigned to interpret the result as signed vs. unsigned.
; pSize is the size of PSource's integer in bytes (e.g. 4 bytes for a DWORD or Int).
; pSource must be ByRef to avoid corruption during the formal-to-actual copying process
; (since pSource might contain valid data beyond its first binary zero).
{
   Loop %pSize%  ; Build the integer by adding up its bytes.
      result += *(&pSource + pOffset + A_Index-1) << 8*(A_Index-1)
   if (!pIsSigned OR pSize > 4 OR result < 0x80000000)
      return result  ; Signed vs. unsigned doesn't matter in these cases.
   ; Otherwise, convert the value (now known to be 32-bit) to its signed counterpart:
   return -(0xFFFFFFFF - result + 1)
}


GetDeRefInteger(pSource, pIsSigned = false, pSize = 4)
; pSource is an integer pointer to a raw/binary integer
; The caller should pass true for pSigned to interpret the result as signed vs. unsigned.
; pSize is the size of PSource's integer in bytes (e.g. 4 bytes for a DWORD or Int).
{
   Loop %pSize%  ; Build the integer by adding up its bytes.
      result += *(pSource + A_Index-1) << 8*(A_Index-1)
   if (!pIsSigned OR pSize > 4 OR result < 0x80000000)
      return result  ; Signed vs. unsigned doesn't matter in these cases.
   ; Otherwise, convert the value (now known to be 32-bit) to its signed counterpart:
   return -(0xFFFFFFFF - result + 1)
}

SetDeRefInteger(pInteger, pDest, pSize = 4)
; The caller must ensure that *pDest has sufficient capacity and that pDest is a valid dereferencable integer pointer.
; To preserve any existing contents at *pDest, only pSize number of bytes are altered.
{
   Loop %pSize%  ; Copy each byte in the integer into the structure as raw binary data.
      DllCall("RtlFillMemory", UInt, pDest + A_Index-1, UInt, 1, UChar, pInteger >> 8*(A_Index-1) & 0xFF)
}

_________________


Last edited by majkinetor on Mon May 21, 2007 10:54 am; edited 1 time in total
Back to top
View user's profile Send private message MSN Messenger
majkinetor



Joined: 24 May 2006
Posts: 3626
Location: Belgrade

PostPosted: Mon May 21, 2007 10:52 am    Post subject: Reply with quote

The problem with this hook is that it doesn't prevent things.

In normal hook, I can choose if to alow action or not. Take this for example:

Code:
HookHandler(wParam, lParam, msg, hwnd) {
   GetHookParams(lparam, event, hwnd)


   WinGetClass, class, ah`k_id %hwnd%
   if class = Notepad
      msgbox notepad is about to be activated
}


here notepad starts although HookHandler still didn't return. Using normal hook, i can return 0 to prevent OS from creating Notepad class or 1 to allow it.
_________________
Back to top
View user's profile Send private message MSN Messenger
majkinetor



Joined: 24 May 2006
Posts: 3626
Location: Belgrade

PostPosted: Mon May 21, 2007 11:24 am    Post subject: Reply with quote

Real life sample:

Docking. Little gui will be docked to Notepad. Timer is activated only when you move Notepad around and turned off when you stop. Otherwise it runs continuosly. This is only example to see how things work. Real application wouldn't use timers at all but this Accessibilty hook doesn't report window movement, just start and stop of that event.

Code:
SetBatchLines, -1
CoordMode, tooltip, screen
DetectHiddenWindows, On
   EVENT_SYSTEM_MOVESIZESTART  :=  0xA    
   EVENT_SYSTEM_MOVESIZEEND   :=  0xB   
   hwnd:=WinExist("ahk_pid " . DllCall("GetCurrentProcessId","Uint"))
   msg := 0x550
   hook_dll = wineventhook\wineventhook.dll

   Gui, +LastFound + ToolWindow
   hGui := WinExist()
   Gui, Add, Text, ,Open Notepad`nand move it around
   Gui, Show, AutoSize

   hHookModule := API_LoadLibrary( hook_dll )
   hHook := Hook(hwnd, msg, EVENT_SYSTEM_MOVESIZESTART, EVENT_SYSTEM_MOVESIZEEND, "HookHandler")

return

Dock:
     WinGetClass cls, ahk_id %WinHwnd%

     if (cls != "Notepad")
         return
           
     WinGetPos, hostX, hostY, hostW, hostH,  ahk_id %WinHwnd%

     x := hostX + hostW
     WinMove, ahk_id %hGui%, ,%x%, %hostY%
return

Undock:
     Tooltip
return

!e:
   Msgbox % "Unhook: " Unhook(hHook,msg)
return
   
HookHandler(wParam, lParam, msg, hwnd) {
   global  EVENT_SYSTEM_MOVESIZESTART, EVENT_SYSTEM_MOVESIZEEND, WinHwnd
   static ss

   GetHookParams(lparam, e, WinHwnd)      

   if (e = EVENT_SYSTEM_MOVESIZESTART)
      SetTimer Dock, 0

   if (e = EVENT_SYSTEM_MOVESIZEEND){
      SetTimer Dock, Off
      gosub Undock
   }

;   s = E:%e% | C:%class% | HWND: %hwnd%
;   ss .= s  "`n"
;   Tooltip, %ss%, 0, 0, 19
}

Now it can be upgraded this way:

- monitor EVENT_OBJECT_SHOWE to detect when Notepad is created and dock on its startup
- monitor FOREGROUND to move docking window in Z-order according to Notepad
- EVENT_OBJECT_DESTROY or HIDE to to hide dock window when Notepad is destroyed/hiden.

GetHookParams(lparam, ByRef event, ByRef hwnd="", ByRef idObject="", ByRef idChild="", ByRef dwEventThread="", ByRef dwmsEventTime="") {
   event         :=GetDeRefInteger(lParam+4)
   hwnd         :=GetDeRefInteger(lParam+8)
   idObject      :=GetDeRefInteger(lParam+12)
   idChild         :=GetDeRefInteger(lParam+16)
   dwEventThread   :=GetDeRefInteger(lParam+20)
   dwmsEventTime   :=GetDeRefInteger(lParam+24)
}

Hook(comm_hwnd, comm_msg, s_event, e_event, function, wparam=0) {
   global hook_dll
   
   r := DllCall(hook_dll "\reghook", "UInt", comm_hwnd, "UInt", COMM_MSG, "UInt", s_event, "UInt", e_event, "UInt", wparam)
   if !r
      return 0

   OnMessage(COMM_MSG, function)
   return r
}

Unhook(handle, com_msg) {
   OnMessage(com_msg)
   return DllCall("UnhookWinEvent", "UInt", handle)
}

API_LoadLibrary( dll ) {
   return DllCall("LoadLibrary", "str", dll)
}

InsertInteger(pInteger, ByRef pDest, pOffset = 0, pSize = 4)
; The caller must ensure that pDest has sufficient capacity.  To preserve any existing contents in pDest,
; only pSize number of bytes starting at pOffset are altered in it.
{
   Loop %pSize%  ; Copy each byte in the integer into the structure as raw binary data.
      DllCall("RtlFillMemory", UInt, &pDest + pOffset + A_Index-1, UInt, 1, UChar, pInteger >> 8*(A_Index-1) & 0xFF)
}

ExtractInteger(ByRef pSource, pOffset = 0, pIsSigned = false, pSize = 4)
; pSource is a string (buffer) whose memory area contains a raw/binary integer at pOffset.
; The caller should pass true for pSigned to interpret the result as signed vs. unsigned.
; pSize is the size of PSource's integer in bytes (e.g. 4 bytes for a DWORD or Int).
; pSource must be ByRef to avoid corruption during the formal-to-actual copying process
; (since pSource might contain valid data beyond its first binary zero).
{
   Loop %pSize%  ; Build the integer by adding up its bytes.
      result += *(&pSource + pOffset + A_Index-1) << 8*(A_Index-1)
   if (!pIsSigned OR pSize > 4 OR result < 0x80000000)
      return result  ; Signed vs. unsigned doesn't matter in these cases.
   ; Otherwise, convert the value (now known to be 32-bit) to its signed counterpart:
   return -(0xFFFFFFFF - result + 1)
}


GetDeRefInteger(pSource, pIsSigned = false, pSize = 4)
; pSource is an integer pointer to a raw/binary integer
; The caller should pass true for pSigned to interpret the result as signed vs. unsigned.
; pSize is the size of PSource's integer in bytes (e.g. 4 bytes for a DWORD or Int).
{
   Loop %pSize%  ; Build the integer by adding up its bytes.
      result += *(pSource + A_Index-1) << 8*(A_Index-1)
   if (!pIsSigned OR pSize > 4 OR result < 0x80000000)
      return result  ; Signed vs. unsigned doesn't matter in these cases.
   ; Otherwise, convert the value (now known to be 32-bit) to its signed counterpart:
   return -(0xFFFFFFFF - result + 1)
}

SetDeRefInteger(pInteger, pDest, pSize = 4)
; The caller must ensure that *pDest has sufficient capacity and that pDest is a valid dereferencable integer pointer.
; To preserve any existing contents at *pDest, only pSize number of bytes are altered.
{
   Loop %pSize%  ; Copy each byte in the integer into the structure as raw binary data.
      DllCall("RtlFillMemory", UInt, pDest + A_Index-1, UInt, 1, UChar, pInteger >> 8*(A_Index-1) & 0xFF)
}


Anyway, although this work is very usable I think U should wrap SetWindowHookEx instead SetWinEventHook as first one is much more flexibile.
_________________
Back to top
View user's profile Send private message MSN Messenger
new_noobie
Guest





PostPosted: Mon May 21, 2007 11:34 am    Post subject: Reply with quote

@majkinetor
I tried your script and it did not (seem) to work. No tooltip was produced,
but when I hit !e a message box came up with "Unhook: 0".

I had to make some changes to the script as posted. Did I not do something
correctly?

thx for help

Changes:
hHookModule := API_LoadLibrary("wineventhook\wineventhook.dll") to
to
hHookModule := API_LoadLibrary("callback.dll") ; callback.dll is in script dir

!e: to !e::

else ss .= s "`n"` to else ss .= s "`n"

Tooltip %ss%, 0, 0` to Tooltip %ss%, 0, 0
Back to top
majkinetor



Joined: 24 May 2006
Posts: 3626
Location: Belgrade

PostPosted: Mon May 21, 2007 12:45 pm    Post subject: Reply with quote

You shouldn't change anything.

e: was error, I fixed that.
` was error too

Its because I tested with ` hotkey so it accidently was written in the code.[/quote]
_________________
Back to top
View user's profile Send private message MSN Messenger
new_noobie
Guest





PostPosted: Mon May 21, 2007 1:04 pm    Post subject: Reply with quote

@majkinetor
Well, I am definitly not understanding something here.
I don't have wineventhook\wineventhook.dll.
I even did an msdn and google search for this dll and found nothing.

I had assumed that this script/thead was about using "callback.dll".
Have I completely missed the point of this thread??

In any case, I re-copied the script as posted here, and got the same
results - no tooltip, and !e gets me a msgbox with "Unhook: 0"

I hate to be a pain, but ???
please explain
Back to top
majkinetor



Joined: 24 May 2006
Posts: 3626
Location: Belgrade

PostPosted: Mon May 21, 2007 1:05 pm    Post subject: Reply with quote

LOL

Here it is for you: dock script and dll


Quote:
Have I completely missed the point of this thread??

Yes.
_________________
Back to top
View user's profile Send private message MSN Messenger
new_noonir
Guest





PostPosted: Mon May 21, 2007 1:29 pm    Post subject: Reply with quote

Very Happy Very Happy Many Thanks Very Happy Very Happy
@majkinetor
Now I see it working - that docking is real cool.
I don't know where I went off track, but this works and now I have
something I can try to understand.

I appreciate you patience.

Very Happy Very Happy Many Thanks Very Happy Very Happy
Back to top
majkinetor



Joined: 24 May 2006
Posts: 3626
Location: Belgrade

PostPosted: Mon May 21, 2007 1:57 pm    Post subject: Reply with quote

majkinetor wrote:
Anyway, although this work is very usable I think U should wrap SetWindowHookEx instead SetWinEventHook as first one is much more flexibile.

Acctually on second thought, I think we should have both hook types availalble. I see now that some things are more appropriate to do with SetWinEventHook and some with SetWindowsHookEx.

Are you willing to do this, or should I change your dlll code?

Quote:
Now I see it working - that docking is real cool.

Its not bad, far from real cool. With SetWindowsHookEx, realy cool docking is possible. SetWinEventHook can't monitor window messages so I can not track down WM_MOVING to reposition docking client before docking host is moved (thus making them look like the single window contrary to current situation where docking client quickly chace docking host)
_________________
Back to top
View user's profile Send private message MSN Messenger
Sean



Joined: 12 Feb 2007
Posts: 1338

PostPosted: Mon May 21, 2007 2:16 pm    Post subject: Re: WinEventHook example... Reply with quote

JGR wrote:
http://www.autohotkey.net/~JGR/wineventhook.rar

I translate it using callback.dll.

Code:
DetectHiddenWindows, On
Process, Exist
hAHK := WinExist("ahk_pid " . ErrorLevel)

WM_AHK_Callback := DllCall("RegisterWindowMessage", "str", "WM_AHK_Callback")
OnMessage(WM_AHK_Callback, "WinEvents")

hModule   := DllCall("LoadLibrary", "str", "callback")
hCallback := DllCall("callback\callbackit", "Uint", 7, "Uint", hAHK, "Uint", WM_AHK_Callback, "Uint", 0)
hEventHook1 := DllCall("SetWinEventHook", "Uint", 3, "Uint", 3, "Uint", hModule, "Uint", hCallback, "Uint", 0, "Uint", 0, "Uint", 0)
hEventHook2 := DllCall("SetWinEventHook", "Uint", 0x8000, "Uint", 0x8000, "Uint", hModule, "Uint", hCallback, "Uint", 0, "Uint", 0, "Uint", 0)

AppsKey & End::
DllCall("UnhookWinEvent", "Uint", hEventHook1)
DllCall("UnhookWinEvent", "Uint", hEventHook2)
Return
   
WinEvents(wParam, lParam)
{
   DetectHiddenWindows, On
   lParam += 4
   Event:= *lParam++|*lParam++<<8|*lParam++<<16|*lParam++<<24
   hWnd := *lParam++|*lParam++<<8|*lParam++<<16|*lParam++<<24
   WinGetTitle, sTitle, ahk_id %hWnd%
   WinGetClass, sClass, ahk_id %hWnd%
   If Event = 3            ;window foreground
      OutputDebug, AHK: Window Foreground: %sTitle% -- %sClass%
   Else If Event = 0x8000         ;(sub)window creation
      OutputDebug, AHK: Window: %sTitle% -- %sClass%
}
Back to top
View user's profile Send private message
JGR



Joined: 15 Jun 2006
Posts: 52
Location: Unavailable until ~30th August

PostPosted: Mon May 21, 2007 3:50 pm    Post subject: Reply with quote

SetWindowsHookEx dll is posted earlier in this thread here, you are advised to read the preceding posts before trying to use it.
As for changing my DLL code, unless you write ASM, don't try.

The example (partially successfully) hooks CBT but a message hook could be done, but it will cause a *massive* performance hit, unless you only monitor certain threads/processes.
The HCBT_MOVESIZE event of CBT should be good for window docking.

Note that Notepad (and explorer) refused to load the hook dll when I tried it.
The DLL must be injected into every process which needs to be monitored, with the distinct possibility of crashing/hanging your whole system.
That is why the SetWinEventHook should be used instead where possible.

If you want to monitor messages of a specific process, it may be easier just to subclass (or is it superclass?) the window at runtime, with the address of an injected WndProc dll, to cut down on context switch and AHK overhead...

JGR
Back to top
View user's profile Send private message
majkinetor



Joined: 24 May 2006
Posts: 3626
Location: Belgrade

PostPosted: Mon May 21, 2007 4:13 pm    Post subject: Reply with quote

Quote:
As for changing my DLL code, unless you write ASM, don't try.
The code needed for generic wrappers of hooks and callbacks is trivial, language is not important, it can be Chinese if you ask me. I choose the language according to the task I have, not vice-versa like ppl do - to model the problem according to the language.

Quote:
with the distinct possibility of crashing/hanging your whole system.

I know.
I had several extensive hooking projects in the past (used Object Pascal & C), so I know that global hooks can be pretty nasty. I remember messages I got back in time where something I have never seen before and after in Windows... You don't have to neceserally crash the system. Some errors make your next tries impossible until you restart the system as hook is hard to kill once it is injected.

Its good you did it in ASM, and I thank you for that as I was planning to do it myself the same way for both hooks and callbacks. This is the right way as hook is injected in every process so it must be as small and fast as possible. After looking at your hook dll code I can say that I doubt it can be faster and smaller.
_________________


Last edited by majkinetor on Mon May 21, 2007 4:31 pm; edited 1 time in total
Back to top
View user's profile Send private message MSN Messenger
JGR



Joined: 15 Jun 2006
Posts: 52
Location: Unavailable until ~30th August

PostPosted: Mon May 21, 2007 4:31 pm    Post subject: Reply with quote

Quote:
The code needed for generic wrappers of hooks and callbacks is trivial, language is not important, it can be Chinese if you ask me.

If only... It took me a fair while to get the stack correction and position independent code for the callback DLL perfect, and even longer to get the shared data system for multiple contexts/addresses for the SetWindowsHookEx right.

The only way to make it faster would be to have the callback/hook not call AHK at all, or store something in memory, but that would require DLLs specialised for the application...

Quote:
Its good you did it in ASM

Variable parameter STDCALL/Pascal calling convention functions are not supported by ANY other language... Smile
As for making it smaller, by reducing the file alignment I got it down to 990 bytes, but Windows refused to load it Sad

Note that the SetWinEventHook DLL is not injected into every process (that option is turned off in the DLL code, this worsens performance, but is much easier, more reliable and event are guaranteed to be sequential...).

JGR
Back to top
View user's profile Send private message
majkinetor



Joined: 24 May 2006
Posts: 3626
Location: Belgrade

PostPosted: Mon May 21, 2007 4:36 pm    Post subject: Reply with quote

Quote:
If only...

Hook is trivial. Callback is more complicated because of things you mentiond.

Quote:
The only way to make it faster would be to have the callback/hook not call AHK at all, or store something in memory, but that would require DLLs specialised for the application...

Indeed.

Quote:
Variable parameter STDCALL/Pascal calling convention functions are not supported by ANY other language...

Indeed.

Quote:
Note that the SetWinEventHook DLL is not injected into every process

Nor SetWindowHookEx until process is hooked. It doesn't have to be hooked depending on the type of the hook U are doing. For instance, you can monitor activation, but certain window doesn't have to be activated ever, so it will not be hooked AFAIK. I remember I had to manuely hook the window I am interested to have imediate hook - like, in previously mentioned example to SetActiveWindow after seting the hook up.
_________________
Back to top
View user's profile Send private message MSN Messenger
JGR



Joined: 15 Jun 2006
Posts: 52
Location: Unavailable until ~30th August

PostPosted: Mon May 21, 2007 4:45 pm    Post subject: Reply with quote

Quote:
Nor SetWindowHookEx until process is hooked. It doesn't have to be hooked depending on the type of the hook U are doing. For instance, you can monitor activation, but certain window doesn't have to be activated ever, so it will not be hooked AFAIK.

If you are using the CBT hook, any one of the events monitored by CBT will cause the hook to be loaded, even if you're not particularly interested in it.

If you are just monitoring one window, it is easier to just inject a DLL and reassign the WndProc to it using SetWindowLong, and filter the messages, than to use a hook IMO.
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions All times are GMT
Goto page Previous  1, 2, 3, 4, 5, 6  Next
Page 2 of 6

 
Jump to:  
You can post new topics in this forum
You can reply to topics in this forum


Powered by phpBB © 2001, 2005 phpBB Group