MarkMontemuro Posted April 24, 2014 Share Posted April 24, 2014 I have a application that contains many child windows, and they all should support the same right click drop down context menu items like copy, select all, toggle caption bar or status bar , etc. Is there a way within Auto IT or even calling Win32API to discover these items? Thanks Link to comment Share on other sites More sharing options...
LarsJ Posted April 25, 2014 Share Posted April 25, 2014 Certainly. This can be done with the UI Automation framework.The example creates an UI Automation event handler, that listens for menu open events, and print the menu items in Scite console:expandcollapse popup#include "CUIAutomation2.au3" Opt( "MustDeclareVars", 1 ) Global Const $S_OK = 0x00000000 Global Const $E_NOINTERFACE = 0x80004002 Global Const $sIID_IUnknown = "{00000000-0000-0000-C000-000000000046}" Global $tIUIAutomationEventHandler, $oIUIAutomationEventHandler Global $oUIAutomation MainFunc() Func MainFunc() $oIUIAutomationEventHandler = ObjectFromTag( "oIUIAutomationEventHandler_", $dtagIUIAutomationEventHandler, $tIUIAutomationEventHandler ) If Not IsObj( $oIUIAutomationEventHandler ) Then Return $oUIAutomation = ObjCreateInterface( $sCLSID_CUIAutomation, $sIID_IUIAutomation, $dtagIUIAutomation ) If Not IsObj( $oUIAutomation ) Then Return Local $pUIElement $oUIAutomation.GetRootElement( $pUIElement ) ; Desktop If Not $pUIElement Then Return ; Menu open events If $oUIAutomation.AddAutomationEventHandler( $UIA_MenuOpenedEventId, $pUIElement, $TreeScope_Subtree, 0, $oIUIAutomationEventHandler ) Then Exit HotKeySet( "{ESC}", "Quit" ) While Sleep(100) WEnd EndFunc Func Quit() $oIUIAutomationEventHandler = 0 DeleteObjectFromTag( $tIUIAutomationEventHandler ) Exit EndFunc Func oIUIAutomationEventHandler_HandleAutomationEvent( $pSelf, $pMenu, $iEventId ) ; Ret: long Par: ptr;int ; Print menu name Local $oMenu = ObjCreateInterface( $pMenu, $sIID_IUIAutomationElement, $dtagIUIAutomationElement ), $sName $oMenu.AddRef() $oMenu.GetCurrentPropertyValue( $UIA_NamePropertyId, $sName ) If $sName = "" Then $sName = "Menu" ConsoleWrite( $sName & ":" & @CRLF ) ; Condition to find menu items Local $pCondition $oUIAutomation.CreatePropertyCondition( $UIA_ControlTypePropertyId, $UIA_MenuItemControlTypeId, $pCondition ) ; Find menu items Local $pUIElementArray, $oUIElementArray, $iElements $oMenu.FindAll( $TreeScope_Children, $pCondition, $pUIElementArray ) $oUIElementArray = ObjCreateInterface( $pUIElementArray, $sIID_IUIAutomationElementArray, $dtagIUIAutomationElementArray ) $oUIElementArray.Length( $iElements ) ; Print menu item names Local $pItem, $oItem For $i = 0 To $iElements - 1 $oUIElementArray.GetElement( $i, $pItem ) $oItem = ObjCreateInterface( $pItem, $sIID_IUIAutomationElement, $dtagIUIAutomationElement ) $oItem.GetCurrentPropertyValue( $UIA_NamePropertyId, $sName ) ConsoleWrite( " " & $sName & @CRLF ) Next ConsoleWrite( @CRLF ) Return $S_OK EndFunc Func oIUIAutomationEventHandler_QueryInterface( $pSelf, $pRIID, $pObj ) ; Ret: long Par: ptr;ptr* Local $sIID = StringFromGUID( $pRIID ) If $sIID = $sIID_IUnknown Then ;ConsoleWrite( "oIUIAutomationEventHandler_QueryInterface: IUnknown" & @CRLF ) DllStructSetData( DllStructCreate( "ptr", $pObj ), 1, $pSelf ) oIUIAutomationEventHandler_AddRef( $pSelf ) Return $S_OK ElseIf $sIID = $sIID_IUIAutomationEventHandler Then ;ConsoleWrite( "oIUIAutomationEventHandler_QueryInterface: IUIAutomationEventHandler" & @CRLF ) DllStructSetData( DllStructCreate( "ptr", $pObj ), 1, $pSelf ) oIUIAutomationEventHandler_AddRef( $pSelf ) Return $S_OK Else ;ConsoleWrite( "oIUIAutomationEventHandler_QueryInterface: " & $sIID & @CRLF ) Return $E_NOINTERFACE EndIf EndFunc Func oIUIAutomationEventHandler_AddRef( $pSelf ) ; Ret: ulong ;ConsoleWrite( "oIUIAutomationEventHandler_AddRef" & @CRLF ) Return 1 EndFunc Func oIUIAutomationEventHandler_Release( $pSelf ) ; Ret: ulong ;ConsoleWrite( "oIUIAutomationEventHandler_Release" & @CRLF ) Return 1 EndFunc Func StringFromGUID( $pGUID ) Local $aResult = DllCall( "ole32.dll", "int", "StringFromGUID2", "struct*", $pGUID, "wstr", "", "int", 40 ) If @error Then Return SetError( @error, @extended, "" ) Return SetExtended( $aResult[0], $aResult[2] ) EndFunc ; Copied and slightly modified from this post by trancexx: ; http://www.autoitscript.com/forum/index.php?showtopic=153520&view=findpost&p=1143566 Func ObjectFromTag($sFunctionPrefix, $tagInterface, ByRef $tInterface, $fPrint = False, $bIsUnknown = Default, $sIID = "{00000000-0000-0000-C000-000000000046}") ; last param is IID_IUnknown by default If $bIsUnknown = Default Then $bIsUnknown = True Local $sInterface = $tagInterface ; copy interface description Local $tagIUnknown = "QueryInterface hresult(ptr;ptr*);" & _ "AddRef dword();" & _ "Release dword();" ; Adding IUnknown methods If $bIsUnknown Then $tagInterface = $tagIUnknown & $tagInterface ; Below line is really simple even though it looks super complex. It's just written weird to fit in one line, not to steal your attention ;Local $aMethods = StringSplit(StringReplace(StringReplace(StringReplace(StringReplace(StringTrimRight(StringReplace(StringRegExpReplace(StringRegExpReplace($tagInterface, "\w+\*", "ptr"), "\h*(\w+)\h*(\w+\*?)\h*(\((.*?)\))\h*(;|;*\z)", "$1\|$2;$4" & @LF), ";" & @LF, @LF), 1), "object", "idispatch"), "hresult", "long"), "bstr", "ptr"), "variant", "ptr"), @LF, 3) Local $aMethods = StringSplit(StringReplace(StringReplace(StringReplace(StringTrimRight(StringReplace(StringRegExpReplace(StringRegExpReplace($tagInterface, "\w+\*", "ptr"), "\h*(\w+)\h*(\w+\*?)\h*(\((.*?)\))\h*(;|;*\z)", "$1\|$2;$4" & @LF), ";" & @LF, @LF), 1), "hresult", "long"), "bstr", "ptr"), "variant", "ptr"), @LF, 3) Local $iUbound = UBound($aMethods) Local $sMethod, $aSplit, $sNamePart, $aTagPart, $sTagPart, $sRet, $sParams, $hCallback ; Allocation $tInterface = DllStructCreate("int RefCount;int Size;ptr Object;ptr Methods[" & $iUbound & "];int_ptr Callbacks[" & $iUbound & "];ulong_ptr Slots[16]") ; 16 pointer sized elements more to create space for possible private props If @error Then Return SetError(1, 0, 0) For $i = 0 To $iUbound - 1 $aSplit = StringSplit($aMethods[$i], "|", 2) If UBound($aSplit) <> 2 Then ReDim $aSplit[2] $sNamePart = $aSplit[0] $sTagPart = $aSplit[1] $sMethod = $sFunctionPrefix & $sNamePart If $fPrint Then Local $iPar = StringInStr( $sTagPart, ";", 2 ), $t If $iPar Then $t = "Ret: " & StringLeft( $sTagPart, $iPar - 1 ) & " " & _ "Par: " & StringRight( $sTagPart, StringLen( $sTagPart ) - $iPar ) Else $t = "Ret: " & $sTagPart EndIf Local $s = "Func " & $sMethod & _ "( $pSelf ) ; " & $t & @CRLF & _ "EndFunc" & @CRLF ConsoleWrite( $s ) EndIf $aTagPart = StringSplit($sTagPart, ";", 2) $sRet = $aTagPart[0] $sParams = StringReplace($sTagPart, $sRet, "", 1) $sParams = "ptr" & $sParams $hCallback = DllCallbackRegister($sMethod, $sRet, $sParams) ;ConsoleWrite(@error & @CRLF & @CRLF) DllStructSetData($tInterface, "Methods", DllCallbackGetPtr($hCallback), $i + 1) ; save callback pointer DllStructSetData($tInterface, "Callbacks", $hCallback, $i + 1) ; save callback handle Next DllStructSetData($tInterface, "RefCount", 1) ; initial ref count is 1 DllStructSetData($tInterface, "Size", $iUbound) ; number of interface methods DllStructSetData($tInterface, "Object", DllStructGetPtr($tInterface, "Methods")) ; Interface method pointers Return ObjCreateInterface(DllStructGetPtr($tInterface, "Object"), $sIID, $sInterface, $bIsUnknown) ; pointer that's wrapped into object EndFunc Func DeleteObjectFromTag(ByRef $tInterface) For $i = 1 To DllStructGetData($tInterface, "Size") DllCallbackFree(DllStructGetData($tInterface, "Callbacks", $i)) Next $tInterface = 0 EndFuncYou need CUIAutomation2.au3.This is output from the context menu in Scite console, the Edit menu and the Paragraph submenu.expandcollapse popupContext: Undo Redo Cut Copy Paste Delete Select All Hide Edit: Undo Ctrl+Z Redo Ctrl+Y Cut Ctrl+X Copy Ctrl+C Paste Ctrl+V Duplicate Ctrl+D Delete Del Select All Ctrl+A Copy as RTF Match Brace Ctrl+E Select to Brace Ctrl+Shift+E Show Calltip Ctrl+Shift+Space Complete Symbol Ctrl+I Complete Word Ctrl+Enter Expand Abbreviation Ctrl+B Insert Abbreviation Ctrl+Shift+R Block Comment or Uncomment Ctrl+Q Box Comment Ctrl+Shift+B Stream Comment Ctrl+Shift+Q Make Selection Uppercase Ctrl+Shift+U Make Selection Lowercase Ctrl+U Paragraph Paragraph: Join Split 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...
jdelaney Posted April 25, 2014 Share Posted April 25, 2014 (edited) Another example of getting the scite menu items: #include <GuiMenu.au3> $hWin = WinGetHandle("[CLASS:SciTEWindow]") $hPrimaryMenu = _GUICtrlMenu_GetMenu($hWin) For $i = 0 To _GUICtrlMenu_GetItemCount($hPrimaryMenu) - 1 ConsoleWrite(_GUICtrlMenu_GetItemText($hPrimaryMenu,$i) & @CRLF) $hSub1 = _GUICtrlMenu_GetItemSubMenu($hPrimaryMenu,$i) For $j = 0 To _GUICtrlMenu_GetItemCount($hSub1) - 1 ConsoleWrite(@TAB & _GUICtrlMenu_GetItemText($hSub1,$j) & @CRLF) Next Next Example of recurssion to get all child menus: #include <GuiMenu.au3> $hWin = WinGetHandle("[CLASS:SciTEWindow]") $hPrimaryMenu = _GUICtrlMenu_GetMenu($hWin) Global $iTabs = "" GetMenuItems($hPrimaryMenu) Func GetMenuItems($hParentMenu) For $i = 0 To _GUICtrlMenu_GetItemCount($hParentMenu) - 1 ConsoleWrite($iTabs & _GUICtrlMenu_GetItemText($hParentMenu,$i) & @CRLF) $iPriorTabs = $iTabs $iTabs &= @TAB $hChild = _GUICtrlMenu_GetItemSubMenu($hParentMenu,$i) If $hChild Then GetMenuItems($hChild) EndIf $iTabs = $iPriorTabs Next EndFunc Edited April 25, 2014 by jdelaney IEbyXPATH-Grab IE DOM objects by XPATH IEscriptRecord-Makings of an IE script recorder ExcelFromXML-Create Excel docs without excel installed GetAllWindowControls-Output all control data on a given window. Link to comment Share on other sites More sharing options...
LarsJ Posted April 25, 2014 Share Posted April 25, 2014 jdelaney, How do you get the context menu in this way? 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...
jdelaney Posted April 25, 2014 Share Posted April 25, 2014 misread as menu, not context. IEbyXPATH-Grab IE DOM objects by XPATH IEscriptRecord-Makings of an IE script recorder ExcelFromXML-Create Excel docs without excel installed GetAllWindowControls-Output all control data on a given window. Link to comment Share on other sites More sharing options...
LarsJ Posted April 26, 2014 Share Posted April 26, 2014 You can still use the old code:expandcollapse popup#include <GuiMenu.au3> #include <SendMessage.au3> #include <WindowsConstants.au3> Opt( "MustDeclareVars", 1 ) MainFunc() Func MainFunc() Local Static $hLastMenu = 0 HotKeySet( "{ESC}", "Quit" ) While Sleep( 100 ) Local $hDesktop = _WinAPI_GetDesktopWindow(), $hMenu = 0 Local $hChild = _WinAPI_GetWindow( $hDesktop, $GW_CHILD ), $i = 0 While $hChild And $i < 3 If _WinAPI_GetClassName( $hChild ) = "#32768" Then $hMenu = _SendMessage( $hChild, $MN_GETHMENU ) ExitLoop EndIf $hChild = _WinAPI_GetWindow( $hChild, $GW_HWNDNEXT ) $i += 1 WEnd If $hMenu And $hLastMenu <> $hMenu Then Local $iCount = _GUICtrlMenu_GetItemCount( $hMenu ), $sText ConsoleWrite( "Menu:" & @CRLF ) For $i = 0 To $iCount - 1 $sText = _GUICtrlMenu_GetItemText( $hMenu, $i ) If $sText <> "" Then ConsoleWrite( " " & $sText & @CRLF ) Next ConsoleWrite( @CRLF ) $hLastMenu = $hMenu EndIf WEnd EndFunc Func Quit() Exit EndFunc 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...
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