Jump to content
k4rl3on

[SOLVED] Get selected item text from a menu

Recommended Posts

k4rl3on

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
k4rl3on

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

Share this post


Link to post
Share on other sites
k4rl3on

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
junkew

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
LarsJ

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
  • Like 1

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

    • therks
      By therks
      I'm trying to create a simple clock widget that automatically scales the text to the size of the window. I came up with the following method, but it doesn't work as well as I'd like. It especially has trouble scaling to the width of the window for some reason (in the example, try resizing the window to be narrow and tall).
      Does anyone have a better method?
      #include <Misc.au3> #include <WinAPIConv.au3> #include <GUIConstants.au3> #include <GDIPlus.au3> Opt('MustDeclareVars', 1) Global $_FONT_FAMILY = 'Arial', $_LB_TEXT Main() Func Main() _GDIPlus_Startup() Local $hGUI GUIRegisterMsg($WM_SIZE, WM_SIZE) $hGUI = GUICreate('', 300, 100, Default, Default, $WS_OVERLAPPEDWINDOW, BitOR($WS_EX_TOOLWINDOW, $WS_EX_TOPMOST)) $_LB_TEXT = GUICtrlCreateLabel('This is a string', 0, 0, 300, 100, BitOR($SS_CENTER, $SS_CENTERIMAGE)) GUICtrlSetFont($_LB_TEXT, _MeasureString($hGUI, GUICtrlRead($_LB_TEXT), $_FONT_FAMILY), 0, 0, $_FONT_FAMILY, 5) GUISetState() Local $iGM While 1 $iGM = GUIGetMsg() Switch $iGM Case $GUI_EVENT_CLOSE ExitLoop EndSwitch WEnd _GDIPlus_Shutdown() EndFunc Func WM_SIZE($hWnd, $iMsg, $wParam, $lParam) GUICtrlSetFont($_LB_TEXT, _MeasureString($hWnd, GUICtrlRead($_LB_TEXT), $_FONT_FAMILY), 0, 0, $_FONT_FAMILY, 5) EndFunc Func _MeasureString($hWnd, $sString, $sFont = 'Arial') Local $iError, $aSize, $hGraphic, $hFormat, $hFamily, $tLayout, $iFontSize, $hFont, $aInfo If Not IsHWnd($hWnd) Then $hWnd = GUICtrlGetHandle($hWnd) EndIf $aSize = WinGetClientSize($hWnd) $hGraphic = _GDIPlus_GraphicsCreateFromHWND($hWnd) $hFormat = _GDIPlus_StringFormatCreate() $hFamily = _GDIPlus_FontFamilyCreate($sFont) $tLayout = _GDIPlus_RectFCreate(0, 0, $aSize[0], $aSize[1]) $iFontSize = 0 Do If Not $hFamily Then $iError = 1 $iFontSize = 10 ExitLoop EndIf $iFontSize += 1 $hFont = _GDIPlus_FontCreate($hFamily, $iFontSize, 0) $aInfo = _GDIPlus_GraphicsMeasureString($hGraphic, $sString, $hFont, $tLayout, $hFormat) _GDIPlus_FontDispose($hFont) If $aInfo[1] = 0 Then ExitLoop Until DllStructGetData($aInfo[0], 3) >= $aSize[0] Or DllStructGetData($aInfo[0], 4) >= $aSize[1] $iFontSize -= 1 _GDIPlus_FontFamilyDispose($hFamily) _GDIPlus_StringFormatDispose($hFormat) _GDIPlus_GraphicsDispose($hGraphic) Return SetError($iError, 0, $iFontSize) EndFunc
    • dadalt95
      By dadalt95
      Hi!
      I want to get the signal in this website:
      https://binary-signal.com/pt/chart/eurusd
      I have tried using _IEBodyReadText  and some _StringBetween. What happens is that the text are being update every tick and _IEBodyReadText doesn't.
      To perform the update I used  _IEAction($oIE, "refresh") but it's not good because the website block me after some time due too many requests..
      Is there any other way to get this text every tick?
      PS: The text I want to get is WAIT, CALL or PUT.
       
      Here is the code:
      global $oIE = _IECreate ('https://binary-signal.com/pt/chart/eurusd', 0, 1 , 1 , 0) Local $sText = _IEBodyReadText($oIE) $result = _StringBetween ( $sText , 'PUTEUR/USD on Binary-signal.com', 'sinal está PRONTA') ;MsgBox ( 0, "asf", $result[0]) $espera=StringInStr($result[0], "WAIT") $compra=StringInStr($result[0], "CALL") $venda=StringInStr($result[0], "PUT") ;MsgBox($MB_SYSTEMMODAL, "", $espera) $n=0 $c=0 Captar() Func Captar() ;_IENavigate($oIE, "https://binary-signal.com/pt/chart/eurusd") ;MsgBox($MB_SYSTEMMODAL, "", $n) Local $sText = _IEBodyReadText($oIE) $result = _StringBetween ( $sText , 'PUTEUR/USD on Binary-signal.com', 'sinal está PRONTA') If (Not $compra=0) And $n=0 Then MsgBox($MB_SYSTEMMODAL, "", "COMPRE") $n=1 $c=$c+1 ;_IEAction($oIE, "refresh") Sleep(60000) Captar() ElseIf (Not $venda=0) And $n=0 Then MsgBox($MB_SYSTEMMODAL, "", "VENDA") $c=$c+1 $n=1 ;_IEAction($oIE, "refresh") ;MsgBox($MB_SYSTEMMODAL, "", $n) Sleep(60000) Captar() ElseIf (Not $venda=0) And $n=1 Then ;MsgBox($MB_SYSTEMMODAL, "", "Esperando próxima rodada") $n=1 ;MsgBox($MB_SYSTEMMODAL, "", $n) ;_IEAction($oIE, "refresh") Sleep(60000) Captar() ElseIf (Not $venda=0) And $n=1 Then ;MsgBox($MB_SYSTEMMODAL, "", "Esperando próxima rodada") $n=1 ;MsgBox($MB_SYSTEMMODAL, "", $n) ;_IEAction($oIE, "refresh") Sleep(60000) Captar() Else ;MsgBox("", "", "ESPERE") $n=0 Sleep(1000) ;_IEAction($oIE, "refresh") Local $sText = _IEBodyReadText($oIE) $result = _StringBetween ( $sText , 'PUTEUR/USD on Binary-signal.com', 'sinal está PRONTA') Captar() EndIf EndFunc  
    • nacerbaaziz
      By nacerbaaziz
      Hello
      I have a query please
      It is about windows file explorer
      If i design a tool in autoit language to delete files for example
      and i  added it in the context menu in File Explorer
      If i right-click on any file And you choose the tool, it will be get the path of the selected file or folder
      The problem here is
      If i select a set of files or folders
      it only detect the first one
      The code that i  used is
      if $CMDLine[0] = 0 then
      msgBox(16, "error", "you don't selecte any files")
      exit
      else
      for $i = 1 to $CMDLine[0]
      msgBox(0, "files", $CMDLine[$i]
      next
      endIf
      exit
    • vin1
      By vin1
      i need a script that can replace words in text with other words from a words list
      it has to have 2 edit boxes input/output and a button "run"
      from word_list.txt it choses which word to replace with. words are separated with commas for multiple choice
       

       
       

       
       
      ; AutoIt GUI Example ; Created: 17/01/2005 - CyberSlug ; Modifed: 05/12/2011 - guinness ; Modifed: 09/06/2014 - mLipok #Region INCLUDE #include <AVIConstants.au3> #include <GuiConstantsEx.au3> #include <TreeViewConstants.au3> #EndRegion INCLUDE #Region GUI GUICreate("Sample GUI", 400, 420) GUISetIcon(@SystemDir & "\mspaint.exe", 0) #EndRegion GUI #Region EDIT GUICtrlCreateEdit(@CRLF & " Sample Edit Control", 10, 10, 380, 175) GUICtrlSetTip(-1, '#Region EDIT') #EndRegion EDIT #Region EDIT2 GUICtrlCreateEdit(@CRLF & " Sample Edit Control", 10, 185, 380, 175) GUICtrlSetTip(-1, '#Region EDIT') #EndRegion EDIT2 #Region BUTTON GUICtrlCreateButton("Sample Button", 10, 360, 100, 30) GUICtrlSetTip(-1, '#Region BUTTON') #EndRegion BUTTON #Region GUI MESSAGE LOOP GUISetState(@SW_SHOW) While 1 Switch GUIGetMsg() Case $GUI_EVENT_CLOSE ExitLoop EndSwitch WEnd GUIDelete() #EndRegion GUI MESSAGE LOOP  
    • Ambient
      By Ambient
      I have tried several way sto get this to work to no avail. Any help would be appreciated. #include <ListViewConstants.au3> #include <GuiListView.au3> #include "GuiListViewEx.au3" $idItem = GUICtrlCreateListViewItem($r & " | " & $c & $adoRs.Fields("TransactionAmount").value & " | " & $Timestamp & " | " & _StringProper($adoRs.Fields("Tillid").value) & @CRLF & @CRLF & "Card Number : " & $cardnum & " " , $idListView) ConsoleWrite( "IDITEM" & $idItem) If STRINGLEFT($adoRs.Fields("TransactionAmount").value,1)= "-" Then Consolewrite(" String is Negative" & @CRLF) ;Drops in Here as expected ;GUICtrlSetColor(-1, $COLOR_RED) ; Colour line if item is negative this didn' work $TxC= _GUICtrlListView_SetTextColor($hListView, $CLR_RED) ; I also tried $TxC= _GUICtrlListView_SetTextColor(-1, 0xFF0000) CONSOLEWRITE("TEXTCOLOUR RETURNED " & $TxC & @crlf) ; This is returning True ELSE Consolewrite(" String is NOT Negative" & @CRLF) EndIf  
×