Jump to content

[SOLVED] Get selected item text from a menu


k4rl3on
 Share

Recommended Posts

Hi,

i'm looking for a way to get the text of selected item from a popup menu

here is my script, it works fine for non-Explorer.exe window (tested on SciTE on both context menu and menubar menu), but fails on displayed menu on Desktop...

Local $hDLL, $hWinEventProc, $hHook
Local $LastMenuItem[5] ;Save last menu item propieties
Local $EVENT_Min = 0x0006 ;An MSAA event indicating that a pop-up menu was displayed.
Local $EVENT_Max = 0x0007 ;An MSAA event indicating that a pop-up menu was closed.

Local $hWinEventProc = DllCallbackRegister("_WinEventProc", "none", "hwnd;int;hwnd;long;long;int;int")
If Not @error Then
    OnAutoItExitRegister("OnAutoItExit")
Else
    MsgBox(16 + 262144, "Error", "DllCallbackRegister(_WinEventProc) did not succeed.")
    Exit
EndIf

$hHook = _SetWinEventHook($EVENT_Min, $EVENT_Max)
If @error Then
    MsgBox(16 + 262144, "Error", "_SetWinEventHook() did not succeed.")
    Exit
EndIf

While True
    Sleep(250)
    ;function that get last menu item propieties
    _PopupMenuItem()
WEnd

Func _WinEventProc($hHook, $iEvent, $hWnd, $idObject, $idChild, $iEventThread, $iEventTime)
    Switch $iEvent
        Case 6
            ConsoleWrite("(i) pop-up menu was displayed..."&@CRLF)
        Case 7
            _SelectedMenuItem()
            ConsoleWrite("(i) pop-up menu was closed..."&@CRLF)
    EndSwitch
EndFunc   ;==>_WinEventProc

Func _SetWinEventHook($iEventMin, $iEventMax)
    Local $aRet
    Local Const $WINEVENT_OUTOFCONTEXT = 0x0
    Local Const $WINEVENT_SKIPOWNPROCESS = 0x2
    $aRet = DllCall("User32.dll", "hwnd", "SetWinEventHook", _
            "uint", $iEventMin, _
            "uint", $iEventMax, _
            "hwnd", 0, _
            "ptr", DllCallbackGetPtr($hWinEventProc), _
            "int", 0, _
            "int", 0, _
            "uint", BitOR($WINEVENT_OUTOFCONTEXT, $WINEVENT_SKIPOWNPROCESS))
    If @error Then Return SetError(@error, 0, 0)
    Return $aRet[0]
EndFunc   ;==>_SetWinEventHook

Func OnAutoItExit()
    If $hWinEventProc Then DllCallbackFree($hWinEventProc)
    If $hHook Then DllCall("User32.dll", "int", "UnhookWinEvent", "hwnd", $hHook)
EndFunc   ;==>OnAutoItExit


Func _PopupMenuItem()
  Local $aRect, $hMenu = False, $MenuItem[9]
  Local $hDesktop = _WinAPI_GetDesktopWindow()
  Local $hChild = _WinAPI_GetWindow( $hDesktop, 5 ), $i = 0

    Local $hWindow = WinGetHandle("[ACTIVE]")
    Local $hChild = _WinAPI_GetWindow($hDesktop, 5)
    Local $i = 0
    While $hChild And $i < 5
      If _WinAPI_GetClassName( $hChild ) = "#32768" Then
        $hMenu = _SendMessage( $hChild, 0x01E1 )
        ExitLoop
      EndIf
      $hChild = _WinAPI_GetWindow( $hChild, 2 )
      $i += 1
    WEnd
    If $hMenu Then
      Local $iCount = _GUICtrlMenu_GetItemCount($hMenu), $sText
      For $i = 0 To $iCount - 1
        If $i = _GUICtrlMenu_MenuItemFromPoint($hWindow, $hMenu) Then
            If _GUICtrlMenu_GetItemEnabled($hMenu, $i) Then
                $MenuItem[0] = $hWindow
                $MenuItem[1] = $hMenu
                $MenuItem[2] = $i
                $MenuItem[3] = _GUICtrlMenu_GetItemText($hMenu, $i )
                $MenuItem[4] = _GUICtrlMenu_GetItemRectEx($hWindow, $hMenu, $i)
                If $LastMenuItem[0] <> $MenuItem[0] Or $LastMenuItem[1] <> $MenuItem[1] Or $LastMenuItem[2] <> $MenuItem[2] Then
                    $LastMenuItem = $MenuItem
                    ConsoleWrite("* mouse over item: "&$LastMenuItem[3]&@CRLF)
                EndIf
            EndIf
            ExitLoop
        EndIf
      Next
    EndIf
EndFunc

Func _SelectedMenuItem()
    Local $tPOINT = _WinAPI_GetMousePos()
    ConsoleWrite("(i) last mouse position: "&DllStructGetData($tPoint, "X")&":"&DllStructGetData($tPoint, "Y")&@CRLF)
    If _WinAPI_PtInRect($LastMenuItem[4], $tPOINT) Then
        ConsoleWrite("-> selected item: "&$LastMenuItem[3]&@CRLF)
    Else
        ConsoleWrite("(i) last menu item position Rect: "&DllStructGetData($LastMenuItem[4], "Left")&">"&DllStructGetData($LastMenuItem[4], "Right")&":"&DllStructGetData($LastMenuItem[4], "Top")&"V"&DllStructGetData($LastMenuItem[4], "Bottom")&@CRLF)
    EndIf
EndFunc

; Functions From Include Directory
Func _WinAPI_GetWindow($hWnd, $iCmd)
    Local $aResult = DllCall("user32.dll", "hwnd", "GetWindow", "hwnd", $hWnd, "uint", $iCmd)
    If @error Then Return SetError(@error, @extended, 0)

    Return $aResult[0]
EndFunc

Func _WinAPI_GetClassName($hWnd)
    If Not IsHWnd($hWnd) Then $hWnd = GUICtrlGetHandle($hWnd)
    Local $aResult = DllCall("user32.dll", "int", "GetClassNameW", "hwnd", $hWnd, "wstr", "", "int", 4096)
    If @error Or Not $aResult[0] Then Return SetError(@error, @extended, '')

    Return SetExtended($aResult[0], $aResult[2])
EndFunc

Func _GUICtrlMenu_GetItemCount($hMenu)
    Local $aResult = DllCall("user32.dll", "int", "GetMenuItemCount", "handle", $hMenu)
    If @error Then Return SetError(@error, @extended, -1)
    Return $aResult[0]
EndFunc

Func _SendMessage($hWnd, $iMsg, $wParam = 0, $lParam = 0, $iReturn = 0, $wParamType = "wparam", $lParamType = "lparam", $sReturnType = "lresult")
    Local $aResult = DllCall("user32.dll", $sReturnType, "SendMessageW", "hwnd", $hWnd, "uint", $iMsg, $wParamType, $wParam, $lParamType, $lParam)
    If @error Then Return SetError(@error, @extended, "")
    If $iReturn >= 0 And $iReturn <= 4 Then Return $aResult[$iReturn]
    Return $aResult
EndFunc

Func _GUICtrlMenu_GetItemRectEx($hWnd, $hMenu, $iItem)
    Local $tRECT = DllStructCreate("struct;long Left;long Top;long Right;long Bottom;endstruct")
    Local $aResult = DllCall("user32.dll", "bool", "GetMenuItemRect", "hwnd", $hWnd, "handle", $hMenu, "uint", $iItem, "struct*", $tRECT)
    If @error Then Return SetError(@error, @extended, 0)
    Return SetExtended($aResult[0], $tRECT)
EndFunc

Func _GUICtrlMenu_GetItemText($hMenu, $iItem, $bByPos = True)
    Local $iByPos = 0

    If $bByPos Then $iByPos = 0x00000400
    Local $aResult = DllCall("user32.dll", "int", "GetMenuStringW", "handle", $hMenu, "uint", $iItem, "wstr", "", "int", 4096, "uint", $iByPos)
    If @error Then Return SetError(@error, @extended, 0)
    Return SetExtended($aResult[0], $aResult[3])
EndFunc

Func _GUICtrlMenu_GetItemInfo($hMenu, $iItem, $bByPos = True)
    Local $tInfo = DllStructCreate("uint Size;uint Mask;uint Type;uint State;uint ID;handle SubMenu;handle BmpChecked;handle BmpUnchecked;" & _
    "ulong_ptr ItemData;ptr TypeData;uint CCH;handle BmpItem")
    DllStructSetData($tInfo, "Size", DllStructGetSize($tInfo))
    DllStructSetData($tInfo, "Mask", 0x0000003F)
    Local $aResult = DllCall("user32.dll", "bool", "GetMenuItemInfo", "handle", $hMenu, "uint", $iItem, "bool", $bByPos, "struct*", $tInfo)
    If @error Then Return SetError(@error, @extended, 0)
    Return SetExtended($aResult[0], $tInfo)
EndFunc

Func _GUICtrlMenu_GetItemStateEx($hMenu, $iItem, $bByPos = True)
    Local $tInfo = _GUICtrlMenu_GetItemInfo($hMenu, $iItem, $bByPos)
    Return DllStructGetData($tInfo, "State")
EndFunc

Func _GUICtrlMenu_GetItemEnabled($hMenu, $iItem, $bByPos = True)
    Return BitAND(_GUICtrlMenu_GetItemStateEx($hMenu, $iItem, $bByPos), 0x00000002) = 0
EndFunc

Func _GUICtrlMenu_MenuItemFromPoint($hWnd, $hMenu, $iX = -1, $iY = -1)
    If $iX = -1 Then $iX = _WinAPI_GetMousePosX()
    If $iY = -1 Then $iY = _WinAPI_GetMousePosY()
    Local $aResult = DllCall("user32.dll", "int", "MenuItemFromPoint", "hwnd", $hWnd, "handle", $hMenu, "int", $iX, "int", $iY)
    If @error Then Return SetError(@error, @extended, -1)
    Return $aResult[0]
EndFunc

Func _WinAPI_GetMousePosX($bToClient = False, $hWnd = 0)
    Local $tPoint = _WinAPI_GetMousePos($bToClient, $hWnd)
    If @error Then Return SetError(@error, @extended, 0)

    Return DllStructGetData($tPoint, "X")
EndFunc

Func _WinAPI_GetMousePosY($bToClient = False, $hWnd = 0)
    Local $tPoint = _WinAPI_GetMousePos($bToClient, $hWnd)
    If @error Then Return SetError(@error, @extended, 0)

    Return DllStructGetData($tPoint, "Y")
EndFunc

Func _WinAPI_PtInRect(ByRef $tRECT, ByRef $tPoint)
    Local $aResult = DllCall("user32.dll", "bool", "PtInRect", "struct*", $tRECT, "struct", $tPoint)
    If @error Then Return SetError(@error, @extended, False)

    Return $aResult[0]
EndFunc

Func _WinAPI_GetMousePos($bToClient = False, $hWnd = 0)
    Local $iMode = Opt("MouseCoordMode",1)
    Local $aPos = MouseGetPos()
    Opt("MouseCoordMode", $iMode)

    Local $tPoint = DllStructCreate("struct;long X;long Y;endstruct")
    DllStructSetData($tPoint, "X", $aPos[0])
    DllStructSetData($tPoint, "Y", $aPos[1])
    If $bToClient And Not _WinAPI_ScreenToClient($hWnd, $tPoint) Then Return SetError(@error + 20, @extended, 0)

    Return $tPoint
EndFunc

Func _WinAPI_ScreenToClient($hWnd, ByRef $tPoint)
    Local $aResult = DllCall("user32.dll", "bool", "ScreenToClient", "hwnd", $hWnd, "struct*", $tPoint)
    If @error Then Return SetError(@error, @extended, False)

    Return $aResult[0]
EndFunc

Func _WinAPI_GetDesktopWindow()
    Local $aResult = DllCall("user32.dll", "hwnd", "GetDesktopWindow")
    If @error Then Return SetError(@error, @extended, 0)

    Return $aResult[0]
EndFunc

Cause of the problem: _WinAPI_PtInRect fails on Explorer.exe windows because _GUICtrlMenu_GetItemRectEx returns relative rectangle bounds to the menu (i think) where always X1 = 0.

Any suggestions to make this script works on all menus?

Thanks

Edited by k4rl3on
Link to comment
Share on other sites

hi junkew

i have to admit that i'm lost.

i have downloaded all files presents in:

where can i found the way to retreive selected menu item text in these? i tried to run ex28_eventsMSAA.au3 from EXAMPLES folder but stills  "MSAccessibility.au3" missing and i cant found it in both UIA_V0_51 and EXAMPLES_V0_5 folders :blink:

Link to comment
Share on other sites

Try with simplespy to get the basic properties of the popupwindow menu probably a class like #..... after that its just a treewalker in one of the first few examples. The events examples could be used but is more coding. Most likely msaa logic is not needed for popup menu.

Link to comment
Share on other sites

For a standard #32768 class context/popup menu you can get the information with this code. Works for the desktop too.

#include <GuiMenu.au3>
#include <SendMessage.au3>
#include <WindowsConstants.au3>

Example()

Func Example()
  ConsoleWrite( "5 seconds to open a menu ..." & @CRLF )
  Sleep( 5000 ) ; 5 seconds to open a menu
  Local $hMenu = GetMenuHandle()
  If Not $hMenu Then Return
  Local $iCount = _GUICtrlMenu_GetItemCount( $hMenu ), $sText, $iState
  For $i = 0 To $iCount - 1
    $sText = _GUICtrlMenu_GetItemText( $hMenu, $i )
    $iState = _GUICtrlMenu_GetItemState( $hMenu, $i )
    If $sText <> "" Then ConsoleWrite( $sText )
    If BitAND( $iState, 16 ) Then ConsoleWrite( "  <--  Selected" )
    ConsoleWrite( @CRLF )
  Next
EndFunc

Func GetMenuHandle()
  Local $hDesktop = _WinAPI_GetDesktopWindow(), $i = 0
  Local $hChild = _WinAPI_GetWindow( $hDesktop, $GW_CHILD )
  While $hChild And $i < 50
    If _WinAPI_GetClassName( $hChild ) = "#32768" Then ExitLoop
    $hChild = _WinAPI_GetWindow( $hChild, $GW_HWNDNEXT )
    $i += 1
  WEnd
  If $i = 50 Then Return 0
  Local $hMenu = _SendMessage( $hChild, $MN_GETHMENU )
  If $hMenu > 0 Then Return $hMenu
  Return 0
EndFunc

Information is printed in SciTE console.

See this post to get the information for a non-standard menu. The script is based on IU Automation code.

Edited by LarsJ
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

×
×
  • Create New...