k4rl3on

[SOLVED] Get selected item text from a menu

7 posts in this topic

#1 ·  Posted (edited)

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

Share this post


Link to post
Share on other sites

thank you for your suggestion, if it works i will publish the solution here :)

Share this post


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

Share this post


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

Share this post


Link to post
Share on other sites

#6 ·  Posted (edited)

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
1 person likes this

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

  • Similar Content

    • ur
      By ur
      I am trying to identify the window based on the window title and text.
      The title will be the "erwin DM - filename"

      It is working till date, but some operating systems our application is displaying window as "erwin DM - [filename]"
       
      I tried  "erwin DM - *filename*" But this regular expression is not working.
      Any suggestion?
       
      $sModelFile = "C:\Users\Administrator\Documents\My Models\eMovies.erwin" $wdModel = _WinWaitActivate1("erwin DM - "&FileNameOnly($sModelFile),"") Func _WinWaitActivate1($title,$text,$timeout=0);Will Return the window Handler Logging("Waiting for "&$title&":"&$text) $dHandle = WinWait($title,$text,$timeout) if not ($dHandle = 0) then If Not WinActive($title,$text) Then WinActivate($title,$text) return WinWaitActive($title,$text,$timeout) Else Logging("Timeout occured while waiting for the window...") Exit EndIf EndFunc Func FileNameOnly($sFilePath) Local $sDrive = "", $sDir = "", $sFileName = "", $sExtension = "" Local $aPathSplit = _PathSplit($sFilePath, $sDrive, $sDir, $sFileName, $sExtension) ;_ArrayDisplay($aPathSplit, "_PathSplit of " & @ScriptFullPath) return $sFileName EndFunc  
    • nacerbaaziz
      By nacerbaaziz
      Detect if the focus item is a button or check box
      Hello everyone.
      My dears I have a problem
      When I try to use
      ControlGetFocus function
      it  doesn't recognize the check  boxes.
      The result is that the selector is a button
      Although the specified is a check box
      but I want a way to identify the check boxes.
      Because I want to make a tool that helps screen readers users
      I hope you have the solution.
      Thank you in advance.
       
    • natedog102
      By natedog102
      This small UDF helps you save any text to speech to a .wav file. 
      UDF:
      #AutoIt3Wrapper_Au3Check_Parameters=-q -d -w 1 -w 2 -w 3 -w- 4 -w 5 -w 6 -w- 7 #include-once ; #INDEX# ======================================================================================================================= ; Title .........: _TTStoWav() ; AutoIt Version : 3.3.14.2 ; Language ......: English ; Author(s) .....: natedog102 ; Modifiers .....: ; Forum link ....: https://www.autoitscript.com/forum/topic/191573-tts-text-to-speech-to-wav-file-using-sapi/ ; Description ...: TTS to .wav file using SAPI ; =============================================================================================================================== ; #FUNCTION# ==================================================================================================================== ; Name...........: _TTStoWav ; Description ...: TTS to .wav file using SAPI ; Syntax.........: _TTStoWav($sText, $sLocation[, $iRate = 1[, $iVolume = 100]]) ; Parameters ....: $sText - String you want converted to a .wav ; $sLocation - Save location + file name, example: "C:\folder\sample.wav" ; $iRate - TTS speak rate between -10 and 10 ; $iVolume - Volume of the TTS between 0 and 100 ; Return values .: Success - Returns one ; Failure - Returns zero ; Author ........: natedog102 ; Modified.......: ; Remarks .......: ; Related .......: ; Link ..........: ; Example .......: No ; =============================================================================================================================== Func _TTStoWav($sText, $sLocation, $iRate = 1, $iVolume = 100) $oFileStream = ObjCreate("SAPI.SpFileStream") If @error Then Return 0 EndIf $oFileStream.Format.Type = 39 $oFileStream.Open($sLocation, 3) $oSpeech = ObjCreate('SAPI.SpVoice') If @error Then Return 0 EndIf $oSpeech.AudioOutputStream = $oFileStream $oSpeech.Rate = $iRate $oSpeech.Volume = $iVolume $oSpeech.Speak($sText, 3) $oSpeech.WaitUntilDone(10000) $oFileStream.Close() Return 1 EndFunc ;==>_TTStoWav And here's example usage:
      _TTStoWav("This is a test", "C:\folder-must-exist\sample2.wav") I have never made a UDF before so I don't know if I coded it correctly or if I followed all the recommended guidelines. Apologies if I haven't.
      DOWNLOAD:
       _TTStoWav.au3
    • Shirdish_chakravarthi
      By Shirdish_chakravarthi
      Hello Guys this is my gui and i need the numeric value typed in the highlighted  inputbox. if this value is greater than 1 i need to perform some operations
      \
       
      Here is the summary about that field which i got from wininfo tool

      i tried writing the code as shown below.. but the msgbox is popping up empty value..when the same code is pasted under some button action i am able to access the value present in the field..but when placed alone as written in the attached script i am unable to fetch it... 

      here i am using the valu present in the field

      can you guys tell me what's wrong??? or how can i get that value?
       
      My_GUI.au3

    • Shirdish_chakravarthi
      By Shirdish_chakravarthi
      in need the path/text present int above shown input box to be copied or retrieved into a variable .. how can i do that?
       
      Thanks