alexanderdillard Posted May 11, 2014 Share Posted May 11, 2014 Warning: The code I have posted is relatively low-level system code. As such, please do not attempt to run it unless you are sure you know what you are doing. Furthermore, don't attempt to run it until after reading my entire post. Simple version of my question: Can AutoIt be used to install system hooks of the WH_CALLWNDPROC type? Preliminary (presently very unsure) answer: perhaps not... Slightly more detail: I have been attempting to create a WH_CALLWNDPROC type hook procedure using _WinAPI_SetWindowsHookEx() but so far my efforts have failed. If you know how to do this properly (or are certain AutoIt can't do it...) please advise. A lot more detail: What I originally set out to do was create a script that always knows the handle of the window which was active prior to the script becoming active. My initial solution was to use GUIRegisterMsg($WM_NCLBUTTONDOWN, 'titleBarClick') and then in my 'titleBarClick' function do the following: 1: Use _WinAPI_GetForegroundWindow() to get the desired handle. 2: Make my script active with _WinAPI_SetForegroundWindow() Unfortunately, this only works correctly if my script is activated by clicking its 'Non-Client area' (the title bar). One situation which is particularly slippery is if my script gets activated by the user clicking its taskbar button at the bottom of the screen. (Refer to the following regarding the taskbar activation conundrum: http://stackoverflow.com/questions/885623/get-window-handle-of-last-activated-window) So, based on the idea in the above Stack Overflow link I decided attempt a more generalized solution to getting the handle of the prior active window by hooking WH_CALLWNDPROC and keeping track of all WM_ACTIVATEAPP messages. My starting point was the _WinAPI_SetWindowsHookEx() example code found at http://www.autoitscript.com/autoit3/docs/libfunctions/_WinAPI_SetWindowsHookEx.htm After going through the example code I removed all the 'fluff' and reduced it down to a prototype of sorts: #include <WinAPI.au3> Global $hHook, $hStub_MyProc Local $hmod OnAutoItExitRegister("Cleanup") $hStub_MyProc = DllCallbackRegister("_MyProc", "LRESULT", "int;wparam;lparam") $hmod = _WinAPI_GetModuleHandle(0) $hHook = _WinAPI_SetWindowsHookEx($WH_KEYBOARD_LL, DllCallbackGetPtr($hStub_MyProc), $hmod) While 1 Sleep(10) WEnd Func _MyProc($nCode, $wParam, $lParam) Return _WinAPI_CallNextHookEx($hHook, $nCode, $wParam, $lParam) EndFunc Func Cleanup() _WinAPI_UnhookWindowsHookEx($hHook) DllCallbackFree($hStub_MyProc) EndFunc In the above prototype code the hook procedure type is set to WH_KEYBOARD_LL, which seems to work fine. However, after testing the above with all 15 of the hook procedure types / idHook values I have found it works for only 12 of the 15. The following three procedure types give problems: WH_CALLWNDPROC, WH_CALLWNDPROCRET and WH_DEBUG. Note that 'problems' is probably an understatement, on my system (XP SP3) swapping any of the three 'problematic' hook procedure types in to the above code totally crashes the OS (killing AutoIt with the Task Manager doesn't help -- a reboot is required). In fact, the crash is about the most violent I have seen in XP ever (it even triggers Data Execution Prevention / DEP). I'm not sure what would happen in Windows 7. If you experiment with the above code please be extremely careful. One observation: in the example code at http://www.autoitscript.com/autoit3/docs/libfunctions/_WinAPI_SetWindowsHookEx.htm the DllCallbackRegister() return type is set to "long" but it my code the return type is set to "LRESULT". My decision to use "LRESULT" as the return type is based on the MSDN Library Windows API documentation at http://msdn.microsoft.com/query/dev10.query?appId=Dev10IDEF1&l=EN-US&k=k(SetWindowsHookEx);k(DevLang-C);k(TargetOS-WINDOWS)&rd=true If anyone has comments on which return type is correct I'd be interested in that as well. Link to comment Share on other sites More sharing options...
LarsJ Posted May 14, 2014 Share Posted May 14, 2014 Try this:expandcollapse popup#include <GUIConstantsEx.au3> #include <WinAPI.au3> Opt( "MustDeclareVars", 1 ) Global Const $tagCWPSTRUCT = "lparam lParam;wparam wParam;uint message;hwnd hwnd" Global $hHook MainScript() Func MainScript() Local $hGui = GUICreate( "Test", 300, 200, -1, 300 ) Local $hStub_MyProc = DllCallbackRegister("_MyProc", "LRESULT", "int;wparam;lparam") $hHook = _WinAPI_SetWindowsHookEx($WH_CALLWNDPROC, DllCallbackGetPtr($hStub_MyProc), 0, _WinAPI_GetCurrentThreadId()) GUISetState() While 1 Switch GUIGetMsg() Case $GUI_EVENT_CLOSE ExitLoop EndSwitch WEnd GUIDelete( $hGui ) _WinAPI_UnhookWindowsHookEx($hHook) DllCallbackFree($hStub_MyProc) Exit EndFunc Func _MyProc($nCode, $wParam, $lParam) Local $tCWPSTRUCT = DllStructCreate( $tagCWPSTRUCT, $lParam ) Local $hWnd = DllStructGetData( $tCWPSTRUCT, "hwnd" ) ConsoleWrite( "$hWnd = " & $hWnd & @CRLF ) Return _WinAPI_CallNextHookEx($hHook, $nCode, $wParam, $lParam) EndFuncTo see some events in Scite console, move the "Test" window around with the mouse, minimize and restore the window, and switch forth and back between other windows. Controls, File Explorer, ROT objects, UI Automation, Windows Message MonitorCompiled code: Accessing AutoIt variables, DotNet.au3 UDF, Using C# and VB codeShell menus: The Context menu, The Favorites menu. Shell related: Control Panel, System Image ListsGraphics related: Rubik's Cube, OpenGL without external libraries, Navigating in an image, Non-rectangular selectionsListView controls: Colors and fonts, Multi-line header, Multi-line items, Checkboxes and icons, Incremental searchListView controls: Virtual ListViews, Editing cells, Data display functions Link to comment Share on other sites More sharing options...
alexanderdillard Posted May 16, 2014 Author Share Posted May 16, 2014 LarsJ, After analyzing your code I am fairly sure it doesn't quite solve my problem. Very neat example though! I tried your code, which works fine on my system but as far as I can only registers events associated with itself. What I'm really looking for is a way to detect every single WM_ACTIVATEAPP event no matter which running program it is associated with. I'm guessing your code is limitied to monitoring itself because you set $dwThreadId to the return value of _WinAPI_GetCurrentThreadId(). I tried changing $dwThreadId to 0, since the AutoIt and MSDN documentation seem to indicate that would lead to all threads being monitored. However, after making that change it appears the hook procedure _MyProc() is never called. So I remain confused. Also, why set $hmod to 0 rather than the return value of _WinAPI_GetModuleHandle(0)? Link to comment Share on other sites More sharing options...
LarsJ Posted May 16, 2014 Share Posted May 16, 2014 This is just a simple example that shows how to use a $WH_CALLWNDPROC hook.The main issue for catching global events is that the callback procedure must be implemented in a DLL. This is very clearly described in the MSDN documentation. And then you use $hMod as a handle for the DLL. Controls, File Explorer, ROT objects, UI Automation, Windows Message MonitorCompiled code: Accessing AutoIt variables, DotNet.au3 UDF, Using C# and VB codeShell menus: The Context menu, The Favorites menu. Shell related: Control Panel, System Image ListsGraphics related: Rubik's Cube, OpenGL without external libraries, Navigating in an image, Non-rectangular selectionsListView controls: Colors and fonts, Multi-line header, Multi-line items, Checkboxes and icons, Incremental searchListView controls: Virtual ListViews, Editing cells, Data display functions Link to comment Share on other sites More sharing options...
junkew Posted May 17, 2014 Share Posted May 17, 2014 @Alexander: See event examples in '?do=embed' frameborder='0' data-embedContent>> That way you can monitor what is changing like AddAutomationEventHandler method AddFocusChangedEventHandler method AddPropertyChangedEventHandler method AddPropertyChangedEventHandlerNativeArray method AddStructureChangedEventHandler method FAQ 31 How to click some elements, FAQ 40 Test automation with AutoIt, Multithreading CLR .NET Powershell CMDLets Link to comment Share on other sites More sharing options...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now