Jump to content

[SOLVED] Get selected item text from a menu


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 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:

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.

Link to post
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 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
  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By MrKm
      AutoIT-OCRSpace-UDF1.3.zip
      This tiny yet powerful UDF will help you to convert Images to text with the help of  OCRSpace API version 3.50 .
      Detect text from a local file.
      ; ========================================================= ; Example 2 : Gets text from an image from a local path reference ; : Searchable PDF is not requested by default. ; : Processes it using a basic OCR logic. ; ========================================================= $b_Create_Searchable_PDF = True ; Use a table logic for receipt OCR $b_Table = True ; Set your key here. $v_OCRSpaceAPIKey = "" $OCROptions = _OCRSpace_SetUpOCR($v_OCRSpaceAPIKey, 1, $b_Table, True, "eng", True, Default, Default, $b_Create_Searchable_PDF) $sText_Detected = _OCRSpace_ImageGetText($OCROptions, @scriptdir & "\receipt.jpg", 0, "SEARCHABLE_URL") ConsoleWrite( _ " Detected text : " & $sText_Detected & @CRLF & _ " Error Returned : " & @error & @CRLF & _ " PDF URL : " & Eval("SEARCHABLE_URL") & @CRLF)  
      Detect text from a URL reference.
      ; ========================================================= ; Example 1 : Gets text from an image using a url reference ; : Searchable PDF is not requested. ; : Processes it using a basic OCR logic. ; ========================================================= $v_OCRSpaceAPIKey = "" ; SetUp some preferences.. $OCROptions = _OCRSpace_SetUpOCR($v_OCRSpaceAPIKey, 1, False, True, "eng", True, Default, Default, False) ; Make the request.. $sText_Detected = _OCRSpace_ImageGetText($OCROptions, "https://i.imgur.com/vbYXwJm.png", 0) ConsoleWrite( _ " Detected text : " & $sText_Detected & @CRLF & _ " Error Returned : " & @error & @CRLF)    
      Detect text from a URL reference to an array
      #include "OCRSpaceUDF\_OCRSpace_UDF.au3" #include <array.au3> ; Set your key here. $v_OCRSpaceAPIKey = "" $OCROptions = _OCRSpace_SetUpOCR($v_OCRSpaceAPIKey, 1, $b_Table, True, "eng", True, Default, Default, False) ; Below, the return type is set to 1 to return an array containing the coordinates of the bounding boxes for each word detected, ; in the format : #WordDetected , #Left , #Top , 3Height, #Width $aText_Detected = _OCRSpace_ImageGetText($OCROptions, "https://i.imgur.com/Z1enogD.jpeg", 1) _ArrayDisplay($aText_Detected, "")  
       
       
       
       
      https://github.com/KabueMurage/AutoIT-OCRSpace-UDF
      Download Latest Version : 
       
    • By nacerbaaziz
      hello evrybody
      here is an example about how to split your texts using a delimiter with the ability to select how much of delimiters shows in each colum  with $i_number
      e.g
      you have a long text and you want to split it in an array
      that evry colum have a number (n) of lines
      i made a function that do that for you
      just call it with a three params
      $s_text
      your text
      $i_number
      the number that you want to put in each col
      $s_siparator
      the siparator
      default is "|"
      here is the function with example
      i hope that it will be useful for you
       
      ****
       
      #include <Array.au3> $s_txt = "some text1some text2|some text3|some text4|some text5|some text6" $array = splitText($s_txt, 2) _ArrayDisplay($array) Func splitText($s_text, $i_number, $s_siparator = "|") Local $a_TXT = StringSplit($s_text, $s_siparator) Local $a_Return[$a_TXT[0] + 1] If ($a_TXT[0] <= $i_number) Or ($i_number <= 0) Then ReDim $a_Return[2] $a_Return[0] = 1 $a_Return[1] = $s_text Return $a_Return EndIf Local $i_Processed = 1, $i_arrayProcessed = 1 Do For $i = $i_Processed To ($i_Processed + $i_number) - 1 If ($a_TXT[0] < $i) Then ExitLoop If Not ($a_Return[$i_arrayProcessed]) Then $a_Return[$i_arrayProcessed] = $a_TXT[$i] Else $a_Return[$i_arrayProcessed] &= $s_siparator & $a_TXT[$i] EndIf $i_Processed += 1 Next $i_arrayProcessed += 1 Until ($a_TXT[0] < $i_Processed) ReDim $a_Return[$i_arrayProcessed] $a_Return[0] = $i_arrayProcessed - 1 Return $a_Return EndFunc ;==>splitText
      accept my greetings
      thanks to
      @Dan_555
      for his notes
       
    • By nacerbaaziz
      hello autoit team
      please i've a question for you.
      am creating a audio player
      and in this audio player i want to show the current trac info
      such as the total time and the position ... etc
      i know i can show it as label
      but the screen reader for the blind read the text every change
      because it have a screen scan
      what i want is to show this informations but such image or icon
      i mean i need to create
      GUICtrlCreatepic or GUICtrlCreateicon ....
      or some thing as that
      and show this informations as image on it
      i think that i can do that with the 
      _GDIPlus functions
      but i couldn't find the currect way to do it
      i tried the _GDIPlus_GraphicsDrawString
      but i couldn't know how it work
      what i need is a small example that create a GUI
      and add a multy line text to it as graphic or image.
      so i need a simple way because it will changed every sec
      i hope any one can help me to do that
      global $GUI = GUICreate("text", 400, 400) global $label = GUICtrlCreateLabel(GetText(), 10, 10, 380, 380) GUISetState() do sleep(100) until GUIGetMSG() = -3 exit func GetText() return StringFormat("file name is test.mp3 \r\n total time is 00:30:00 \r\n position is 00:05:50") endFunc  
    • By Irios
      I have several context menus that's been built using _GUICtrlMenu_CreatePopup() and _GUICtrlMenu_AddMenuItem().
      When using _GUICtrlMenu_CreatePopup() it returns the identifier of the clicked item, sure. But it blocks the main loop while the menu is open.
      When using _GUICtrlMenu_CreatePopup($MNS_MODELESS) it returns immediately (of course), but I cannot figure out how/where to read the item that was clicked. Is there a Windows Message (WM) somewhere that is triggered? I've spend an entire day trying to figure out how to do this, but I'm not getting anywhere. My google fu is depleted (I must have tried like hundreds of script variations today), I'm about to smash my keyboard and just go to bed, and abandon the entire endeavor of making pretty menus and go back to using Koda and forget about using menu icons, colors,  etc.
      Anyone wanna help me out with how to read the clicked item? Before I smash my keyboard, please...
       
      Example script here:
       
      #include <GuiMenu.au3> #include <WinAPIError.au3> #include <WindowsConstants.au3> #include <GUIConstantsEx.au3> GUIRegisterMsg($WM_CONTEXTMENU, "_WM_CONTEXTMENU") Global $idMenuItem = 10000, $counter = 0 Global $hGUI = GUICreate("Menu test", 400, 300) Global $hListview = GUICtrlCreateListView("", 2, 2, 396, 200) GUISetState(@SW_SHOW) ;~ Global $hMenuContext = _GUICtrlMenu_CreatePopup() Global $hMenuContext = _GUICtrlMenu_CreatePopup($MNS_MODELESS) _GUICtrlMenu_AddMenuItem($hMenuContext, "Menu item", $idMenuItem) Global $hTimer = TimerInit() Do If TimerDiff($hTimer)>200 Then $counter += 1 ConsoleWrite($counter & " " ) $hTimer = TimerInit() EndIf Until GUIGetMsg() = $GUI_EVENT_CLOSE Exit Func _WM_CONTEXTMENU($hWnd, $iMsg, $wParam, $lParam) #forceref $hWnd, $iMsg, $lParam Switch $wParam Case GUICtrlGetHandle($hListview) ConsoleWrite( @CRLF & "_GUICtrlMenu_TrackPopupMenu = " & _GUICtrlMenu_TrackPopupMenu($hMenuContext, $wParam, -1, -1, 1, 1, 2) & @CRLF) EndSwitch EndFunc  
       
    • By nacerbaaziz
      hello autoit group
      please i've a question
      i had make a function that put some thing into the clipBoard and paste it
      using the send function
      e.g
      ClipPut("hello")
      send("^v")
       
      when the keyboard is english all things work fine
      but when the keyboard is arabic the send command write the ltr v insted of the text in clipboard
      i was tryed to use
      send("{ctrldown}v{ctrlUp}")
      but the same
      please can any one help me
       
×
×
  • Create New...