dantay9 Posted November 9, 2009 Posted November 9, 2009 (edited) I am trying to make a little app, just for fun and to learn a bit more about lower level programming.When I click the middle mouse button, the window the mouse is over is sent to the back of the z order.When I hold the middle mouse, the window is sent to the front of the z order.All of this is working. I am having a little trouble with scrolling. If the mouse is over a control that can be scrolled, when I use the mouse wheel, that control is scrolled, even if it isn't on an active window. That part works. But when I have an active window with an edit control as well as the mouse being over a nonactive edit control, they both scroll. I think this is because they are sharing the command. From what I have read, Zedna's subclassing will fix this. The part of the code that will need fixed is the$WM_MOUSEWHEEL case in the mouse hook function.Here is a picture of the setup when the problem scrolling occurs.I took a look at Zedna's posts, but didn't understand exactly how subclassing worked or how to use it. Thanks in advance!expandcollapse popup#include <Constants.au3> #include <EditConstants.au3> #include <ScrollBarConstants.au3> #include <SendMessage.au3> #include <StructureConstants.au3> #include <WinAPI.au3> #include <WindowsConstants.au3> HotKeySet("{ESC}", "_Exit") Global Const $WM_MOUSEWHEEL = 0x020A ;wheel up/down ;~ Global Const $WM_MBUTTONDBLCLK = 0x0209 ;wheel clicks Global Const $User32 = DllOpen("User32.dll") Global $hKey_Proc, $hM_Hook, $TimeSinceLastDown, $WindowToFront, $MouseUpSent = False, $CanScroll = False SetWindowsHook() ;set low level mouse hook $MGP = MouseGetPos() While 1 If $MGP[0] <> MouseGetPos(0) And $MGP[1] <> MouseGetPos(1) Then ;if mouse moved $MGP = MouseGetPos() ;get mouse pos $Window = WinAPI_WindowFromPoint($MGP[0], $MGP[1]) ;get window mouse is over If $Window <> WinGetHandle("[Active]") Then ;make sure window isn't active window $hOverControl = CtrlGetInfoFromPoint($Window, $MGP[0], $MGP[1], "Handle") ;get the handle of the control mouse is over $ControlStyle = "0x" & Hex(_WinAPI_GetWindowLong($hOverControl, $GWL_STYLE)) ;get control style from handle If CanScroll($ControlStyle) Then ;see if the control can be scrolled $CanScroll = True Else $CanScroll = False EndIf EndIf EndIf Sleep(100) WEnd Func WinAPI_WindowFromPoint($iX, $iY) Local $a_wfp, $a_ga $a_wfp = DllCall("user32.dll", "hwnd", "WindowFromPoint", "long", $iX, "long", $iY) If @error Then Return SetError(1, @error, 0) $a_ga = _WinAPI_GetAncestor($a_wfp[0], $GA_ROOT) If @error Then Return SetError(2, @error, 0) Return $a_ga EndFunc ;==>WinAPI_WindowFromPoint Func _Exit() Exit EndFunc ;==>_Exit #Region Mouse Hook Func SetWindowsHook() Local $hM_Module $hKey_Proc = DllCallbackRegister("_Mouse_Proc", "int", "int;ptr;ptr") $hM_Module = DllCall("kernel32.dll", "hwnd", "GetModuleHandle", "ptr", 0) $hM_Hook = DllCall("user32.dll", "hwnd", "SetWindowsHookEx", "int", $WH_MOUSE_LL, "ptr", DllCallbackGetPtr($hKey_Proc), "hwnd", $hM_Module[0], "dword", 0) EndFunc ;==>SetWindowsHook Func _Mouse_Proc($nCode, $wParam, $lParam) If $nCode < 0 Then _WinAPI_CallNextHookEx($hM_Hook, $nCode, $wParam, $lParam) Switch $wParam Case $WM_MBUTTONDOWN $MouseUpSent = False ;~ If TimerDiff($TimeSinceLastDown) < 325 Then ;middle mouse button double click ;~ AdlibEnable("MiddleDoubleClick", 100) ;call the function after $WM_MBUTTONDOWN is passed on to system ;~ Else ;~ EndIf $TimeSinceLastDown = TimerInit() ;reset the timer CheckForMiddleUp() AdlibEnable("CheckForMiddleUp", 75) Case $WM_MBUTTONUP $MouseUpSent = True Case $WM_MOUSEWHEEL If $CanScroll = True Then $Info = DllStructCreate("int X;int Y;dword mouseData;dword flags;dword time;ulong_ptr dwExtraInfo", $lParam) $MouseData = DllStructGetData($Info, 3) If _WinAPI_HiWord($MouseData) > 0 Then _SendMessage($hOverControl, $EM_SCROLL, $SB_LINEUP) Else _SendMessage($hOverControl, $EM_SCROLL, $SB_LINEDOWN) EndIf Return 0 EndIf EndSwitch Return _WinAPI_CallNextHookEx($hM_Hook, $nCode, $wParam, $lParam) EndFunc ;==>_Mouse_Proc Func OnAutoItExit() WinSetOnTop($WindowToFront, "", 0) DllCall("user32.dll", "int", "UnhookWindowsHookEx", "hwnd", $hM_Hook[0]) $hM_Hook[0] = 0 DllCallbackFree($hKey_Proc) $hKey_Proc = 0 EndFunc ;==>OnAutoItExit #EndRegion Mouse Hook Func CheckForMiddleUp() If $MouseUpSent = False And TimerDiff($TimeSinceLastDown) > 100 Then ;middle mouse down held ConsoleWrite("Activate Window" & @CRLF) AdlibDisable() WinSetOnTop($WindowToFront, "", 0) $WindowToFront = WinAPI_WindowFromPoint(MouseGetPos(0), MouseGetPos(1)) WinSetOnTop($WindowToFront, "", 1) ElseIf $MouseUpSent = True Then ;single middle mouse click ConsoleWrite("Send To Back" & @CRLF) AdlibDisable() Local $WFP = WinAPI_WindowFromPoint(MouseGetPos(0), MouseGetPos(1)) Local $aResult = DllCall($User32, "hwnd", "FindWindow", "str", "Progman", "str", "Program Manager") _WinAPI_SetWindowPos($WFP, $HWND_BOTTOM, 0, 0, 0, 0, BitOR($SWP_NOMOVE, $SWP_NOSIZE, $SWP_NOACTIVATE)) DllCall($User32, "int", "SetForegroundWindow", "hwnd", $aResult[0]) ;activate the window EndIf EndFunc ;==>CheckForMiddleUp Func CtrlGetInfoFromPoint($hWin, $iX, $iY, $Mode) Local $sClassList = WinGetClassList($hWin) Local $sSplitClass = StringSplit(StringTrimRight($sClassList, 1), @LF) Local $nCount, $ClientCoords, $tPoint ;convert screen coords to client coords $tPoint = DllStructCreate($tagPOINT) DllStructSetData($tPoint, "X", $iX) DllStructSetData($tPoint, "Y", $iY) _WinAPI_ScreenToClient($hWin, $tPoint) ;set x and y to client coords $iX = DllStructGetData($tPoint, "X") $iY = DllStructGetData($tPoint, "Y") For $iCount = UBound($sSplitClass) - 1 To 1 Step -1 $nCount = 0 While 1 $nCount += 1 Local $aCPos = ControlGetPos($hWin, "", $sSplitClass[$iCount] & $nCount) If @error Then ExitLoop If $iX >= $aCPos[0] And $iX <= ($aCPos[0] + $aCPos[2]) _ And $iY >= $aCPos[1] And $iY <= ($aCPos[1] + $aCPos[3]) Then If $sSplitClass[$iCount] <> "" Then Switch $Mode Case "NN" Return $sSplitClass[$iCount] & $nCount Case "Handle" Return ControlGetHandle($hWin, "", "[CLASS:" & $sSplitClass[$iCount] & "]") Case "ID" Return _WinAPI_GetWindowLong(ControlGetHandle($hWin, "", "[CLASS:" & $sSplitClass[$iCount] & "]"), $GWL_ID) ;~ Return Dec(StringTrimLeft(_WinAPI_GetDlgCtrlID(ControlGetHandle($hWin, "", "[CLASS:" & $sSplitClass[$iCount] & "]")), 2)) Case "Style" Return "0x" & Hex(_WinAPI_GetWindowLong(ControlGetHandle($hWin, "", "[CLASS:" & $sSplitClass[$iCount] & "]"), $GWL_STYLE)) Case "ExStyle" Return "0x" & Hex(_WinAPI_GetWindowLong(ControlGetHandle($hWin, "", "[CLASS:" & $sSplitClass[$iCount] & "]"), $GWL_EXSTYLE)) Case Else Return $sSplitClass[$iCount] EndSwitch EndIf EndIf WEnd Next Return "" EndFunc ;==>CtrlGetInfoFromPoint Func CanScroll($Style) Return (BitAND($Style, $WS_VSCROLL) Or BitAND($Style, $WS_HSCROLL)) EndFunc ;==>CanScroll Edited November 9, 2009 by dantay9
Authenticity Posted November 9, 2009 Posted November 9, 2009 You hook procedure never return to the rest of the chain, it should be:Func _Mouse_Proc($nCode, $wParam, $lParam) If $nCode < 0 Then _WinAPI_CallNextHookEx($hM_Hook[0], $nCode, $wParam, $lParam) Switch $wParam Case $WM_MBUTTONDOWN $MouseUpSent = False ;~ If TimerDiff($TimeSinceLastDown) < 325 Then ;middle mouse button double click ;~ AdlibEnable("MiddleDoubleClick", 100) ;call the function after $WM_MBUTTONDOWN is passed on to system ;~ Else ;~ EndIf $TimeSinceLastDown = TimerInit() ;reset the timer CheckForMiddleUp() AdlibEnable("CheckForMiddleUp", 75) Case $WM_MBUTTONUP $MouseUpSent = True Case $WM_MOUSEWHEEL If $CanScroll = True Then $Info = DllStructCreate("int X;int Y;dword mouseData;dword flags;dword time;ulong_ptr dwExtraInfo", $lParam) $MouseData = DllStructGetData($Info, 3) If _WinAPI_HiWord($MouseData) > 0 Then _SendMessage($hOverControl, $EM_SCROLL, $SB_LINEUP) Else _SendMessage($hOverControl, $EM_SCROLL, $SB_LINEDOWN) EndIf Return 0 EndIf EndSwitch Return _WinAPI_CallNextHookEx($hM_Hook[0], $nCode, $wParam, $lParam) EndFunc ;==>_Mouse_Proc..or set $hM_Hook = $hM_Hook[0] after you're calling DllCall().Subclassing your window's control is something else than subclassing some other program's control, it's much more complex. Look here, it's an example of how to install a hook procedure that monitors messages posted to a message loop of an application, you can see which window this message is for and whether to intercept the message or let the window receive this message. You must install this hook using a dll.
dantay9 Posted November 9, 2009 Author Posted November 9, 2009 (edited) OK. That makes sense. Thanks for the help with the hook, but it still doesn't work. This problem doesn't happen when neither of the windows are active. Could the scroll message activated by my mouse wheel being scrolled be arriving at the active window before it is passed on to my program? Edited November 9, 2009 by dantay9
dantay9 Posted November 11, 2009 Author Posted November 11, 2009 Anyone else? I looked at Authenticity's link, but I don't know how to code that. I don't know VB code. A sample code in autoit that displays intercepting an event before the active window would be helpful. Or maybe there is another way to achieve what I am looking for.
Authenticity Posted November 11, 2009 Posted November 11, 2009 ...Could the scroll message activated by my mouse wheel being scrolled be arriving at the active window before it is passed on to my program? Nope, take this for example: Case $WM_MOUSEWHEEL Return 1 ...now no window in the system is getting notified about the event. What it can be is that another application registers a hook procedure later or after you register yours, this way, the other application gets notified before your hook. expandcollapse popup#include <Constants.au3> #include <EditConstants.au3> #include <ScrollBarConstants.au3> #include <SendMessage.au3> #include <StructureConstants.au3> #include <WinAPI.au3> #include <WindowsConstants.au3> If Not IsDeclared("WM_MBUTTONDOWN") Then Global Const $WM_MBUTTONDOWN = 0x0207 If Not IsDeclared("WM_MBUTTONUP") Then Global Const $WM_MBUTTONUP = 0x0208 HotKeySet("{ESC}", "_Exit") Global Const $WM_MOUSEWHEEL = 0x020A ;wheel up/down ;~ Global Const $WM_MBUTTONDBLCLK = 0x0209 ;wheel clicks Global Const $User32 = DllOpen("User32.dll") Global $hKey_Proc, $hM_Hook, $TimeSinceLastDown, $WindowToFront, $MouseUpSent = False, $CanScroll = False SetWindowsHook() ;set low level mouse hook $MGP = MouseGetPos() While 1 Sleep(100) WEnd Func WinAPI_WindowFromPoint($iX, $iY) Local $a_wfp, $a_ga $a_wfp = DllCall("user32.dll", "hwnd", "WindowFromPoint", "long", $iX, "long", $iY) If @error Then Return SetError(1, @error, 0) ;~ $a_ga = _WinAPI_GetAncestor($a_wfp[0], $GA_ROOT) ;~ If @error Then Return SetError(2, @error, 0) Return $a_wfp[0] EndFunc ;==>WinAPI_WindowFromPoint Func _Exit() Exit EndFunc ;==>_Exit #Region Mouse Hook Func SetWindowsHook() Local $hM_Module $hKey_Proc = DllCallbackRegister("_Mouse_Proc", "int", "int;ptr;ptr") $hM_Module = DllCall("kernel32.dll", "hwnd", "GetModuleHandle", "ptr", 0) $hM_Hook = DllCall("user32.dll", "hwnd", "SetWindowsHookEx", "int", $WH_MOUSE_LL, "ptr", DllCallbackGetPtr($hKey_Proc), "hwnd", $hM_Module[0], "dword", 0) $hM_Hook = $hM_Hook[0] EndFunc ;==>SetWindowsHook Func _Mouse_Proc($nCode, $wParam, $lParam) Local $hWindow, $hOverControl Local $iX, $iY Local $MouseData If $nCode < 0 Then _WinAPI_CallNextHookEx($hM_Hook, $nCode, $wParam, $lParam) Switch $wParam Case $WM_MBUTTONDOWN $MouseUpSent = False ;~ If TimerDiff($TimeSinceLastDown) < 325 Then ;middle mouse button double click ;~ AdlibEnable("MiddleDoubleClick", 100) ;call the function after $WM_MBUTTONDOWN is passed on to system ;~ Else ;~ EndIf $TimeSinceLastDown = TimerInit() ;reset the timer CheckForMiddleUp() AdlibEnable("CheckForMiddleUp", 75) Case $WM_MBUTTONUP $MouseUpSent = True Case $WM_MOUSEWHEEL $Info = DllStructCreate("int X;int Y;dword mouseData;dword flags;dword time;dword dwExtraInfo", $lParam) $iX = DllStructGetData($Info, "X") $iY = DllStructGetData($Info, "Y") $MouseData = DllStructGetData($Info, "mouseData") $hOverControl = WinAPI_WindowFromPoint($iX, $iY) ;get window mouse is over If CanScroll(_WinAPI_GetWindowLong($hOverControl, $GWL_STYLE)) Then ;see if the control can be scrolled ConsoleWrite("Yes" & @CRLF) ;~ If _WinAPI_HiWord($MouseData) > 0 Then ;~ _SendMessage($hOverControl, $EM_SCROLL, $SB_LINEUP) ;~ Else ;~ _SendMessage($hOverControl, $EM_SCROLL, $SB_LINEDOWN) ;~ EndIf _SendMessage($hOverControl, $WM_MOUSEWHEEL, $MouseData, BitOR(BitShift($iY, -16), $iX)) Return 1 EndIf EndSwitch Return _WinAPI_CallNextHookEx($hM_Hook, $nCode, $wParam, $lParam) EndFunc ;==>_Mouse_Proc Func OnAutoItExit() WinSetOnTop($WindowToFront, "", 0) DllCall("user32.dll", "int", "UnhookWindowsHookEx", "hwnd", $hM_Hook) DllCallbackFree($hKey_Proc) EndFunc ;==>OnAutoItExit #EndRegion Mouse Hook Func CheckForMiddleUp() If $MouseUpSent = False And TimerDiff($TimeSinceLastDown) > 100 Then ;middle mouse down held ConsoleWrite("Activate Window" & @CRLF) AdlibDisable() WinSetOnTop($WindowToFront, "", 0) $WindowToFront = WinAPI_WindowFromPoint(MouseGetPos(0), MouseGetPos(1)) WinSetOnTop($WindowToFront, "", 1) ElseIf $MouseUpSent = True Then ;single middle mouse click ConsoleWrite("Send To Back" & @CRLF) AdlibDisable() Local $WFP = WinAPI_WindowFromPoint(MouseGetPos(0), MouseGetPos(1)) Local $aResult = DllCall($User32, "hwnd", "FindWindow", "str", "Progman", "str", "Program Manager") _WinAPI_SetWindowPos($WFP, $HWND_BOTTOM, 0, 0, 0, 0, BitOR($SWP_NOMOVE, $SWP_NOSIZE, $SWP_NOACTIVATE)) DllCall($User32, "int", "SetForegroundWindow", "hwnd", $aResult[0]) ;activate the window EndIf EndFunc ;==>CheckForMiddleUp Func CtrlGetInfoFromPoint($hWin, $iX, $iY, $Mode) Local $sClassList = WinGetClassList($hWin) Local $sSplitClass = StringSplit(StringTrimRight($sClassList, 1), @LF) Local $nCount, $ClientCoords, $tPoint ;convert screen coords to client coords $tPoint = DllStructCreate($tagPOINT) DllStructSetData($tPoint, "X", $iX) DllStructSetData($tPoint, "Y", $iY) _WinAPI_ScreenToClient($hWin, $tPoint) ;set x and y to client coords $iX = DllStructGetData($tPoint, "X") $iY = DllStructGetData($tPoint, "Y") For $iCount = UBound($sSplitClass) - 1 To 1 Step -1 $nCount = 0 While 1 $nCount += 1 Local $aCPos = ControlGetPos($hWin, "", $sSplitClass[$iCount] & $nCount) If @error Then ExitLoop If $iX >= $aCPos[0] And $iX <= ($aCPos[0] + $aCPos[2]) _ And $iY >= $aCPos[1] And $iY <= ($aCPos[1] + $aCPos[3]) Then If $sSplitClass[$iCount] <> "" Then Switch $Mode Case "NN" Return $sSplitClass[$iCount] & $nCount Case "Handle" Return ControlGetHandle($hWin, "", "[CLASS:" & $sSplitClass[$iCount] & "]") Case "ID" Return _WinAPI_GetWindowLong(ControlGetHandle($hWin, "", "[CLASS:" & $sSplitClass[$iCount] & "]"), $GWL_ID) ;~ Return Dec(StringTrimLeft(_WinAPI_GetDlgCtrlID(ControlGetHandle($hWin, "", "[CLASS:" & $sSplitClass[$iCount] & "]")), 2)) Case "Style" Return "0x" & Hex(_WinAPI_GetWindowLong(ControlGetHandle($hWin, "", "[CLASS:" & $sSplitClass[$iCount] & "]"), $GWL_STYLE)) Case "ExStyle" Return "0x" & Hex(_WinAPI_GetWindowLong(ControlGetHandle($hWin, "", "[CLASS:" & $sSplitClass[$iCount] & "]"), $GWL_EXSTYLE)) Case Else Return $sSplitClass[$iCount] EndSwitch EndIf EndIf WEnd Next Return "" EndFunc ;==>CtrlGetInfoFromPoint Func CanScroll($Style) Return (BitAND($Style, $WS_VSCROLL) Or BitAND($Style, $WS_HSCROLL)) EndFunc ;==>CanScroll Hope this is what you're trying to do.
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