AutoHotkey Community

It is currently May 26th, 2012, 10:38 pm

All times are UTC [ DST ]




Post new topic Reply to topic  [ 6 posts ] 
Author Message
PostPosted: September 12th, 2009, 4:55 am 
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. :D

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.
   }
}


Report this post
Top
  
Reply with quote  
 Post subject:
PostPosted: September 12th, 2009, 11:47 am 
Offline

Joined: June 18th, 2008, 8:36 am
Posts: 4923
Location: AHK Forum
Like this :?:
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),index:=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 ? index " : " . 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.
   }
}

_________________
AHK_H (2alpha) AHF TT _Struct WatchDir Yaml _Input ObjTree RapidHotkey DynaRun :wink:


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: September 12th, 2009, 6:05 pm 
Offline

Joined: August 22nd, 2009, 11:23 pm
Posts: 294
Thanks for the quick response HotkeyIt!

Although your syntax is correct.. I'm trying to put the value of "GetMenuItemID" where
you are inserting a "Index" value.

See MSDN http://msdn.microsoft.com/en-us/library/ms647979(VS.85).aspx

The following code does not work but it gives an example of what I'm trying to do.

Replace:
Code:
       ;StrSize := GetContextMenuText(ControlHwnd, a_index-1)


New:
Code:
StrSize := GetContextMenuText(ControlHwnd, a_index-1),MenuitemID := DllCall( "GetMenuItemID", UInt, hMenu, Int, a_index-1 )   


Replace:
Code:
Tooltip, %TextText%, 0, 0


New:
Code:
   Tooltip, % TextText ? MenuitemID " : " . TextText : "", 0, 0


If you want to see what value I'm trying to insert into the Tooltip

Do the following without making the changes above. Instead do the following.

; UnComment these lines:
Code:
SendMessage, 0x1E1, 0,0,, ahk_class #32768 ; VERIFIED for MenuitemID, FAILED BREAKS Menuitem Name & Hotkey portion
   ;hMenu := ErrorLevel


Code:
TextText   = %TextText%ID=%MenuitemID%  ; ****VERIFIED when used with DllCall( "GetMenuItemID" ONLY ***


Comment Out these lines:

Code:
TextText = %TextText%%a_index%:%StrSize%`n  ; VERIFIED w/o MenuitemID


Code:
TextText = %TextText%      ; VERIFIED w/o MenuitemID


And then start Notepad & run the script. You'll see the "GetMenuItemID" value (range 1-65999) for each menuitem in the Tooltip
as you slowly move the thru each menuitem under a Menu, eg. Edit in Notepad is a good example.

Thanks again for your help!

_________________
Image
"Man's quest for knowledge is an expanding series whose limit is infinity"


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: September 12th, 2009, 8:14 pm 
Offline

Joined: June 18th, 2008, 8:36 am
Posts: 4923
Location: AHK Forum
How about that :?:
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)
       SendMessage, 0x01E1, , , , ahk_id %ControlHwnd%
       ;Errorlevel is set by SendMessage. It contains the handle to the menu
       MenuitemID := DllCall( "GetMenuItemID", "UInt", ErrorLevel, "Int", 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 ? MenuitemID " : " . 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.
   }
}

_________________
AHK_H (2alpha) AHF TT _Struct WatchDir Yaml _Input ObjTree RapidHotkey DynaRun :wink:


Report this post
Top
 Profile  
Reply with quote  
 Post subject: Solution to MenuSpy
PostPosted: September 13th, 2009, 3:40 am 
Offline

Joined: August 22nd, 2009, 11:23 pm
Posts: 294
Thanks for the quick response HotKeyIt.

The code you suggested is correct, however, the location breaks
the dynamic tooltip updatiing.

I moved it to the "IsEnable" section of the script. IT WORKS!

Thanks for all great work of Micha, Majkinetor,Sean & HotKeyIt

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 ID, Name & Hotkey

The ID is the "GetMenuItemID" function - see MSDN.
It's the value you see in WinInspector and you use in a PostMessage to select a specific menuitem.

As I mentioned above. This works on any type of menu;
standard, context, popup,tray & ahk menus in a tooltip
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. :D

Here's the finished code.

Code:
; MenuSpy 1.0
; by TxQuestor
; 9/12/09

; It dynamically displays the menuitem ID, Name & Hotkey in a tooltip as user moves thru each menuitem.

; This script uses part or all of these Menu Scripts

; Micha: GetContext Menu - http://www.autohotkey.com/forum/topic21451.html
; Majkinetor: MMenu - http://www.autohotkey.com/forum/topic17674.html
; Sean: GetInfo fromContext Menu - http://www.autohotkey.com/forum/viewtopic.php?p=137692.html
; HotKeyIt - created code to add the "GetMenuItemID" value to the MenuSpy Tooltip.   

#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)
         
  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)
   ; Uses SendMessage to get the "GetMenuItemID" value
   SendMessage, 0x01E1, , , , ahk_id %ControlHwnd%
   MenuitemID := DllCall( "GetMenuItemID", "UInt", ErrorLevel, "Int", a_index-1 )
   

   {
      TextText =
    if (IsEnabled & MFS_HILITE)

       StrSize := GetContextMenuText(ControlHwnd, a_index-1)
       TextText = %TextText%%a_index%:%StrSize%`n
       StringTrimLeft, TextText, TextText, 2
       StringReplace, TextText, TextText, `:, , All
       TextText = %TextText%   
   }   
  }
  CoordMode, Tooltip, Screen
  Tooltip, % TextText  ? "ID" . MenuitemID " : " . 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.
   }
}

_________________
Image
"Man's quest for knowledge is an expanding series whose limit is infinity"


Report this post
Top
 Profile  
Reply with quote  
PostPosted: September 15th, 2009, 8:36 pm 
Offline

Joined: August 22nd, 2009, 11:23 pm
Posts: 294
"MenuSpy" Solved

Dynamically display a Menuitem's ID, Name & Hotkey in a Toolip as user moves thru each Menu's Menuitem

Thanks for all great work of Micha, Majkinetor,Sean & HotKeyIt

I used all, part or ideas from their Menu scripts to solve dynamic tooltip of menuitems

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 ID, Name & Hotkey

The ID is the "GetMenuItemID" function - see MSDN for details.
It's the value you see in WinInspector and you use the value in a PostMessage to select a specific menuitem.

As I mentioned above. This works on any type of menu;
standard, context, popup,tray & ahk menus in a tooltip
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 finished code.

Code:
; MenuSpy
; by TxQuestor
; 09/12/2009

; It dynamically displays a Menu's  menuitem Name & Hotkey in a tooltip as user moves thru each menuitem.

; Micha: GetContext Menu - http://www.autohotkey.com/forum/topic21451.html
; Sean: GetInfo fromContext Menu - http://www.autohotkey.com/forum/viewtopic.php?p=137692.html
; Majkinetor: MMenu - http://www.autohotkey.com/forum/topic17674.html
; HotKeyIt:  created the code to add the MenuitemID to MenuSpy

#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 IsEnabled & Highlight
  loop, %ContextMenCnt%
  {

   ; Get the Menuitem State & send Message to Menuitem to retrieve the "GetMenuItemID" value
   IsEnabled := GetContextMenuState(ControlHwnd, a_index-1)
   SendMessage, 0x01E1, , , , ahk_id %ControlHwnd%
   ; Errorlevel is set by SendMessage. It contains the ID handle to the menuitem not the same as wID in MIIM_ID from GetMenuItemInfo Function ()
   MenuitemID := DllCall( "GetMenuItemID", "UInt", ErrorLevel, "Int", a_index-1 )  ; gets MenuitemID to show in Tooltip

   {
      TextText =
    if (IsEnabled & MFS_HILITE)      ; If Menuitem is Enabled & Highlighted get the Values for it's ID, Name & Hotkey
       
     StrSize := GetContextMenuText(ControlHwnd, a_index-1) ; Gets the Name & Hotkey for a Menuitem
     TextText = %TextText%%a_index%:%StrSize%`n 
     StringTrimLeft, TextText, TextText, 2
     StringReplace, TextText, TextText, `:, , All
         }   
  }
  CoordMode, Tooltip, Screen
  Tooltip, % TextText  ? "ID" . MenuitemID " : " . TextText : "",    ; Displays Menuitem's ID, Name & Hotkey
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
}
;***************************************************************
; Majkinetor's - Get Menu's MenuitemInfo
;***************************************************************

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.
   }
}
 

_________________
Image
"Man's quest for knowledge is an expanding series whose limit is infinity"


Report this post
Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 6 posts ] 

All times are UTC [ DST ]


Who is online

Users browsing this forum: Google [Bot], JSLover, Miguel, rbrtryn, XstatyK and 62 guests


You can post new topics in this forum
You can reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Powered by phpBB® Forum Software © phpBB Group