 |
AutoHotkey Community Let's help each other out
|
| View previous topic :: View next topic |
| Author |
Message |
majkinetor
Joined: 24 May 2006 Posts: 3626 Location: Belgrade
|
Posted: Mon May 21, 2007 10:27 am Post subject: |
|
|
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 |
|
 |
majkinetor
Joined: 24 May 2006 Posts: 3626 Location: Belgrade
|
Posted: Mon May 21, 2007 10:52 am Post subject: |
|
|
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 |
|
 |
majkinetor
Joined: 24 May 2006 Posts: 3626 Location: Belgrade
|
Posted: Mon May 21, 2007 11:24 am Post subject: |
|
|
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 |
|
 |
new_noobie Guest
|
Posted: Mon May 21, 2007 11:34 am Post subject: |
|
|
@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
|
Posted: Mon May 21, 2007 12:45 pm Post subject: |
|
|
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 |
|
 |
new_noobie Guest
|
Posted: Mon May 21, 2007 1:04 pm Post subject: |
|
|
@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
|
Posted: Mon May 21, 2007 1:05 pm Post subject: |
|
|
LOL
Here it is for you: dock script and dll
| Quote: | | Have I completely missed the point of this thread?? |
Yes. _________________
 |
|
| Back to top |
|
 |
new_noonir Guest
|
Posted: Mon May 21, 2007 1:29 pm Post subject: |
|
|
Many Thanks
@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.
Many Thanks  |
|
| Back to top |
|
 |
majkinetor
Joined: 24 May 2006 Posts: 3626 Location: Belgrade
|
Posted: Mon May 21, 2007 1:57 pm Post subject: |
|
|
| 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 |
|
 |
Sean
Joined: 12 Feb 2007 Posts: 1338
|
Posted: Mon May 21, 2007 2:16 pm Post subject: Re: WinEventHook example... |
|
|
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 |
|
 |
JGR
Joined: 15 Jun 2006 Posts: 52 Location: Unavailable until ~30th August
|
Posted: Mon May 21, 2007 3:50 pm Post subject: |
|
|
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 |
|
 |
majkinetor
Joined: 24 May 2006 Posts: 3626 Location: Belgrade
|
Posted: Mon May 21, 2007 4:13 pm Post subject: |
|
|
| 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 |
|
 |
JGR
Joined: 15 Jun 2006 Posts: 52 Location: Unavailable until ~30th August
|
Posted: Mon May 21, 2007 4:31 pm Post subject: |
|
|
| 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...
As for making it smaller, by reducing the file alignment I got it down to 990 bytes, but Windows refused to load it
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 |
|
 |
majkinetor
Joined: 24 May 2006 Posts: 3626 Location: Belgrade
|
Posted: Mon May 21, 2007 4:36 pm Post subject: |
|
|
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 |
|
 |
JGR
Joined: 15 Jun 2006 Posts: 52 Location: Unavailable until ~30th August
|
Posted: Mon May 21, 2007 4:45 pm Post subject: |
|
|
| 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 |
|
 |
|
|
You can post new topics in this forum You can reply to topics in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|