sohfeyr Posted August 11, 2006 Posted August 11, 2006 I need to get a list of items in a pop-up (context) menu. I have Larry's code fromhttp://www.autoitscript.com/forum/index.php?showtopic=6577and it works fantastic with regular menus and system menus, but I can't get anything from context menus. I'm very new with the Win32 API; could someone help me figure this out?Here is my code so far: CODE#region ConstGlobal Const $MF_BITMAP = 0x00000004 ;... Uses a bitmap as the menu itemGlobal Const $MF_BYCOMMAND = 0x00000000Global Const $MF_BYPOSITION = 0x00000400Global Const $MF_CHECKED = 0x00000008 ;..... A check mark is placed next to the item (for drop-down menus, submenus, and shortcut menus only).Global Const $MF_DISABLED = 0x00000002 ;.... The item is disabled.Global Const $MF_ENABLED = 0x00000000Global Const $MF_GRAYED = 0x00000001 ;...... The item is disabled and grayed.Global Const $MF_HILITE = 0x00000080 ;...... The item is highlighted.Global Const $MF_MENUBARBREAK = 0x00000020 ; This is the same as the MF_MENUBREAK flag, except for drop-down menus, submenus, and; shortcut menus, where the new column is separated from the old column by a vertical line.Global Const $MF_MENUBREAK = 0x00000040;... The item is placed on a new line (for menu bars) or in a new column; (for drop-down menus, submenus, and shortcut menus) without separating columns.Global Const $MF_OWNERDRAW = 0x00000100;... The item is owner-drawn.Global Const $MF_POPUP = 0x00000010 ;... Menu item is a submenu.Global Const $MF_SEPARATOR = 0x00000800;... There is a horizontal dividing line (for drop-down menus, submenus, and shortcut menus only).Global Const $MF_STRING = 0x00000000 ;... Specifies that the menu item is a text stringGlobal Const $MF_UNCHECKED = 0x00000000;... Does not place a check mark next to the item (default).#endregion#include "Array.au3"; This works - gets menu hierarchy of the active window.ListMenuItems(WinGetHandle("")); This doesn't work;For $J = 1 to 5 CWL("---------------------------------------------") $S = @SEC + 5 If $S > 60 Then $S = $S - 60 While @SEC <> $S Sleep(1000) WEnd $hw = ___GETCLICKMENUHWND("right") ;ListMenuItems($hw,-1,3,0);NextAutoItSetOption("WinSearchChildren",1); **** THIS IS BROKEN ***;~ ------------------------------------------------------------------;~ Function: ___GETCLICKMENUHWND;~ Description: Returns handle of a popup menu;~ Input: $button as string;~ Output: Returns the handle of a popup menu;~ Dependencies: ___WINLISTSNAPSHOT, ___GETMENUITEM;~ ------------------------------------------------------------------Func ___GETCLICKMENUHWND($button) ; As HWnd $hwnd1=___WINLISTSNAPSHOT() ; Perform action Select Case $button="left" or $button="middle" or $button="right" MouseClick($button) ;Case StringRight($button, 1)="s" and IsNumber(StringLeft($button, StringLen($button)-1)) ; Sleep(Number(StringLeft($button, StringLen($button)-1))) Case Else Send($button) EndSelect $hwnd2=___WINLISTSNAPSHOT() Send("{DOWN}") $WM_CONTEXTMENU = 0x007B $WM_ENTERMENULOOP = 0x0211 $WM_EXITMENULOOP = 0x0212 $WM_MENUCHAR = 0x0120 $WM_MENUCOMMAND = 0x0126 $WM_MENUDRAG = 0x0123 $WM_MENUGETOBJECT = 0x0124 $WM_MENURBUTTONUP = 0x0122 $WM_MENUSELECT = 0x011F $WM_NEXTMENU = 0x0213 $WM_UNINITMENUPOPUP = 0x0125 $WM_INITMENU = 0x0116 $WM_INITMENUPOPUP = 0x0117 ;Compare the two window lists For $h In $hwnd2 ;If IsMenu($h)=1 then If _ArraySearch($hwnd1,$h) = -1 Then ;cwl("Loc: " & $L) ;cwl($h & @tab & "Items: " & GetMenuItemCount($h)) $j=ControlGetFocus($h) $k=ControlGetHandle($h, "", $j) $L=ControlGetPos($h, "", $j) $p=WinGetPos($k) ___TEST("k", $k) $smk = ___GetMenuItem($k, 0, 0) ;___TEST("smk", ___GetMenuItem($k, $k, 0)) ;___TEST("smk", ___GetMenuItem($k, 1, 0)) ;___TEST("smk", ___GetMenuItem($k, $k, 1)) ;___TEST("smk", ___GetMenuItem($k, 1, 1)) ___TEST("GSM", GetSubMenu($k, 1)) ___TEST("GMS", GetMenuString($k, 1)) $mk = GetMenu($k) ___TEST("mk", $mk) ___TEST("MIID", ____MIID($k, 1)) ___TEST("GMS2", ____GMS2($k,1)) ___TEST("MIC",GetMenuItemCount($k)) ;$b=___GETMENUITEM($k,$k,0) ;___TEST("b", $ cwl("pos: " & _ArrayToString($p,",")) cwl("pos: " & _ArrayToString($L,",")) ;cwl("itm: " & _ArrayToString($b,",")) cwl($j & @TAB & $k & @TAB & _ArrayToString($L,",")) ListMenuItems($k) ;cwl($k & @tab & "Main: " & GetMenu($k) & @tab & "Sub: " & GetSubMenu($k,0) & @tab & "ID: " & GetMenuItemID($k,1) & @tab & "Items: " & $k) return $h EndIf ;EndIf NextEndFunc;Private Declare Function TrackPopupMenu Lib "user32" (ByVal hMenu As Long, ByVal wFlags As Long, ByVal x As Long, ByVal y As Long, ByVal nReserved As Long, ByVal hwnd As Long, ByVal lprc As Any) As Long;Private Declare Function TrackPopupMenuEx Lib "user32" (ByVal hMenu As Long, ByVal wFlags As Long, ByVal x As Long, ByVal y As Long, ByVal HWnd As Long, ByVal lptpm As Any) As Long;DllCall("user32","int","TrackPopUpMenuEx","int",$hMenu,"int",$wFlags,"int",$x,"int",$y,"int",$HWnd,Func ___TEST($txt, $var) ConsoleWrite("Testing: " & $txt & @TAB) If IsArray($var) then ConsoleWrite("Array " & _ArrayToString($var, ",") & @cr) If IsObj($var) then ConsoleWrite("Obj" & @TAB) If IsNumber($var) then ConsoleWrite("Number" & @TAB) If IsString($var) then ConsoleWrite("String" & @TAB) If IsHWnd($var) then ConsoleWrite("HWnd" & @TAB) If IsWindow($var) then ConsoleWrite("Window" & @TAB) If IsMenu($var) then ConsoleWrite("Menu" & @TAB) cwl($var)EndFunc;~ ------------------------------------------------------------------;~ Function: ListMenuItems;~ Description: Outputs hierarchy of MenuItems starting with $hMenu (which can be a window!);~ Input: None;~ Output: None... For now...;~ Dependencies: IsWindow, GetMenu, GetSubMenu, ___GETMENUITEM;~ ------------------------------------------------------------------Func ListMenuItems($hMenu, $pos=-1, $recurse=3, $nest=0) If $recurse > 0 and $nest=0 Then $nest=$recurse If IsWindow($hMenu) Then $hMenu=GetMenu($hMenu) If $pos>-1 Then $hSubMenu = GetSubMenu($hMenu, $pos) Else $hSubMenu=$hMenu EndIf $num=GetMenuItemCount($hSubMenu) For $i=0 to $num-1 $x = ___GETMENUITEM($hMenu, $hSubMenu, $i) For $g = $recurse to $nest ConsoleWrite(@tab) Next For $w = 0 to UBound($x)-1 Consolewrite($x[$w] & @tab) Next ConsoleWrite(@cr) If $recurse > 0 then If $x[6] = 1 then ListMenuItems($x[0], -1, $recurse - 1, $nest) EndIf Next EndFunc;~ ------------------------------------------------------------------;~ Function: ___WINLISTSNAPSHOT;~ Description: Returns an array of all current window handles;~ Input: None;~ Output: Returns an array of all current window handles;~ Dependencies: None;~ ------------------------------------------------------------------Func ___WINLISTSNAPSHOT() ; As Array(Of HWnd) $winArray = WinList() $hwnds = _ArrayCreate("") For $i = 1 To $winArray[0][0] _ArrayAdd($hwnds, $winArray[$i][1]) Next Return $hwndsEndFunc;===============================================================================; Function Name: ___FLAGTEST; Description: Boolean test for presence of a particular BitFlag;===============================================================================Func ___FLAGTEST($Check, $For) ; As Boolean If BitAND($Check, $For) = $For Then Return 1 Else Return 0 EndIfEndFunc;===============================================================================; Function Name: CWL; Description: ConsoleWriteLine; Short wrapper for ConsoleWrite($data & @CR) ; Parameters: $data = output;===============================================================================Func CWL($data) ConsoleWrite($data & @cr)EndFunc;===============================================================================; Function Name: ___GETMENUITEM; Description: ; Parameters: ; Author(s): ; Returns: An array: [0] ID [1] text [2] NormalText [3] TextWithAccl ;~ [4] Accelerator [5] Hotkey [6] IsPopup [7] IsChecked ;~ [8] IsDisabled [9] IsHilited [10] IsGrayed [11] IsSeperator ;~ [12] IsMenuBreak [13] IsMenuBarBreak;===============================================================================Func ___GETMENUITEM($hMenu, $hSubMenu, $pos) Local $id = GetMenuItemID($hSubMenu, $pos) Local $MS = GetMenuState($hSubMenu, $id) Local $IsChecked =___FLAGTEST($MS, $MF_CHECKED) Local $IsDisabled =___FLAGTEST($MS, $MF_DISABLED) Local $IsHilited =___FLAGTEST($MS, $MF_HILITE) Local $IsPopup =___FLAGTEST($MS, $MF_POPUP) Local $IsGrayed =___FLAGTEST($MS, $MF_GRAYED) Local $IsSeperator =___FLAGTEST($MS, $MF_SEPARATOR) Local $IsMenuBreak =___FLAGTEST($MS, $MF_MENUBREAK) Local $IsMenuBarBreak =___FLAGTEST($MS, $MF_MENUBARBREAK) Local $text="" If $IsPopUp Then $id=GetSubMenu($hSubMenu, $pos) $text=GetMenuString($hSubMenu, $id) ;$text=GetSubMenuTitle($id, $pos) If IsArray($text) Then ;ConsoleWrite($id & @tab & $text[0] & @tab & $text[1] & @tab & $text[2] & _ ;@tab & $text[3] & @tab & $text[4] & @tab & $text[5] & @cr) $text=$text[3] EndIf Else $text=GetMenuString($hMenu, $id) If IsArray($text) Then $text=$text[3] EndIf Local $NormalText=$text $AcclText=$text $Hotkey="" $Accelerator="" $tmp=StringInStr($text, "&") If $tmp>0 then $Accelerator=StringMid($text,$tmp+1,1) EndIf $tmp=StringInStr($text, @tab) If $tmp>0 then $Hotkey=StringMid($text,$tmp+1) $AcclText = StringMid($text,1,$tmp-1) EndIf $NormalText=StringReplace($AcclText,"&","") $ary= _ArrayCreate($id, $text, $NormalText, $AcclText, $Accelerator, $Hotkey, $IsPopup, _ $IsChecked, $IsDisabled, $IsHilited, $IsGrayed, $IsSeperator, $IsMenuBreak, $IsMenuBarBreak) ;cwl(_ArrayToString($ary, @tab)) Return $aryEndFunc #region Wrapped API;===============================================================================; Function Name: AppendMenuItem; Description: ; Parameters: ; Author(s): ; Returns: ;===============================================================================Func AppendMenuItem($DMI_hwnd, $DMI_flags=0x00000000,$DMI_string="New Item") $DMI_r = DllCall("user32.dll","int","AppendMenu","hwnd",$DMI_hwnd,"int",$DMI_flags,"int",0,"str",$DMI_string)EndFunc;===============================================================================; Function Name: DeleteMenuItem; Description: ; Parameters: ; Author(s): ; Returns: ;===============================================================================Func DeleteMenuItem($DMI_hWnd, $DMI_id, $DMI_opt) If $DMI_hWnd <= 0 Or $DMI_id < 0 Then Return -1 If $DMI_opt = 0 Then $DMI_opt = $MF_BYCOMMAND Else $DMI_opt = $MF_BYPOSITION EndIf $DMI_r = DllCall("user32.dll", "hwnd", "DeleteMenu", _ "hwnd", $DMI_hWnd, _ "int", $DMI_id, _ "int", $DMI_opt) If @error Then Return 0 Return $DMI_r[0]EndFunc ;==>DeleteMenuItem;===============================================================================; Function Name: GetMenuState; Description: ; Parameters: ; Author(s): ; Returns: ;===============================================================================Func GetMenuState($GMSte_hWnd, $GMSte_id, $GMSte_Opt = 0) If $GMSte_hWnd <= 0 Or $GMSte_id < 0 Then Return -1 If $GMSte_Opt = 0 Then $GMSte_Opt = $MF_BYCOMMAND Else $GMSte_Opt = $MF_BYPOSITION EndIf $GMSte_r = DllCall("user32.dll", "int", "GetMenuState", _ "hwnd", $GMSte_hWnd, _ "int", $GMSte_id, _ "int", $GMSte_Opt) If @error Then Return -1 Return $GMSte_r[0]EndFunc ;==>GetMenuStateFunc ____GMS2($GMSte_hWnd, $GMSte_id, $GMSte_Opt = 0) If $GMSte_Opt = 0 Then $GMSte_Opt = $MF_BYCOMMAND Else $GMSte_Opt = $MF_BYPOSITION EndIf $GMSte_r = DllCall("user32.dll", "int", "GetMenuState", _ "hwnd", $GMSte_hWnd, _ "int", $GMSte_id, _ "int", $GMSte_Opt) Return $GMSte_rEndFunc;===============================================================================; Function Name: GetMenuString; Description: The GetMenuItemCount function determines the number of items in the specified menu.; Parameters: $GMS_hWnd handle the menu or submenu; $GMS_id Menu item to query ; Syntax: GetMenuString($GMIC_hWnd); Author(s): ; Returns: Array: TextLength (return value), ParentHandle, MenuID, Text, TextLength+1, 0;===============================================================================Func GetMenuString($GMS_hWnd, $GMS_id, $GMS_Opt = 0) If $GMS_hWnd <= 0 Or $GMS_id < 0 Then Return 0 If $GMS_Opt = 0 Then $GMS_Opt = $MF_BYCOMMAND Else $GMS_Opt = $MF_BYPOSITION EndIf $GMS_r = DllCall("user32.dll", "int", "GetMenuString", "hwnd", $GMS_hWnd, "int", $GMS_id, "str", "", "int", 0, "int", $GMS_Opt) If @error Then Return 0 ;ConsoleWrite("GetMenuString[0] : [0]=" & $GMS_r[0] & " [1]=" & $GMS_r[1] & " [2]=" & $GMS_r[2] & " [3]=" & $GMS_r[3] & " [4]=" & $GMS_r[4] & " [5]=" & $GMS_r[5] & @cr) $GMS_r = DllCall("user32.dll", "int", "GetMenuString", "hwnd", $GMS_hWnd, "int", $GMS_id, "str", "", "int", $GMS_r[0] + 1, "int", $GMS_Opt) If @error Then Return 0 ;ConsoleWrite("GetMenuString[1] : [0]=" & $GMS_r[0] & " [1]=" & $GMS_r[1] & " [2]=" & $GMS_r[2] & " [3]=" & $GMS_r[3] & " [4]=" & $GMS_r[4] & " [5]=" & $GMS_r[5] & @cr) Return $GMS_rEndFunc ;==>GetMenuString;~ ------------------------------------------------------------------;~ Function: GetMenu;~ Description: Returns the handle of the main menubar of the given window;~ Input: $GM_hWnd Window handle;~ Output: Returns the handle of the main menubar of the given window;~ Dependencies: ;~ ------------------------------------------------------------------Func GetMenu($GM_hWnd) If $GM_hWnd <= 0 Then Return 0 $GM_r = DllCall("user32.dll", "hwnd", "GetMenu", _ "hwnd", $GM_hWnd) If Not @error Then Return $GM_r[0] Return 0EndFunc ;==>GetMenu;~ ------------------------------------------------------------------;~ Function: GetMenuItemID;~ Description: Returns the MIID of the menuitem at the given position;~ Input: $GM_hWnd Window handle;~ Output: Returns the MIID of the menuitem at the given position. -1 if MIID is null or if the specified item opens a submenu;~ Dependencies: ;~ ------------------------------------------------------------------Func GetMenuItemID($GMI_hWnd, $GMI_pos) If $GMI_hWnd <= 0 Or $GMI_pos < 0 Then Return -3 $GMI_r = DllCall("user32.dll", "int", "GetMenuItemID", "hwnd", $GMI_hWnd, "int", $GMI_pos) If Not @error Then Return $GMI_r[0] Return -2EndFunc ;==>GetMenuItemIDFunc ____MIID($GMI_hWnd, $GMI_pos) Return DllCall("user32.dll", "int", "GetMenuItemID", "hwnd", $GMI_hWnd, "int", $GMI_pos)EndFunc;===============================================================================; Function Name: GetMenuItemCount; Description: The GetMenuItemCount function determines the number of items in the specified menu.; Parameters: $GMIC_hWnd handle the menu or submenu; Syntax: GetMenuItemCount($GMIC_hWnd); Author(s): ; Returns: -1 on error or the menu item count on success;===============================================================================Func GetMenuItemCount($GMIC_hWnd) If $GMIC_hWnd <= 0 Then Return -1 $GMIC_r = DllCall("user32.dll","int","GetMenuItemCount","hwnd",$GMIC_hWnd) If @error Then Return -1 Return $GMIC_r[0]EndFunc;===============================================================================; Function Name: GetSubMenu; Description: Retrieves the handle of the submenu coming form the given position; Parameters: $GMIC_hWnd handle the parent menu or submenu; Author(s): ; Returns: 0 on error or the menu handle on success;===============================================================================Func GetSubMenu($GSM_hWnd, $GSM_pos) If $GSM_hWnd <= 0 Or $GSM_pos < 0 Then Return 0 $GSM_r = DllCall("user32.dll", "hwnd", "GetSubMenu", "hwnd", $GSM_hWnd, "int", $GSM_pos) If Not @error Then Return $GSM_r[0] Return 0EndFunc ;==>GetSubMenuFunc ____GSM($GSM_hWnd, $GSM_pos) Return DllCall("user32.dll", "hwnd", "GetSubMenu", "hwnd", $GSM_hWnd, "int", $GSM_pos)EndFunc ;===============================================================================; Function Name: GetSubMenuTitle; Description: Retrieves the title of the submenu coming from the given position; Parameters: $hMenu handle the parent menu or submenu; $pos positon to interrogete; Author(s): ; Returns: 0 on error or the menu handle on success;===============================================================================Func GetSubMenuTitle($hMenu, $pos) ; As String $text = DllCall("user32.dll", "int", "GetMenuString", _ "hwnd", $hMenu, "int", $pos, "str", "", "int", 24, "int", $MF_BYPOSITION) Return $textEndFunc ;===============================================================================; Function Name: GetSystemMenu; Description: Retrieves the system menu for the given window; Parameters: $GSM_hWnd Window handle; $GSM_bRevert ?; Author(s): ; Returns: 0 on error or the menu handle on success;===============================================================================Func GetSystemMenu($GSM_hWnd, $GSM_bRevert = 0) If $GSM_hWnd <= 0 Then Return -1 If $GSM_bRevert <> 0 Then $GSM_bRevert = 1 $GSM_r = DllCall("user32.dll", "hwnd", "GetSystemMenu", _ "hwnd", $GSM_hWnd, _ "int", $GSM_bRevert) If @error Then Return -1 Return $GSM_r[0]EndFunc ;==>GetSystemMenu;===============================================================================; Function Name: IsMenu; Description: Is the given HWND a menu handle?; Parameters: $hWnd handle to test; Author(s): JW; Returns: 0 or 1 if it is a menu handle;===============================================================================Func IsMenu($hWnd) $hWnd=HWnd($hWnd) If $hWnd <= 0 Then Return 0 $GM_r = DllCall("user32.dll", "int", "IsMenu", "hwnd", $hWnd) If @error Then Return 0 If $GM_r=0 then Return 0 Return 1EndFunc;===============================================================================; Function Name: IsWindow; Description: Is the given HWND a window handle?; Parameters: $hWnd handle to test; Author(s): JW; Returns: 0 or 1 if it is a window handle;===============================================================================Func IsWindow($hWnd) $hWnd=HWnd($hWnd) If $hWnd <= 0 Then Return 0 $GM_r = DllCall("user32.dll", "int", "IsWindow", "hwnd", $hWnd) If @error Then Return -4 If $GM_r=0 then Return 0 Return 1EndFunc#endregion Mine:Time Functions - Manipulate the system clock! | WinControlList (WinGetClassList++) | .Net Setup Wrapper, Detect or install .Net | Writing and using a VB .NET COM object in AutoItNot mine, but highly recommended:AutoItTreeViewExtension plugin | Menu code | Callback helper dll | Auto3Lib - Control the uncontrollable | Creating COM objects in AutoIt | Using .Net framework classes in AutoIt
sohfeyr Posted August 28, 2006 Author Posted August 28, 2006 I need to get a list of items in a pop-up (context) menu. I have Larry's code fromhttp://www.autoitscript.com/forum/index.php?showtopic=6577and it works fantastic with regular menus and system menus, but I can't get anything from context menus. I'm very new with the Win32 API; could someone help me figure this out?Nobody else has had this problem and figured out a way? It's such a common UI thing...I can already detect new windows, and I can get a handle to the window hosting the context menu - maybe even the menu itself - I just can't get at the items!Anyone? Anyone? Mine:Time Functions - Manipulate the system clock! | WinControlList (WinGetClassList++) | .Net Setup Wrapper, Detect or install .Net | Writing and using a VB .NET COM object in AutoItNot mine, but highly recommended:AutoItTreeViewExtension plugin | Menu code | Callback helper dll | Auto3Lib - Control the uncontrollable | Creating COM objects in AutoIt | Using .Net framework classes in AutoIt
curtisk Posted November 29, 2007 Posted November 29, 2007 Nobody else has had this problem and figured out a way? It's such a common UI thing...I can already detect new windows, and I can get a handle to the window hosting the context menu - maybe even the menu itself - I just can't get at the items!Anyone? Anyone? I realize this post is over a year old, but I am trying to do the same thing and I can't find an answer anywhere on the forums despite having been trawling for two days. I know Auto3Lib can automate context menus but I can't figure out how to do this with the latest version of AutoIt. Can anyone point me in the right direction? I'm willing to beg. :-)
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