Jump to content
k4rl3on

[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

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

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

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

    • By nacerbaaziz
      good morning all.
      first lit me give you all a thinks to your help
      you're help me allot thank you all.
      sirs today i've a new problem
      it's not a problem but thing i want to add it to blind accessability.
      the ListBox can send a notification when the user send a double click on it items
      but as we know that the blind users can't use the mouse for that they use the keybord to navigate.
      as we know that the enter replace the double click on the keybord
      for that i need when the user send a inter above any listBox item the list send a double click notification.
      i know some of you tell me that i can use the  GUISetAccelerators function
      but the enter has a other tasks such as leav a blanc line on edits and activate the defaultButton and other tasks.
      that what i need and i hope that you can help me
      this is a simple example.
      #include <GUIConstantsEx.au3> #include <StructureConstants.au3> #include <MsgBoxConstants.au3> #include <WindowsConstants.au3> #include <ListBoxConstants.au3> Example() Func Example() Local $sMESSAGE = "The following buttons have been clicked" GUICreate("My GUI list") ; will create a dialog box that when displayed is centered Local $idButton_Add = GUICtrlCreateButton("Add", 64, 32, 75, 25) Local $idButton_Clear = GUICtrlCreateButton("Clear", 64, 72, 75, 25) global $idMylist = GUICtrlCreateList("buttons that have been clicked", 176, 32, 121, 97) GUICtrlSetLimit(-1, 200) ; to limit horizontal scrolling GUICtrlSetData(-1, $sMESSAGE) global $DummyList = GUICtrlCreateDummy() GUICtrlSendToDummy($DummyList, 1) Local $idButton_Close = GUICtrlCreateButton("my closing button", 64, 160, 175, 25) GUIRegisterMsg($WM_command, "WM_command") GUISetState(@SW_SHOW) ; Loop until the user exits. While 1 Switch GUIGetMsg() Case $GUI_EVENT_CLOSE ExitLoop Case $idButton_Add GUICtrlSetData($idMylist, "You clicked button No1|") Case $idButton_Clear GUICtrlSetData($idMylist, "") Case $idButton_Close MsgBox($MB_SYSTEMMODAL, "", "the closing button has been clicked", 2) Exit case $DummyList $g_iTemp = GUICtrlRead($DummyList) if $g_iTemp = $LBN_DBLCLK then ;$LBN_DBLCLK then msgBox(64, "", "") endIf GUICtrlSendToDummy($DummyList, 0) EndSwitch WEnd EndFunc ;==>Example Func WM_command($hWnd, $iMsg, $wParam, $lParam) #forceref $hWnd, $iMsg Local $tNMHDR = DllStructCreate($tagNMHDR, $lParam) $hWndFrom = $lParam $iCode = BitShift($wParam, 16) ; Hi Word Switch $hWndFrom Case GUICtrlGetHandle($idMylist) Switch $iCode Case $LBN_SELCHANGE, $LBN_DBLCLK, $LBN_SELCANCEL, $LBN_SETFOCUS GUICtrlSendToDummy($DummyList, $iCode) case else ;GUICtrlSendToDummy($DummyTreeview, 1) EndSwitch EndSwitch Return $GUI_RUNDEFMSG EndFunc  
    • By _leo_
      Hey there!
      I am having a problem with clicking a button in IE. As you can see in the code, there would be a class name to click it, but since this button exists several times with exactly the same code, I can only use the "Noch nicht veröffentlicht" to separate it from the others. But this part is just pure text without any classification. 
      "<div class="checkmark" data-v-1e5b1f70=""><svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 16 16" data-v-1e5b1f70=""><circle cx="8" cy="8" r="8" data-v-1e5b1f70="" /></svg></div>Noch nicht veröffentlicht"
      I was trying to click it with:
      #include <IE.au3> local $oNNVs = _IEFormGetCollection($oIE2) For $oNNV in $oNNVs If String($oNNV.innertext) = "Noch nicht veröffentlicht" Then _IEAction($oNNV, "click") EndIf Next and:
      $inputs = $oIE2.document.getElementsByTagName("div") For $input In $inputs If $input.innertext == "Noch nicht veröffentlicht" then $input.click() EndIf Next But nothing seems to work. Thanks for the help!
    • By GillesMaisonneuve
      Good morning,
       
      I am trying to align a two-line text in a button so that it would be centered in the button but can't succeed to do it for the vertical part.
      Can someone tell me what I do wrong ?
      Here is my code:
      $btn3 = GUICtrlCreateButton("Execute " & @LF & " file 1", 100, 65, 60, 50, _ BitOr($BS_CENTER,$BS_BOTTOM,$BS_MULTILINE)) and here is the resulting layout I get in my window.
      I tried to follow the help, made a lot of different parameter combinations (here it's `$BS_BOTTOM`, but I tried `$BS_VCENTER` and `$BS_TOP`), still get the same result : vertical alignement is a no go for me.
      Can someone help ?
      TIA
      Gilles

    • By dascondor
      So basically need to pull text from a .txt file or .sql and use that text to replace something within a different file.
       
      First File:
      jumps
       
      Or:
      Text1 = jumps
      Text2 = fence
      Second File:
      My Dog XXXXX over the YYYYY.
       
       
      After Script:
      My Dog jumps over the fence
       
      I have found a good start, I'm basic programmer at best, I have some experience but this gets out of my wheel house.
       
      Any help is appreciated.
       
       
    • By nacerbaaziz
      hi dears, i have a question please
      am now working for an audio player for the blind users
      and i added an option to open a file from the right click context menu.
      but here there is a small problem, i hope that you can help me to find a solution for it.
      the problem is as follow :
      when i added the problem into the context menu it work with successfuly if the user select just one file.
      but if the user selected more then one file, here the problem
      a multiple sections of the program will open.
      my question is :
      how i can detect the multiple selection from the context menu?
      i know that it so  hard, but i know also that you are harder than it
      for that dears please try to give me an solution for that problem.
      thanks in advance
×
×
  • Create New...