k4rl3on Posted February 8, 2016 Share Posted February 8, 2016 (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... expandcollapse popupLocal $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 February 11, 2016 by k4rl3on Link to comment Share on other sites More sharing options...
junkew Posted February 9, 2016 Share Posted February 9, 2016 I would suggest to use IUIAutomation in examples thread  FAQ 31 How to click some elements, FAQ 40 Test automation with AutoIt, Multithreading CLR .NET Powershell CMDLets Link to comment Share on other sites More sharing options...
k4rl3on Posted February 9, 2016 Author Share Posted February 9, 2016 thank you for your suggestion, if it works i will publish the solution here Link to comment Share on other sites More sharing options...
k4rl3on Posted February 9, 2016 Author Share Posted February 9, 2016 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 Link to comment Share on other sites More sharing options...
junkew Posted February 9, 2016 Share Posted February 9, 2016 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. FAQ 31 How to click some elements, FAQ 40 Test automation with AutoIt, Multithreading CLR .NET Powershell CMDLets Link to comment Share on other sites More sharing options...
LarsJ Posted February 10, 2016 Share Posted February 10, 2016 (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 February 10, 2016 by LarsJ k4rl3on 1 Controls,  File Explorer,  ROT objects,  UI Automation,  Windows Message MonitorCompiled code: Accessing AutoIt variables,  DotNet.au3 UDF,  Using C# and VB codeShell menus: The Context menu,  The Favorites menu. Shell related: Control Panel,  System Image ListsGraphics related: Rubik's Cube,  OpenGL without external libraries,  Navigating in an image,  Non-rectangular selectionsListView controls: Colors and fonts,  Multi-line header,  Multi-line items,  Checkboxes and icons,  Incremental searchListView controls: Virtual ListViews,  Editing cells,  Data display functions Link to comment Share on other sites More sharing options...
k4rl3on Posted February 11, 2016 Author Share Posted February 11, 2016 Thanks Link to comment Share on other sites More sharing options...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now