Jump to content

WindowFromPoint help


Recommended Posts

The issue is: WindowFromPoint should return the handle of the window where the mouse is. This only happens when the window is maximised.

When the window is not maximised the handle returned is the next window in sequence that is maxmised. matchWinHandle() confirms this.

To test, run this script in Scite when it is not maximised. The handle returned should be to Window Handle to Scite but it is not.

I expaned the window to cover the entire screen to be sure. Maybe I am missing something but I cannot see it.

#include <WinAPI.au3>
#include <Array.au3>
AutoItSetOption("MustDeclareVars", 1)
AutoItSetOption("MouseCoordMode", 2)
AdlibRegister("_Mouse_Control_GetInfoAdlib", 10)
HotKeySet("^!x", "MyExit") ; Press Ctrl+Alt+x to stop the script
;~ #AutoIt3Wrapper_run_debug_mode=Y

Global $pos1 = MouseGetPos()
Global $pos2 = MouseGetPos() ; must be initialized
Global $appHandle = 0

While 1
    Sleep(0xFFFFFFF)
WEnd

Func _Mouse_Control_GetInfoAdlib()
    $pos1 = MouseGetPos()
    If $pos1[0] <> $pos2[0] Or $pos1[1] <> $pos2[1] Then ; has the mouse moved?
        Local $a_info = _Mouse_Control_GetInfo()
        $pos2 = MouseGetPos()
    EndIf
EndFunc   ;==>_Mouse_Control_GetInfoAdlib

Func _Mouse_Control_GetInfo()
    $appHandle = GetHoveredHwnd() ; Uses the mouse to do the equivalent of WinGetHandle()
    matchWinHandle($appHandle)
    Return 0
EndFunc   ;==>_Mouse_Control_GetInfo


; ===============================================================================
; Retrieves the Handle of GUI/Application the mouse is over.
; Similar to WinGetHandle except it used the current mouse position (Client Coords)
; Taken from http://www.autoitscript.com/forum/index.php?showtopic=444962
; ===============================================================================
Func GetHoveredHwnd()
    Local $iRet = DllCall("user32.dll", "int", "WindowFromPoint", "long", MouseGetPos(), "long", MouseGetPos())
    If IsArray($iRet) Then
        $appHandle = $iRet[0]
        Return HWnd($iRet[0])
    Else
        Return SetError(1, 0, 0)
    EndIf
EndFunc   ;==>GetHoveredHwnd

Func matchWinHandle($hParent)
    Local $aWinList = WinList()
    Local $flag = 0;
    For $i = 0 To UBound($aWinList) - 1
        If $aWinList[$i][1] = $hParent Then
            ConsoleWriteError("hParent match is - " & $aWinList[$i][0] & @LF) ; print the display name of the window handle
            $flag = 1
;~          _ArrayDisplay($aWinList)
            ExitLoop
        EndIf
    Next
    If $flag = 0 Then
        ConsoleWriteError("No windows handle match found in WinList" & @LF)
    EndIf
EndFunc   ;==>matchWinHandle

Func MyExit() ; stops the script
    ConsoleWrite("Script Stoppted By User" & @CR)
    Exit
EndFunc   ;==>MyExit

Post your code because code says more then your words can. SciTe Debug mode - it's magic: #AutoIt3Wrapper_run_debug_mode=Y. Use Opt("MustDeclareVars", 1)[topic="84960"]Brett F's Learning To Script with AutoIt V3[/topic][topic="21048"]Valuater's AutoIt 1-2-3, Class... is now in Session[/topic]Contribution: [topic="87994"]Get SVN Rev Number[/topic], [topic="93527"]Control Handle under mouse[/topic], [topic="91966"]A Presentation using AutoIt[/topic], [topic="112756"]Log ConsoleWrite output in Scite[/topic]

Link to comment
Share on other sites

WindowFromPoint will get non-window elements like controls as well as windows. Just do a check with _WinAPI_GetAncestor($hWin,2) to see if it matches. If not, then use the result as the 'real' window.

Link to comment
Share on other sites

; ===============================================================================
; Retrieves the Handle of GUI/Application the mouse is over.
; Similar to WinGetHandle except it used the current mouse position (Client Coords)
; Taken from http://www.autoitscript.com/forum/index.php?showtopic=444962
; ===============================================================================
Func GetHoveredHwnd()
    Local $iRet = DllCall("user32.dll", "int", "WindowFromPoint", "long", MouseGetPos(), "long", MouseGetPos())
    If IsArray($iRet) Then
        $appHandle = $iRet[0]
        Return HWnd($iRet[0])
    Else
        Return SetError(1, 0, 0)
    EndIf
EndFunc ;==>GetHoveredHwnd

You badly used MouseGetPos() in WindowFromPoint DllCall()

Try this

Local $iRet = DllCall("user32.dll", "int", "WindowFromPoint", "long", MouseGetPos(0), "long", MouseGetPos(1))
Link to comment
Share on other sites

@Ascend4nt - Thanks! The code given is apart of a larger script which does use _WinAPI_GetAncestor however the flag param may work this time. (I've had issues with it before). In that script I notice that _WinAPI_GetAncestor has to be called twice sometimes in order to get match to what WinList returns. I have made another version of matchWinHandle which is recursive to do this however I could not get over the original issue. Once finished I will post the script - it is a fix up of my 'Control Handle under mouse' script so it can work with non-minimized windows.

@Zedna - good pickup, I never would have got that - thanks! I'll try that after Easter. Happy Easter!

Post your code because code says more then your words can. SciTe Debug mode - it's magic: #AutoIt3Wrapper_run_debug_mode=Y. Use Opt("MustDeclareVars", 1)[topic="84960"]Brett F's Learning To Script with AutoIt V3[/topic][topic="21048"]Valuater's AutoIt 1-2-3, Class... is now in Session[/topic]Contribution: [topic="87994"]Get SVN Rev Number[/topic], [topic="93527"]Control Handle under mouse[/topic], [topic="91966"]A Presentation using AutoIt[/topic], [topic="112756"]Log ConsoleWrite output in Scite[/topic]

Link to comment
Share on other sites

Here is the end product.

; ===============================================================================
;~ This script gets the control under the mouse pointer (active or inactive)
;~ The information then can be used with in conjunction with control functions.
;~ Requires AutoIt v3.3.6.0 or later to run and to view apps maximized.
;~ Big thanks to SmOke_N and Valik their help in creating it.
; ===============================================================================
#include <WinAPI.au3>
#include <Array.au3>
#include <WindowsConstants.au3>

AutoItSetOption("MustDeclareVars", 1)
AutoItSetOption("MouseCoordMode", 1)
AdlibRegister("_Mouse_Control_GetInfoAdlib", 10)
HotKeySet("^!x", "MyExit") ; Press Ctrl+Alt+x to stop the script
;~ #AutoIt3Wrapper_run_debug_mode=Y

Global $pos1 = MouseGetPos()
Global $pos2 = MouseGetPos() ; must be initialized
Global $appHandle = 0

While 1
    Sleep(0xFFFFFFF)
WEnd

; ===============================================================================
;~ Retrieves the information of a Control located under the mouse and displayes it in a tool tip next to the mouse.
;~ Function uesd
;~  _Mouse_Control_GetInfo()
;~  GetDlgCtrlID
; ===============================================================================
Func _Mouse_Control_GetInfoAdlib()
    $pos1 = MouseGetPos()
    If $pos1[0] <> $pos2[0] Or $pos1[1] <> $pos2[1] Then ; has the mouse moved?
        Local $a_info = _Mouse_Control_GetInfo()
        Local $aDLL = DllCall('User32.dll', 'int', 'GetDlgCtrlID', 'hwnd', $a_info[0]) ; get the ID of the control
        If @error Then Return
        ToolTip("Handle = " & $a_info[0] & @CRLF & _
                "Class = " & $a_info[1] & @CRLF & _
                "ID = " & $aDLL[0] & @CRLF & _
                "Mouse X Pos = " & $a_info[2] & @CRLF & _
                "Mouse Y Pos = " & $a_info[3] & @CRLF & _
                "ClassNN = " & $a_info[4] & @CRLF & _ ; optional
                "Parent Hwd = " & _WinAPI_GetAncestor($appHandle, $GA_ROOT))
        $pos2 = MouseGetPos()
    EndIf
EndFunc   ;==>_Mouse_Control_GetInfoAdlib

; ===============================================================================
;~ Retrieves the information of a Control located under the mouse.
;~ Uses Windows functions WindowFromPoint and GetClassName to retrieve the information.
;~ Functions used
;~  _GetHoveredHwnd()
;~  _ControlGetClassnameNN()
;~ Returns
;~   [0] = Control Handle of the control
;~   [1] = The Class Name of the control
;~   [2] = Mouse X Pos (converted to Screen Coord)
;~   [3] = Mouse Y Pos (converted to Screen Coord)
;~   [4] = ClassNN
; ===============================================================================
Func _Mouse_Control_GetInfo()
    Local $client_mpos = $pos1 ; gets client coords because of "MouseCoordMode" = 2
    Local $a_mpos
;~  Call to removed due to offset issue $a_mpos = _ClientToScreen($appHandle, $client_mpos[0], $client_mpos[1]) ; $a_mpos now screen coords
    $a_mpos = $client_mpos
    $appHandle = GetHoveredHwnd($client_mpos[0], $client_mpos[1]) ; Uses the mouse to do the equivalent of WinGetHandle()

    If @error Then Return SetError(1, 0, 0)
    Local $a_wfp = DllCall("user32.dll", "hwnd", "WindowFromPoint", "long", $a_mpos[0], "long", $a_mpos[1]) ; gets the control handle
    If @error Then Return SetError(2, 0, 0)

    Local $t_class = DllStructCreate("char[260]")
    DllCall("User32.dll", "int", "GetClassName", "hwnd", $a_wfp[0], "ptr", DllStructGetPtr($t_class), "int", 260)
    Local $a_ret[5] = [$a_wfp[0], DllStructGetData($t_class, 1), $a_mpos[0], $a_mpos[1], "none"]
    Local $sClassNN = _ControlGetClassnameNN($a_ret[0]) ; optional, will run faster without it
    $a_ret[4] = $sClassNN

    Return $a_ret
EndFunc   ;==>_Mouse_Control_GetInfo

; ===============================================================================
; Retrieves the Handle of GUI/Application the mouse is over.
; Similar to WinGetHandle except it used the current mouse position
; Taken from http://www.autoitscript.com/forum/index.php?showtopic=444962
; Changed to take params to allow only one set of coords to be used.
; Params
;~  $i_xpos - x position of the mouse - usually from MouseGetPos(0)
;~  $i_ypos - x position of the mouse - usually from MouseGetPos(1)
; ===============================================================================
Func GetHoveredHwnd($i_xpos, $i_ypos)
    Local $iRet = DllCall("user32.dll", "int", "WindowFromPoint", "long", $i_xpos, "long", $i_ypos)
    If IsArray($iRet) Then
        $appHandle = $iRet[0]
        Return HWnd($iRet[0])
    Else
        Return SetError(1, 0, 0)
    EndIf
EndFunc   ;==>GetHoveredHwnd

; ===============================================================================
;~ Gets the ClassNN of a control (Classname and Instance Count). This is checked with ControlGetHandle
;~ The instance is really a way to uniquely identify classes with the same name
;~ Big thanks to Valik for writing the function, taken from - http://www.autoitscript.com/forum/index.php?showtopic=97662
;~ Param
;~  $hControl - the control handle from which you want the ClassNN
;~ Returns
;~  the ClassNN of the given control

; ===============================================================================
Func _ControlGetClassnameNN($hControl)
    If Not IsHWnd($hControl) Then Return SetError(1, 0, "")
    Local Const $hParent = _WinAPI_GetAncestor($appHandle, $GA_ROOT) ; get the Window handle, this is set in GetHoveredHwnd()
    If Not $hParent Then Return SetError(2, 0, "")

    Local Const $sList = WinGetClassList($hParent) ; list of every class in the Window
    Local $aList = StringSplit(StringTrimRight($sList, 1), @LF, 2)
    _ArraySort($aList) ; improves speed
    Local $nInstance, $sLastClass, $sComposite

    For $i = 0 To UBound($aList) - 1
        If $sLastClass <> $aList[$i] Then ; set up the first occurrence of a unique classname
            $sLastClass = $aList[$i]
            $nInstance = 1
        EndIf
        $sComposite = $sLastClass & $nInstance ;build the ClassNN for testing with ControlGetHandle. ClassNN = Class & ClassCount
        ;if ControlGetHandle(ClassNN) matches the given control return else look at the next instance of the classname
        If ControlGetHandle($hParent, "", $sComposite) = $hControl Then
            Return $sComposite
        EndIf
        $nInstance += 1 ; count the number of times the class name appears in the list
    Next
    Return SetError(3, 0, "")

EndFunc   ;==>_ControlGetClassnameNN

; ===============================================================================
;~ Functions not used anymore.
; ===============================================================================

; ===============================================================================
;~ Translates client coordinates into screen coordinates, [0] = x and [1] = y from the return array.
;~ Requires - AutoItSetOption("MouseCoordMode", 2)
;~ Params
;~   $h_wnd - Identifies the window whose client area is used for the conversion.
;~   $i_x - x pos of the client coord
;~   $i_y - Y pos of the client coord
;~ Returns
;~    Screen coordinates
; ===============================================================================
Func _ClientToScreen($h_wnd, $i_x, $i_y)
;~  ConsoleWrite("Client here, " & $i_x & "," & $i_y & @ CRLF)
    Local $t_point = DllStructCreate("int;int")
    DllStructSetData($t_point, 1, $i_x)
    DllStructSetData($t_point, 2, $i_y)
    DllCall("user32.dll", "int", "ClientToScreen", "hwnd", $h_wnd, "ptr", DllStructGetPtr($t_point))
    Local $a_ret[2] = [DllStructGetData($t_point, 1), DllStructGetData($t_point, 2)]
;~  ConsoleWrite("Screen here, " & $a_ret[0] & "," & $a_ret[1] & @CRLF)
    Return $a_ret
EndFunc   ;==>_ClientToScreen

;~ Finds a matching window with the given handle
Func matchWinHandle($hParent)
    Local $aWinList = WinList()
    Local $flag = 0;
    For $i = 0 To UBound($aWinList) - 1
        If $aWinList[$i][1] = $hParent Then
            ConsoleWriteError("hParent match is " & $aWinList[$i][0] & @LF)
            $flag = 1
;~          _ArrayDisplay($aWinList)
            ExitLoop
        EndIf
    Next
    If $flag = 0 Then
        ConsoleWriteError("No windows handle match found in WinList" & @LF)
    EndIf
EndFunc   ;==>matchWinHandle

Func MyExit() ; stops the script
    ConsoleWrite("Script Stoppted By User" & @CR)
    Exit
EndFunc   ;==>MyExit

;==> Recersive version. No need now that I am using $GA_ROOT with get _WinAPI_GetAncestor
Func matchWinHandleRec($hParent)
    Local $aWinList = WinList()
    Local $flag = 0;
    For $i = 0 To UBound($aWinList) - 1
        If $aWinList[$i][1] = $hParent Then
            ConsoleWriteError("hParent match is " & $aWinList[$i][0] & @LF)
            $flag = 1;
            ExitLoop
            Return
        EndIf
    Next
    If $flag = 0 Then
        ConsoleWriteError("No windows handle match found in WinList. Calling it again" & @LF)
        matchWinHandle(_WinAPI_GetAncestor($hParent))
        Local $sClassList = WinGetClassList($hParent)
        Local $aClassList = StringSplit(StringTrimRight($sClassList, 1), @LF, 2)

        For $i = 0 To UBound($aClassList) - 1
            ConsoleWrite($aClassList[$i] & @LF)
        Next
    EndIf

EndFunc   ;==>matchWinHandleRec

Notes:

SmOke_N originally recommended using client coords then translating them to screen for accuracy however this caused an offset issue that I was unable to resolve. The issue was especially evident with non-maximized screens. I have left the call and function in so you can see what I am talking about.

I have left in other code including a recursive function in case anyone finds it interesting.

If you have any issues please let me know.

Post your code because code says more then your words can. SciTe Debug mode - it's magic: #AutoIt3Wrapper_run_debug_mode=Y. Use Opt("MustDeclareVars", 1)[topic="84960"]Brett F's Learning To Script with AutoIt V3[/topic][topic="21048"]Valuater's AutoIt 1-2-3, Class... is now in Session[/topic]Contribution: [topic="87994"]Get SVN Rev Number[/topic], [topic="93527"]Control Handle under mouse[/topic], [topic="91966"]A Presentation using AutoIt[/topic], [topic="112756"]Log ConsoleWrite output in Scite[/topic]

Link to comment
Share on other sites

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 account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...