This script already works to display a menuitem's Name & Hotkey or menuitem ID.
However, I need help to combine the two code snippets to display
the menuitem ID, Name & Hotkey at the same time.
Dynamically update a tooltip while scrolling thru each menuitem in any menu type.
I call it a MenuSpy.
I've been experimenting with Micha's Sean's & Majkinetor's: Menu scripts.
Specifically the GetContextMenu.... & GetMenu ... Functions.
I've been trying to figure out how to display the contents of each menuitem in any type of menu:
standard, context, popup,tray & ahk menus ONLY in a tooltip as I mouseover each menuitem.
I finally got it working. It displays and updates the Menuitem Name & Hotkey dynamically
in a tooltip as user moves from menuitem to menuitem.
Thanks for the Great menu work! Micha, Sean & Majkinetor:
Use the latest version of Autohotkey 1.0.48.02
How to use this script.
1. Open an application with standards menus. Example: Notepad
2. Run this script
3. Select a Menu, try the EDIT menu.
4. Start moving slowly thru each menuitem
5. The Tooltip appears in the upper left corner of the desktop window
6. The tooltip dynamically updates with the menuitem's Name & Hotkey
As I mentioned above. This works on any type of menu;
standard, context, popup,tray & ahk menus
Even IExplorer, Explorer, and many others.
If it doesn't work on a menu. Then it's NOT a STANDARD Menu!!
You'll need to customize it to suit your needs.
Here's the code.
; MenuSpy
; Select Menuitem and it displays Tooltip for any Menu/Menuitem Dynamically
; This script uses part or all of these Menu Scripts
; Micha: GetContext Menu -
http://www.autohotkey.com/forum/topic21451.html
; Sean: GetInfo fromContext Menu -
http://www.autohotkey.com/forum/viewtop ... 37692.html
; Majkinetor: MMenu -
http://www.autohotkey.com/forum/topic17674.html
Code:
; MenuSpy
; It dynamically displays the menuitem Name & Hotkey in a tooltip as user moves thru each menuitem.
; To view the menuitem ID ONLY - Uncomment the code snippet below & comment out label code.
#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases.
SendMode Input ; Recommended for new scripts due to its superior speed and reliability.
#Persistent
SetTimer, Demo, 500
return
Demo:
;constants
MFS_HILITE = 0x80
WinGet hWnd, ID, A
hMenu := DllCall("GetMenu", "UInt", hWnd) ; VERIFIED for TextText
MouseGetPos, MouseScreenX, MouseScreenY, MouseWindowUID, MouseControlID
WinGet,ControlHwnd, ID,ahk_id %MouseWindowUID%
;Get count of menu items
ContextMenCnt := GetContextMenuCount(ControlHwnd)
if ContextMenCnt < 1
{
Tooltip,
}
;Read info for each menu item on Highlight
loop, %ContextMenCnt%
{
IsEnabled := GetContextMenuState(ControlHwnd, a_index-1)
; UnComment for use with MenuitemID
;SendMessage, 0x1E1, 0,0,, ahk_class #32768 ; VERIFIED for MenuitemID, FAILED BREAKS Menuitem Name & Hotkey portion
;hMenu := ErrorLevel
{
TextText =
if (IsEnabled & MFS_HILITE)
; menu item identifier of a menu item located at the specified position in a menu using the GetMenuItemID function
;MenuitemID := DllCall( "GetMenuItemID", UInt, hMenu, Int, a_index-1 ) ; VERIFIED - UnComment when used with hMenu in "hMenu=errorlevel" above
StrSize := GetContextMenuText(ControlHwnd, a_index-1)
TextText = %TextText%%a_index%:%StrSize%`n ; VERIFIED w/o MenuitemID - Comment Out for MenuitemID
;Msgbox TextText=%TextText% Index=%a_index% StrSize=%StrSize%`n
StringTrimLeft, TextText, TextText, 2
StringReplace, TextText, TextText, `:, , All
TextText = %TextText% ; VERIFIED w/o MenuitemID - Comment Out for MenuitemID
;TextText = %TextText%ID=%MenuitemID% ; ****VERIFIED when used with DllCall( "GetMenuItemID" ONLY ***
}
}
CoordMode, Tooltip, Screen
Tooltip, %TextText%, 0, 0
return
ESC::ExitApp
/***************************************************************
* Micha - Returns the TEXT of a menu entry (standard, context, popup & ahk menus only!!!)
***************************************************************
*/
GetContextMenuText(hWnd, Position)
{
WinGetClass, WindowClass, ahk_id %hWnd%
if WindowClass <> #32768 ; Get Class of Menu/Menuitem
{
return -1
}
SendMessage, 0x01E1, , , , ahk_id %hWnd%
;Errorlevel is set by SendMessage. It contains the handle to the menu
hMenu := errorlevel
;Allocate a struct for MenuItemInfo. It contains all the data of a Menu/Menuitem
VarSetCapacity(MenuItemInfo, 200, 0)
;Set Size of Struct [48] to the first member
InsertInteger(48, MenuItemInfo, 0, 4)
;Retrieve string MIIM_STRING = 0x40 = 64 (/ MIIM_TYPE = 0x10 = 16)
InsertInteger(64, MenuItemInfo, 4, 4)
;GetMenuItemInfo: Handle to Menu, Index of Position, 0=Menu identifier / 1=Index
InfoRes := DllCall("user32.dll\GetMenuItemInfo",UInt,hMenu, Uint, Position, uint, 1, "int", &MenuItemInfo)
if InfoRes = 0
return -1
InfoResError := errorlevel
LastErrorRes := DllCall("GetLastError")
if InfoResError <> 0
return -1
if LastErrorRes <> 0
return -1
;Get size of string from struct
GetMenuItemInfoRes := ExtractInteger(MenuItemInfo, 40, false, 4)
;If menu is empty return
If GetMenuItemInfoRes = 0
return "{Empty String}"
;+1 should be enough, we'll use 2
GetMenuItemInfoRes += 2
;Set capacity of string that will be filled by windows
VarSetCapacity(PopupText, GetMenuItemInfoRes, 0)
;Set Size plus 0 terminator + security ;-)
InsertInteger(GetMenuItemInfoRes, MenuItemInfo, 40, 4)
InsertInteger(&PopupText, MenuItemInfo, 36, 4)
InfoRes := DllCall("user32.dll\GetMenuItemInfo",UInt,hMenu, Uint, Position, uint, 1, "int", &MenuItemInfo)
if InfoRes = 0
return -1
InfoResError := errorlevel
LastErrorRes := DllCall("GetLastError")
if InfoResError <> 0
return -1
if LastErrorRes <> 0
return -1
return PopupText
}
/***************************************************************
* Micha - returns the count of menu items
***************************************************************
*/
GetContextMenuCount(hWnd)
{
WinGetClass, WindowClass, ahk_id %hWnd%
;All popups should have the window class #32768
if WindowClass <> #32768
{
return 0
}
;Retrieve menu handle from window
SendMessage, 0x01E1, , , , ahk_id %hWnd%
;Errorlevel is set by SendMessage. It contains the handle to the menu
hMenu := errorlevel
menuitemcount:=DllCall("GetMenuItemCount",UInt,hMenu)
Return, menuitemcount
}
/***************************************************************
* Micha - returns the state of a menu entry
***************************************************************
*/
GetContextMenuState(hWnd, Position)
{
WinGetClass, WindowClass, ahk_id %hWnd%
if WindowClass <> #32768
{
return -1
}
SendMessage, 0x01E1, , , , ahk_id %hWnd%
;Errorlevel is set by SendMessage. It contains the handle to the menu
hMenu := errorlevel
;We need to allocate a struct
VarSetCapacity(MenuItemInfo, 60, 0)
;Set Size of Struct to the first member
InsertInteger(48, MenuItemInfo, 0, 4)
;Get only Flags from dllcall GetMenuItemInfo MIIM_TYPE = 1
InsertInteger(1, MenuItemInfo, 4, 4)
;GetMenuItemInfo: Handle to Menu, Index of Position, 0=Menu identifier / 1=Index
InfoRes := DllCall("user32.dll\GetMenuItemInfo",UInt,hMenu, Uint, Position, uint, 1, "int", &MenuItemInfo)
InfoResError := errorlevel
LastErrorRes := DllCall("GetLastError")
if InfoResError <> 0
return -1
if LastErrorRes != 0
return -1
;Get Flag from struct
GetMenuItemInfoRes := ExtractInteger(MenuItemInfo, 12, false, 4)
return GetMenuItemInfoRes
}
GetMenu(hMenu)
{
Loop, % DllCall("GetMenuItemCount", "Uint", hMenu)
{
idx := A_Index - 1
idn := DllCall("GetMenuItemID", "Uint", hMenu, "int", idx)
nSize++ := DllCall("GetMenuString", "Uint", hMenu, "int", idx, "Uint", 0, "int", 0, "Uint", 0x400)
VarSetCapacity(sString, nSize)
DllCall("GetMenuString", "Uint", hMenu, "int", idx, "str", sString, "int", nSize, "Uint", 0x400) ;MF_BYPOSITION
If !sString
sString := "---------------------------------------"
sContents .= idx . " : " . idn . A_Tab . A_Tab . sString . "`n"
;msgbox idn=%idn%
If (idn = -1) && (hSubMenu := DllCall("GetSubMenu", "Uint", hMenu, "int", idx))
sContents .= GetMenu(hSubMenu)
}
Return sContents
}
;/*
; *********************************
; Original versions of ExtractInteger and InsertInteger provided by Chris
; - from the AutoHotkey help file - Version 1.0.37.04
; *********************************
; *********************************
ExtractInteger(ByRef pSource, pOffset = 0, pIsSigned = false, pSize = 4)
; pSource is a string (buffer) whose memory area contains a raw/binary integer at pOffset.
; The caller should pass true for pSigned to interpret the result as signed vs. unsigned.
; pSize is the size of PSource's integer in bytes (e.g. 4 bytes for a DWORD or Int).
; pSource must be ByRef to avoid corruption during the formal-to-actual copying process
; (since pSource might contain valid data beyond its first binary zero).
{
SourceAddress := &pSource + pOffset ; Get address and apply the caller's offset.
result := 0 ; Init prior to accumulation in the loop.
Loop %pSize% ; For each byte in the integer:
{
result := result | (*SourceAddress << 8 * (A_Index - 1)) ; Build the integer from its bytes.
SourceAddress += 1 ; Move on to the next byte.
}
if (!pIsSigned OR pSize > 4 OR result < 0x80000000)
return result ; Signed vs. unsigned doesn't matter in these cases.
; Otherwise, convert the value (now known to be 32-bit) to its signed counterpart:
return -(0xFFFFFFFF - result + 1)
}
InsertInteger(pInteger, ByRef pDest, pOffset = 0, pSize = 4)
; To preserve any existing contents in pDest, only pSize number of bytes starting at pOffset
; are altered in it. The caller must ensure that pDest has sufficient capacity.
{
mask := 0xFF ; This serves to isolate each byte, one by one.
Loop %pSize% ; Copy each byte in the integer into the structure as raw binary data.
{
DllCall("RtlFillMemory", UInt, &pDest + pOffset + A_Index - 1, UInt, 1 ; Write one byte.
, UChar, (pInteger & mask) >> 8 * (A_Index - 1)) ; This line is auto-merged with above at load-time.
mask := mask << 8 ; Set it up for isolation of the next byte.
}
}