Jump to content
bo8ster

Control Handle under mouse

Recommended Posts

bo8ster

Hi all. I have seen a number of requests for something like this in the Help and Support section so I thought I would post it. All the credit goes to SmOke_N - I just touched it up.

This script was created when trying to get a ControlHandle that has next to no information displayed in the AutoIT Info Tool.

; ===============================================================================
;~ 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

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

Edit:

Sept 09

Enhancement by corgano

Added ControlID

Added ClassNN and now requires AutoIt v3.3.0.0 or later to run

Removed _WinAPI_GetParent and added global $appHandle

06 Apr 10

Removed ClientToScreenCall

Made _ControlGetClassnameNN apart of the script

General Updates

Edited by bo8ster
  • Like 1

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]

Share this post


Link to post
Share on other sites
rexx

Hi, I'm finding how to get ClassNN under mouse, and i found this post.

But it only get the Class, not the ClassNN.

Is it possible to get ClassNN?

Share this post


Link to post
Share on other sites
corgano

added

Global $pos1 = MouseGetPos()
Global $pos2 = MouseGetPos()

to the begining and changed

Func _Mouse_Control_GetInfoAdlib()
    $pos1 = MouseGetPos()
    If $pos1[0] <> $pos2[0] and $pos1[1] <> $pos2[1] Then
        Local $a_info = _Mouse_Control_GetInfo()
        If @error Then Return
        ToolTip("Handle = " & $a_info[0] & @CRLF & _
                "Class = " & $a_info[1] & @CRLF & _
                "Mouse X Pos = " & $a_info[2] & @CRLF & _
                "Mouse Y Pos = " & $a_info[3])
    $pos2 = MouseGetPos()
    EndIf
EndFunc

so it would not flicker. i am impressed, it got the handle of win 7's IE (shown nothing in au3info)


0x616e2069646561206973206c696b652061206d616e20776974686f7574206120626f64792c20746f206669676874206f6e6520697320746f206e657665722077696e2e2e2e2e

Share this post


Link to post
Share on other sites
bo8ster

@rexx, this was not designed to give the ClassNN but I understand that it can be handy.

SmOke_N also created a function called WinGetCtrlInfo which will give you the ClassNN.

Once you get the handle from my function, pass it to WinGetCtrlInfo. The original post is here - http://www.autoitscript.com/forum/index.php?showtopic=32781

@corgano - that's the beauty of black box calling Window's dlls, the implementation does not matter. I cannot comment why au3info tool did not work, that's the dev's area.


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]

Share this post


Link to post
Share on other sites
corgano

You should update script with the fix i posted a few posts ago. It reduces flicker


0x616e2069646561206973206c696b652061206d616e20776974686f7574206120626f64792c20746f206669676874206f6e6520697320746f206e657665722077696e2e2e2e2e

Share this post


Link to post
Share on other sites
bo8ster

You should update script with the fix i posted a few posts ago. It reduces flicker

Thanks corgano. I experience no flicker using XP so I almost missed what your enhancement actually does. I see it is to stop _Mouse_Control_GetInfo() from being called if the mouse does not move which helps reduce flicker.

I will update my original post. Thanks again.


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]

Share this post


Link to post
Share on other sites
rexx

@rexx, this was not designed to give the ClassNN but I understand that it can be handy.

SmOke_N also created a function called WinGetCtrlInfo which will give you the ClassNN.

Once you get the handle from my function, pass it to WinGetCtrlInfo. The original post is here - http://www.autoitscript.com/forum/index.php?showtopic=32781

Thanks!

But I got problem using this.

I added a line

Local $a_classnn = _WinGetCtrlInfo($a_info[0])

in func

Func _Mouse_Control_GetInfoAdlib()

But I got 0 in $a_classnn[0][0], i cant get the correct classnn of the control.

I also tried

Local $a_classnn = _WinGetCtrlInfo(WinGetHandle("[active]"))

and it does return a list of controls.

What's going wrong?

Edited by rexx

Share this post


Link to post
Share on other sites
bo8ster

Sorry for the delayed response been away from the computer and it has taken some time to get up to SmOke_N's level of thinking (even briefly).

ClassNN is the Class name combined with the instance count of that class.

SmOke_N builds a count of the instances returned from WinGetClassList in _WinGetCtrlInfo, he does not get the instance from a control handle as I originally thought/hoped. WinGetCtrlInfo thaks a handle to a Window, not a control, which is why it did not work for you, sorry for that. If it is at all helpful, Local $aDLL = DllCall('User32.dll', 'int', 'GetDlgCtrlID', 'hwnd', $a_info[0]) ; get the ID of the control, will get the control ID for you

At this time I do not know how to get the instance (HINSTANCE) from a control to get the ClassNN, cleanly.

They only solution I can think of (which just came to me) is to get the control ID from above line (put it in _Mouse_Control_GetInfoAdlib) then match that to the ID in the array created by WinGetCtrlInfo .

I know how handy knowing the instance of the control you have is so I'll keep trying.

Edit: Minor corrections

Edited by bo8ster

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]

Share this post


Link to post
Share on other sites
burlip

Nice one, bo8ster.

If you change line 25 to:

If $pos1[0] <> $pos2[0] or $pos1[1] <> $pos2[1] Then

then the Tool Tip behaves much smoother and more responsive.

Cheers...

Share this post


Link to post
Share on other sites
bo8ster

@burlip, thanks for the pickup - I have updated the script.

@rexx, you can now get the ClassNN. Thanks must go to Valik for making that possible.

The tool tip now displays just about everything you need to know about any control.


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]

Share this post


Link to post
Share on other sites
rexx

@rexx, you can now get the ClassNN. Thanks must go to Valik for making that possible.

Thank you bo8ster!

But I tried it and it seem not working correctly.

It always get the first instance.

I also tried to add while loop around

$sComposite = $sLastClass & $nInstance ;build the ClassNN for testing with ControlGetHandle
        ;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
        $nInstance += 1 ; count the number of times the class name appears in the list

But still the first instance.

Thanks again for you help!

Share this post


Link to post
Share on other sites
bo8ster

The function should work. The code you displayed gets run each iteration of the For loop anyhow. If you just use that code, $nInstance will not be setip correctly.

If $sLastClass <> $aList[$i] Then sets up the first instance and $nInstance += 1 does the rest (> 1 Instance).

Make sure you have the latest version of AutoIt or StringSplit won't work and you have ClassNNs that are Foo2 or Foo3. Check off against the AutoIT info tool. I tested with Scite as there are two controls that have the same classname.


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]

Share this post


Link to post
Share on other sites
rexx

The function should work. The code you displayed gets run each iteration of the For loop anyhow. If you just use that code, $nInstance will not be setip correctly.

If $sLastClass <> $aList[$i] Then sets up the first instance and $nInstance += 1 does the rest (> 1 Instance).

Make sure you have the latest version of AutoIt or StringSplit won't work and you have ClassNNs that are Foo2 or Foo3. Check off against the AutoIT info tool. I tested with Scite as there are two controls that have the same classname.

Hi,

I tried it again, and somtimes it works but sometimes not.

I test it on 7zip file manager, and use 2 panel, but on both panel it shows instance 1, both syslistview32 and edit.

but it works on the toolbarwindow32, it shows instance 1 and 2 correctly.

Share this post


Link to post
Share on other sites
bo8ster

Hi,

I tried it again, and somtimes it works but sometimes not.

I test it on 7zip file manager, and use 2 panel, but on both panel it shows instance 1, both syslistview32 and edit.

but it works on the toolbarwindow32, it shows instance 1 and 2 correctly.

Sorry rexx, you will need to provide more information. I did see that if I have the AutoIt Info tool displayed the mouse does funny things so I suggest you keep it minimized.

I tested it with 7Zip FM as well and had no issues. I see that most is syslistview321 but there is one section that is syslistview322, I could find an edit.

Have you tried viewing the list of classes to ensure there are duplicates and do you know where they are? Did you get different results from the info tool, if so how? Did you get output you did not expect, is so what did you expect and what did you get?


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]

Share this post


Link to post
Share on other sites
rexx

Sorry rexx, you will need to provide more information. I did see that if I have the AutoIt Info tool displayed the mouse does funny things so I suggest you keep it minimized.

I tested it with 7Zip FM as well and had no issues. I see that most is syslistview321 but there is one section that is syslistview322, I could find an edit.

Have you tried viewing the list of classes to ensure there are duplicates and do you know where they are? Did you get different results from the info tool, if so how? Did you get output you did not expect, is so what did you expect and what did you get?

Thanks for your fast reply.

http://img160.imageshack.us/img160/5836/87199679.png

In the first screenshot, it shows SysListView321, which is correct. (mouse is on the left panel)

http://img56.imageshack.us/img56/8663/43540544.png

But in the second one, it shows still SysListView321, but it's SysListView322 in AU3Info. (mouse on right panel)

Share this post


Link to post
Share on other sites
bo8ster

Thanks for your fast reply.

http://img160.imageshack.us/img160/5836/87199679.png

In the first screenshot, it shows SysListView321, which is correct. (mouse is on the left panel)

http://img56.imageshack.us/img56/8663/43540544.png

But in the second one, it shows still SysListView321, but it's SysListView322 in AU3Info. (mouse on right panel)

I can now see your issue. Everything is correct except for the ClassNN. The only thing I can think of is the ordering returned from WinGetClassList is not consistent - see http://www.autoitscript.com/forum/index.php?showtopic=97662. I will need to debug it to find out why it is the case. Either way for whatever reason, it is clear that the for loop only runs once as you indirectly pointed out before.

Edit/Update: The WinGetClassList is not returning the desired List, it is not getting the top level window handle from _WinAPI_Getparent or _WinAPI_GetAncestor.

for 7 Zip it returns - thus there is only ever one instance of SysListView32.

SysListView32
SysHeader32
ReBarWindow32
ComboBoxEx32
ComboBox
Edit
ToolbarWindow32
msctls_statusbar32
Edited by bo8ster

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]

Share this post


Link to post
Share on other sites
bo8ster

@rexx, give that a try. The original script was made to be used when apps where maximized, it does funny things if they are not.

Tested with 7 Zip and Scite maximized.


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]

Share this post


Link to post
Share on other sites
rexx

@rexx, give that a try. The original script was made to be used when apps where maximized, it does funny things if they are not.

Tested with 7 Zip and Scite maximized.

It works now!

So it can only used in a maximized window?

it stop updating if a window is not maximized.

Share this post


Link to post
Share on other sites
bo8ster

At this stage it only works with maximized window. I did a lot of messing round to stop that but could not do it. It translates mouse coords so what ends up happening is when not maximized, info is grabbed not where the mouse is. It could be as much as half the screen away which is so confusing! You won't see that as it now though.

I have not idea how the Info Tool does it so well, I have been through the WinAPI calls and MSDN but still can't nail it. It will have to be maximized for the time being, that was the original concept for the script anyway.


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]

Share this post


Link to post
Share on other sites
wraithdu

Opt("MouseCoordMode", 0) -> relative coords to the active window

Opt("MouseCoordMode", 2) -> relative coords to the client area of the active window

I haven't looked at your UDF, but have you considered the WindowFromPoint() function?

The WindowFromPoint function retrieves a handle to the window that contains the specified point.

'Window' also means control (every control is a window too). Edited by wraithdu

Share this post


Link to post
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

×

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.