IErrors Posted March 31, 2017 Posted March 31, 2017 I am working on some functionality to detect whether a standard keyboard was used for input in my UI or if a barcode scanner (that acts like a keyboard) was used. Instead of dealing with a scanner SDK, I decided I would read from the raw input to get device info and make a determination. I discovered while testing with multiple UIs is that when the WM_INPUT message is received and the function called, the $hWnd param is always the same (apparently whatever handle I set the "hTarget" to in the RAWINPUTDEVICE struct). Is this normal behavior?? Using the test code below, no matter what window you give input to the $hWnd param never changes. #include <WindowsConstants.au3> #include <GUIConstantsEx.au3> #include <WinAPISys.au3> $Form1 = GUICreate("Form 1", 300, 300, -1, 10) ConsoleWrite("$Form1 = " & $Form1 & @CRLF) $Edit1 = GUICtrlCreateEdit("", 0, 0, 300, 300) GUISetState(@SW_SHOW, $Form1) $Form2 = GUICreate("Form 2", 300, 300, -1, 350) ConsoleWrite("$Form2 = " & $Form2 & @CRLF) $Edit2 = GUICtrlCreateEdit("", 0, 0, 300, 300) GUISetState(@SW_SHOW, $Form2) GUIRegisterMsg($WM_INPUT, "WM_INPUT") ; https://msdn.microsoft.com/en-us/library/ms645590(v=vs.85).aspx $tRID = DllStructCreate($tagRAWINPUTDEVICE) ; https://msdn.microsoft.com/en-us/library/ms645565(v=vs.85).aspx DllStructSetData($tRID, "UsagePage", 1) DllStructSetData($tRID, "Usage", 6) DllStructSetData($tRID, "Flags", 0) DllStructSetData($tRID, "hTarget", $Form1) ; According to the struct def, if hTarget=NULL then it should follow keyboard focus. - nope :( $pRID = DllStructGetPtr($tRID) _WinAPI_RegisterRawInputDevices($pRID) ; https://msdn.microsoft.com/en-us/library/ms645600(v=vs.85).aspx Do Until GUIGetMsg() = $GUI_EVENT_CLOSE Func WM_INPUT($hWnd, $iMsg, $wParam, $lParam) ConsoleWrite("hWnd = " & $hWnd & " | Title = " & WinGetTitle($hWnd) & @CRLF) ConsoleWrite("Active Window = " & WinGetTitle("[ACTIVE]") & @CRLF) Return $GUI_RUNDEFMSG EndFunc ;==>WM_INPUT
mikell Posted March 31, 2017 Posted March 31, 2017 47 minutes ago, IErrors said: I would read from the raw input to get device info and make a determination. So... _WinAPI_GetRawInputDeviceInfo ?
IErrors Posted March 31, 2017 Author Posted March 31, 2017 Just now, mikell said: So... _WinAPI_GetRawInputDeviceInfo ? Of course! That's how I am doing it and it works just fine! - Thanks. I was wondering if the $hWnd param of the WM_INPUT message handler should reflect the actual window handle the message was received in, because it stays the same no matter what form I type in.
mikell Posted March 31, 2017 Posted March 31, 2017 Wrong way IMHO to get the window handle. The $hWnd param will always return the registered hTarget value
LarsJ Posted April 1, 2017 Posted April 1, 2017 IErrors, Yes, this is normal behaviour. $hWnd param of the WM_INPUT message reflects the window handle the message is send to. As mikell has already explained. Your WM_INPUT message handler should look like this: Func WM_INPUT($hWnd, $iMsg, $wParam, $lParam) If $hWnd = WinGetHandle("[ACTIVE]") Then ConsoleWrite("hWnd = " & $hWnd & " | Title = " & WinGetTitle($hWnd) & @CRLF) ConsoleWrite("Active Window = " & WinGetTitle("[ACTIVE]") & @CRLF) EndIf Return $GUI_RUNDEFMSG EndFunc ;==>WM_INPUT GUIRegisterMsg is able to catch messages which are send to a GUI. If you set the value of hTarget parameter to null, the WM_INPUT messages are send directly to the control which has the keybord focus. In this case either $Edit1 or $Edit2. To catch these messages you have to use subclassing in this way: expandcollapse popup#include <WindowsConstants.au3> #include <GUIConstantsEx.au3> #include <WinAPIShellEx.au3> #include <WinAPISys.au3> $Form1 = GUICreate("Form 1", 300, 300, -1, 10) ConsoleWrite("$Form1 = " & $Form1 & @CRLF) $Edit1 = GUICtrlCreateEdit("", 0, 0, 300, 300) $hEdit1 = GUICtrlGetHandle( $Edit1 ) GUISetState(@SW_SHOW, $Form1) $pEdit1Callback = DllCallbackGetPtr( DllCallbackRegister( "Edit1Callback", "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr" ) ) _WinAPI_SetWindowSubclass( $hEdit1, $pEdit1Callback, 9999, 0 ) $Form2 = GUICreate("Form 2", 300, 300, -1, 350) ConsoleWrite("$Form2 = " & $Form2 & @CRLF) $Edit2 = GUICtrlCreateEdit("", 0, 0, 300, 300) $hEdit2 = GUICtrlGetHandle( $Edit2 ) GUISetState(@SW_SHOW, $Form2) $pEdit2Callback = DllCallbackGetPtr( DllCallbackRegister( "Edit2Callback", "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr" ) ) _WinAPI_SetWindowSubclass( $hEdit2, $pEdit2Callback, 9998, 0 ) GUIRegisterMsg($WM_INPUT, "WM_INPUT") ; https://msdn.microsoft.com/en-us/library/ms645590(v=vs.85).aspx $tRID = DllStructCreate($tagRAWINPUTDEVICE) ; https://msdn.microsoft.com/en-us/library/ms645565(v=vs.85).aspx DllStructSetData($tRID, "UsagePage", 1) DllStructSetData($tRID, "Usage", 6) DllStructSetData($tRID, "Flags", 0) ;DllStructSetData($tRID, "hTarget", $Form1) ; According to the struct def, if hTarget=NULL then it should follow keyboard focus. - nope :( DllStructSetData($tRID, "hTarget", 0) ; It indeed does follow keyboard focus $pRID = DllStructGetPtr($tRID) _WinAPI_RegisterRawInputDevices($pRID) ; https://msdn.microsoft.com/en-us/library/ms645600(v=vs.85).aspx Do Until GUIGetMsg() = $GUI_EVENT_CLOSE _WinAPI_RemoveWindowSubclass( $Edit1, $pEdit1Callback, 9999 ) _WinAPI_RemoveWindowSubclass( $Edit2, $pEdit2Callback, 9998 ) Func WM_INPUT($hWnd, $iMsg, $wParam, $lParam) If $hWnd = WinGetHandle("[ACTIVE]") Then ConsoleWrite("hWnd = " & $hWnd & " | Title = " & WinGetTitle($hWnd) & @CRLF) ConsoleWrite("Active Window = " & WinGetTitle("[ACTIVE]") & @CRLF) EndIf Return $GUI_RUNDEFMSG EndFunc ;==>WM_INPUT Func Edit1Callback( $hWnd, $iMsg, $wParam, $lParam, $iSubclassId, $pData ) Switch $iMsg Case $WM_INPUT ConsoleWrite("Edit1Callback" & @CRLF) ConsoleWrite(" hWnd = " & $hWnd & " | Title = " & WinGetTitle($hWnd) & @CRLF) ConsoleWrite(" Active Window = " & WinGetTitle("[ACTIVE]") & @CRLF) EndSwitch ; Call next function in subclass chain Return DllCall( "comctl32.dll", "lresult", "DefSubclassProc", "hwnd", $hWnd, "uint", $iMsg, "wparam", $wParam, "lparam", $lParam )[0] #forceref $iSubclassId, $pData EndFunc Func Edit2Callback( $hWnd, $iMsg, $wParam, $lParam, $iSubclassId, $pData ) Switch $iMsg Case $WM_INPUT ConsoleWrite("Edit2Callback" & @CRLF) ConsoleWrite(" hWnd = " & $hWnd & " | Title = " & WinGetTitle($hWnd) & @CRLF) ConsoleWrite(" Active Window = " & WinGetTitle("[ACTIVE]") & @CRLF) EndSwitch ; Call next function in subclass chain Return DllCall( "comctl32.dll", "lresult", "DefSubclassProc", "hwnd", $hWnd, "uint", $iMsg, "wparam", $wParam, "lparam", $lParam )[0] #forceref $iSubclassId, $pData EndFunc 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
IErrors Posted April 3, 2017 Author Posted April 3, 2017 Thank you both Mikell & LarsJ! I figured it had to do with the registered hTarget, but I wanted to make sure I wasn't doing it wrong. I was able to get the active window and control handle in the message handler of my actual code - although very fumbly. I hadn't thought of creating individual callbacks for each control - This is a great! Thank you for demonstrating the correct way of doing this.
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