Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

Controlling popup menues.


  • Please log in to reply
6 replies to this topic
mugz
  • Members
  • 7 posts
  • Last active: Mar 29 2007 09:48 AM
  • Joined: 14 Sep 2004
Please add an ability to control popup menues.

The basic methods is the following:
1. Find a window with class like #32768
2. Get HMENU by sending MN_GETHMENU to the window
3. Get menu item id (I think you can use existing code to the WinMenuSelectItem)
4. Send a WM_COMMAND to the appropriate window

This is test code for prove this alogoritm (except the last step)
	SendInput,{APPSKEY}
	Sleep, 10
	if WinExist("ahk_class #32768")
	{ 
		SendMessage,0x01E1
		hmenu := ErrorLevel
		if hmenu!=1
		{
			itemCount := DllCall("GetMenuItemCount", "Uint", hmenu, "Uint")
			mii_fMask := MIIM_ID
			itemID := API_GetMenuItemID(hmenu, itemCount-3)
			PostMessage, 0x111, itemID, 0,,

			;MENUITEMINFO_Set("mii")
			;API_GetMenuItemInfo(handle, 0, true, &mii)
			;MENUITEMINFO_Get("mii")

			MsgBox % "iid " . itemID
		}
		
	}

Can you add something like WinMenuSelectItem, but for popups or mke an optional argument for WinMenuSelectItem (HMENU)

Sean
  • Members
  • 2462 posts
  • Last active: Feb 07 2012 04:00 AM
  • Joined: 12 Feb 2007
The #32768 is the classname of the (standard) menu window, meaning that the menu itself should be created to obtain via this method the menu handle of the context menu (:when out of scope, the menu window is simply destroyed). So, not much sense to control the context menus this way, IMHO.

Anyway, thanks for MN_GETHMENU. The day before yesterday (what a surprising coincidence), I tried to obtain the command ID's of the items in the context menu, but, I failed from the start, i. e., to obtain the menu handle from its window handle. So, I was just about going into a research, and read this post. Now my problem is solved without an effort.

mugz
  • Members
  • 7 posts
  • Last active: Mar 29 2007 09:48 AM
  • Joined: 14 Sep 2004

So, not much sense to control the context menus this way, IMHO.


I end up by analysing menu structure and generate {DOWN}s and {ENTER}s

API_GetMenuItemID( hMenu, nPos ) {
	return DllCall("GetMenuItemID", "uint", hMenu, "int", nPos)
}
API_GetSubmenu( hMenu, nPos ) {
	return DllCall("GetSubMenu", "uint", hMenu, "int", nPos)
}
API_GetMenuItemsCount(hMenu)
{
	return DllCall("GetMenuItemCount", "Uint", hMenu, "Uint")
}
GetMenuString(hMenu, nPos)
{
	length := DllCall("GetMenuString"
			, "UInt", hMenu
			, "UInt", nPos
			, "UInt", 0	; NULL
			, "Int", 0	; Get length
			, "UInt", 0x0400)	; MF_BYPOSITION
		VarSetCapacity(lpString, length + 1)	; I don't check the result...
		length := DllCall("GetMenuString"
			, "UInt", hMenu
			, "UInt", nPos
			, "Str", lpString
			, "Int", length + 1
			, "UInt", 0x0400)
	return lpString
}
findMenuItem(hMenu, name) {
	RepeatCount := API_GetMenuItemsCount(hMenu)
	Loop %RepeatCount% {
    	nPos := A_Index - 1
    	if GetMenuString(hMenu, nPos)=name {
    		return nPos
    	}
	}
	return -1
}

runScript(name, name2="")
{
	SendInput,{APPSKEY}	
	Sleep, 100
	if WinExist("ahk_class #32768")
	{ 		
		;WinGet, activeWindow, ID
		;hWnd := activeWindow 
		;activeWindow := DllCall("GetWindow", "Uint", activeWindow, "Uint", 4, "Uint")
		SendMessage,0x01E1
		hmenu := ErrorLevel
		nPos := -1
		nPos2 := -1
		if hmenu!=1
		{
			itemCount := API_GetMenuItemsCount(hmenu)
			if(findMenuItem(hmenu, "Scripts")=(itemCount-1))
			{
				hmenu := API_GetSubMenu(hmenu, itemCount-1)
				nPos := findMenuItem(hmenu, name)
				;MsgBox % "m2 " . nPos
				if(nPos != -1 and name2 != "")
				{
					hMenu2 := API_GetSubMenu(hmenu, nPos)					
					nPos2 := findMenuItem(hmenu2, name2)
					;MsgBox % "m2 " . nPos2
				}

				if((nPos2 != -1) or (nPos != -1 and name2 = ""))
				{
					SendInput,{UP}{ENTER}
					Loop %nPos% 
					{
						SendInput,{DOWN}
					}
					SendInput,{ENTER}	

					if(name2 <> "")
					{
						Loop %nPos2% {
							SendInput,{DOWN}
						}
						SendInput,{ENTER}	
					}
				}
			}

		}
	}
}



mugz
  • Members
  • 7 posts
  • Last active: Mar 29 2007 09:48 AM
  • Joined: 14 Sep 2004
By the way, command IDs in my case are unusable - application does not understand them. I think it uses TrackPopupMenu return value

Sean
  • Members
  • 2462 posts
  • Last active: Feb 07 2012 04:00 AM
  • Joined: 12 Feb 2007

By the way, command IDs in my case are unusable - application does not understand them.

Context menu is literally context sensitive, so need more elaborate manipulations, than those of the usual menu bars. First, merely targeting the top level window isn't enough, and, also depends on which element got focused. Here is one example, it's only tested with IE7 (or IE4AHK.ahk) in XPSP2.

First load this forum in Internet Explorer, then make the LOGO of AutoHotkey get focus. The simplest way may be right-click on it and press Alt key to only dismiss the popped up menu (:the dotted rectagle should remain visible around it). Then, execute the following script. The IE7 need not be the foreground window if didn't alter anything about it. It brought up save picture dialog window in my case.

Sleep 1000
PostMessage, 0x111, 2270, 0, Internet Explorer_Server1, ahk_class IEFrame
Even this method does not always work. Then, may have to go deeper, like IOleCommandTarget, or ultimately to IContextMenu, probably.

Amy
  • Guests
  • Last active:
  • Joined: --
THANK YOU for MN_GETHMENU!!!!!
I've been looking for how to manipulate context menus for a long time, and finally found an answer!!

fragman
  • Members
  • 1591 posts
  • Last active: Nov 12 2012 08:51 PM
  • Joined: 13 Oct 2009
This topic is a bit older, but since I'm currently working on this I have to ask about something I noticed.

With the method posted above, I can succesfully execute menu entries and submenu entries. However, on explorer the "New" submenu doesn't work, while other submenus from explorer context menu work. I get no valid count, and can't list the entries.

Can anyone confirm this or know a better way?
I want to execute new->textfile.

Btw, for international support, MUI verbs should be used to get the proper entry name:
TranslateMUI(resDll, resID)
{
VarSetCapacity(buf, 256) 
hDll := DllCall("LoadLibrary", "str", resDll) 
Result := DllCall("LoadString", "uint", hDll, "uint", resID, "str", buf, "int", 128)
return buf
}

MUI verbs can be found in registry or directly in dll/exe files with a tool such as resource hacker.[/code]