Jump to content

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

Get Info from Context Menu


  • Please log in to reply
19 replies to this topic
Micha
  • Members
  • 539 posts
  • Last active: Dec 31 2011 01:43 PM
  • Joined: 15 Nov 2005
Hi, there are solutions how to get information from a standard menu (File, edit, help) but I haven't found a solution for context menus (i.e. right click in explorer)

Here's a script that traces the names of every entry of a windows standard popup menu and it traces the state (selected, enabled, disabled...)

If you find a software where the script does not work, it uses self-programmed context menus (office, pspad).

To use the code, copy the functions to your script or just delete the demo: label

Ciao
Micha


;
; AutoHotkey Version: 1.x
; Language:       English
; Platform:       Win9x/NT
; Author:         micha
;
; Script Function:
;	Demonstrates how to retrieve infos from a context/ popup menu
;

/*
  This is the struct we are using.

  typedef struct tagMENUITEMINFO {
  UINT    cbSize;
  UINT    fMask;
  UINT    fType;
  UINT    fState;
  UINT    wID;
  HMENU   hSubMenu;
  HBITMAP hbmpChecked;
  HBITMAP hbmpUnchecked;
  ULONG_PTR dwItemData;
  LPTSTR  dwTypeData;
  UINT    cch;
  HBITMAP hbmpItem;
} MENUITEMINFO, *LPMENUITEMINFO;


*/

#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_ENABLED = 0
  MFS_CHECKED = 8
  MFS_DEFAULT = 0x1000
  MFS_DISABLED = 2
  MFS_GRAYED = 1
  MFS_HILITE = 0x80
  ;MFS_UNCHECKED = 0
  ;MFS_UNHILITE = 0

  ;Get mouse position and handle to wnd under the mouse cursor
  MouseGetPos, MouseScreenX, MouseScreenY, MouseWindowUID, MouseControlID
  WinGet,ControlHwnd, ID,ahk_id %MouseWindowUID%
  ;Get count of menu items
  ContextMenCnt := GetContextMenuCount(ControlHwnd)
  if ContextMenCnt < 1
  {
   Tooltip,
   return
  }
  TooltipText =
  ;Read info for each menu item
  loop, %ContextMenCnt%
  {
   IsEnabled := GetContextMenuState(ControlHwnd, a_index-1)
   {
    CurrentText =
    if IsEnabled = 0
     CurrentText = %CurrentText% Enabled
    if (IsEnabled & MFS_CHECKED)
       CurrentText = %CurrentText% Checked
    if (IsEnabled & MFS_DEFAULT)
       CurrentText = %CurrentText% Default
    if (IsEnabled & MFS_DISABLED)
       CurrentText = %CurrentText% Disabled
    if (IsEnabled & MFS_GRAYED)
       CurrentText = %CurrentText% Grayed
    if (IsEnabled & MFS_HILITE)
       CurrentText = %CurrentText% Highlight
    TooltipText = %TooltipText%%a_index%:%CurrentText%`n
   }
  }
  TextText =
  loop, %ContextMenCnt%
  {
    StrSize := GetContextMenuText(ControlHwnd, a_index-1)
    TextText = %TextText%%a_index%:%StrSize%`n
  }
  CoordMode, Tooltip, Screen
  Tooltip, %TooltipText%---`n%TextText%, 0, 0
return

/***************************************************************
 * 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
}

/***************************************************************
 * 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)
  /*
  IsEnabled = 1
  if GetMenuItemInfoRes > 0
     IsEnabled = 0
  return IsEnabled
  */
  return GetMenuItemInfoRes
}

/***************************************************************
 * returns the text of a menu entry (standard windows context menus only!!!)
 ***************************************************************
*/
GetContextMenuText(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, 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)
  ;Set type - Get only size of string we need to allocate
  ;InsertInteger(0, MenuItemInfo, 8, 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
}

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

PrintScreen::reload


------------------------------------------------------
------------------------------------------------------
Another code which retrieves the commandID

;
; AutoHotkey Version: 1.x
; Language:       English
; Platform:       Win9x/NT
; Author:         micha
;
; Script Function:
;	Demonstrates how to retrieve infos from a context/ popup menu
;

/*
  This is the struct we are using.

  typedef struct tagMENUITEMINFO {
  UINT    cbSize;
  UINT    fMask;
  UINT    fType;
  UINT    fState;
  UINT    wID;
  HMENU   hSubMenu;
  HBITMAP hbmpChecked;
  HBITMAP hbmpUnchecked;
  ULONG_PTR dwItemData;
  LPTSTR  dwTypeData;
  UINT    cch;
  HBITMAP hbmpItem;
} MENUITEMINFO, *LPMENUITEMINFO;


*/

#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_ENABLED = 0
  MFS_CHECKED = 8
  MFS_DEFAULT = 0x1000
  MFS_DISABLED = 2
  MFS_GRAYED = 1
  MFS_HILITE = 0x80
  ;MFS_UNCHECKED = 0
  ;MFS_UNHILITE = 0

  ;Get mouse position and handle to wnd under the mouse cursor
  MouseGetPos, MouseScreenX, MouseScreenY, MouseWindowUID, MouseControlID
  WinGet,ControlHwnd, ID,ahk_id %MouseWindowUID%
  ;Get count of menu items
  ContextMenCnt := GetContextMenuCount(ControlHwnd)
  if ContextMenCnt < 1
  {
   Tooltip,
   return
  }
  TooltipText =
  ;Read info for each menu item
  loop, %ContextMenCnt%
  {
   IsEnabled := GetContextMenuState(ControlHwnd, a_index-1)
   {
    CurrentText =
    if IsEnabled = 0
     CurrentText = %CurrentText% Enabled
    if (IsEnabled & MFS_CHECKED)
       CurrentText = %CurrentText% Checked
    if (IsEnabled & MFS_DEFAULT)
       CurrentText = %CurrentText% Default
    if (IsEnabled & MFS_DISABLED)
       CurrentText = %CurrentText% Disabled
    if (IsEnabled & MFS_GRAYED)
       CurrentText = %CurrentText% Grayed
    if (IsEnabled & MFS_HILITE)
       CurrentText = %CurrentText% Highlight
    TooltipText = %TooltipText%%a_index%:%CurrentText%`n
   }
  }
  TextText =
  loop, %ContextMenCnt%
  {
    StrSize := GetContextMenuText(ControlHwnd, a_index-1)
    nID := GetContextMenuID(ControlHwnd, a_index-1)
    TextText = %TextText%%a_index%:%StrSize%-ID=%nID%`n
  }
  CoordMode, Tooltip, Screen
  Tooltip, %TooltipText%---`n%TextText%, 0, 0
return

/***************************************************************
 * 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
}

/***************************************************************
 * 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)
  /*
  IsEnabled = 1
  if GetMenuItemInfoRes > 0
     IsEnabled = 0
  return IsEnabled
  */
  return GetMenuItemInfoRes
}


/***************************************************************
 * returns the ID of a menu entry
 ***************************************************************
*/
GetContextMenuID(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

  ;UINT GetMenuItemID(          HMENU hMenu,    int nPos);
  InfoRes := DllCall("user32.dll\GetMenuItemID",UInt,hMenu, Uint, Position)

  InfoResError := errorlevel
  LastErrorRes := DllCall("GetLastError")
  if InfoResError <> 0
     return -1
  if LastErrorRes != 0
     return -1

  return InfoRes
}

/***************************************************************
 * returns the text of a menu entry (standard windows context menus only!!!)
 ***************************************************************
*/
GetContextMenuText(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, 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)
  ;Set type - Get only size of string we need to allocate
  ;InsertInteger(0, MenuItemInfo, 8, 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
}

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

PrintScreen::reload


daonlyfreez
  • Members
  • 995 posts
  • Last active: Jan 23 2013 08:16 AM
  • Joined: 16 Mar 2005
Great script! Very useful, thank you... 8)
Posted Image mirror 1mirror 2mirror 3ahk4.me • PM or Posted Image

David Andersen
  • Members
  • 140 posts
  • Last active: Jun 28 2011 04:54 PM
  • Joined: 15 Jul 2005
This script is really useful! :D

Would it also be possible to add an entry to the context menu. This would really open up a lot of possibilities for seamless integration of Autohotkey scripts into various programs.

Helpy
  • Guests
  • Last active:
  • Joined: --
Nice!
Note that ExtractInteger and InsertInteger are no longer needed since we have now NumGet and NumPut.

David Andersen, this has been asked some times, but I fear it could be hard to impossible (but impossible is often a void word here!): I guess additional menu items would still have to be processed by the target application which will just ignore them... Unless somebody know how to hook the corresponding messages, before the soft see them!

David Andersen
  • Members
  • 140 posts
  • Last active: Jun 28 2011 04:54 PM
  • Joined: 15 Jul 2005
Yeah, hooking is a good idea. I saw a window hook somewhere here. It would be perfectly acceptable that new items only could be placed at the bottom of the list. Could you get the x/y coordinates of the frame around the context menu? This could be a step on the way, I believe.

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006

Could you get the x/y coordinates of the frame around the context menu?

It should be easy to get the coords of the menu window itself, if that's what you mean:
WinGetPos, x, y,,, ahk_id %ControlHwnd% ; where ControlHwnd is the ID of the menu window...
I don't see what use this is, though. (I think it would be simple to add items to the end of a menu's list of items; the complicated bit would be intercepting messages to detect when the menu item is selected.)

Micha, if I may ask, what is the purpose of the WinGet,,ID call?
MouseGetPos, MouseScreenX, MouseScreenY, MouseWindowUID, MouseControlID
  WinGet,ControlHwnd, ID,ahk_id %MouseWindowUID%
WinGet,,ID gets the unique identifier of a window, and
ahk_id %var% is used to target a window by its unique identifier,
so would not the result (ControlHwnd) always be equal to the input (MouseWindowUID)?

David Andersen
  • Members
  • 140 posts
  • Last active: Jun 28 2011 04:54 PM
  • Joined: 15 Jul 2005
lexikos,

Thanks for your answer. If a menu item could be added to the context menu, then one could get the measurements of the whole context menu before and after we add an item. Then one could probably intercept all mouse clicks and see if they have the right x/y coordinates to hit the item we added.

This process would probably have to be gone through each time a context menu is displayed, in case that some of the items have changed. Visually it could simply look fancy that there is a delay of half a second from the context menu is displayed until the last item shows up. :D

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

Hi, there are solutions how to get information from a standard menu (File, edit, help) but I haven't found a solution for context menus (i.e. right click in explorer)

Thanks for sharing the script, however, it's been found already:
<!-- m -->http://www.autohotke...topic17904.html<!-- m -->

Here is the code I've been using since then. I think I combined just MN_GETHMENU with the script already existing in the forum, but I can't remember it. Just take it as a simplified alternative:

#SingleInstance, Force
SetBatchLInes, -1

WinWait, ahk_class #32768
SendMessage, 0x1E1, 0, 0		; MN_GETHMENU
hMenu := ErrorLevel
sContents := GetMenu(hMenu)
WinWaitClose

MsgBox, % sContents


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"
		If (idn = -1) && (hSubMenu := DllCall("GetSubMenu", "Uint", hMenu, "int", idx))
		   sContents .= GetMenu(hSubMenu)
	}
	Return	sContents
}


Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006

Then one could probably intercept all mouse clicks and see if they have the right x/y coordinates to hit the item we added.

Ah, I hadn't thought of that. If you do take this approach, you might be interested in GetMenuItemRect(), which can be used to get the position and size of a menu item. Also, don't forget keyboard navigation (arrow keys and mnemonic characters.) :)

David Andersen
  • Members
  • 140 posts
  • Last active: Jun 28 2011 04:54 PM
  • Joined: 15 Jul 2005
lexikos,

Excellent! It is great to know of this possibility! In essense one could create seamless plug-ins for various programs (that don't even have support for plug-ins). This should have a big potensial for Autohotkey.

jsmain
  • Members
  • 126 posts
  • Last active: Oct 23 2017 06:24 PM
  • Joined: 11 Jul 2005
This could indeed be usefull, but IMO, we need to be able to activate context menus in a hidden state, for a particular control, item in a control, or mouse coordinates for it to be completely useful.

Anyone familiar with Hidepopup from the MadCodeHook collection?
Used as a girder plugin, you could determine menu settings, and set, unset, or activate menu and context menu items without showing the actual menu.

Makes for a much cleaner solution.
Jeff Main

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006

This could indeed be usefull, but IMO, we need to be able to activate context menus in a hidden state, for a particular control, item in a control, or mouse coordinates for it to be completely useful.

That should be mostly possible by simulating mouse input (i.e. ControlClick.) The hard part is having the context menu activate in a hidden state.

I've had some success using SetWinEventHook (hooking EVENT_SYSTEM_MENUPOPUPSTART) to move a menu off-screen as soon as it opens. At first I was trying to set the transparency level of any menu that pops up, but I found that the "fade in" animation (Windows XP) was overriding that. Fortunately, it seems the fade-in animation also prevents the menu from being visible for the split-second between when the menu is created and when it is moved (off-screen, using WinMove.) I tried using WinHide to hide the menu, but that left the drop-shadow permanently on my screen.

Interestingly, SetWinEventHook can also be used to track menu item selection (EVENT_OBJECT_FOCUS), which could assist in overriding/hooking menu items. (As far as I can tell, there is no event for activating/clicking an item, but mouse/keyboard hotkeys could be used in combinaton with EVENT_OBJECT_FOCUS.)

One possibly big problem I can see is in determining if the menu that popped up is the right one...

I think a CBT hook (SetWindowsHookEx, WH_CBT) could be used to hook creation of the menu window properly - i.e. without the menu appearing briefly - but it seems CBT hook callbacks must be compiled into DLL files.

jsmain
  • Members
  • 126 posts
  • Last active: Oct 23 2017 06:24 PM
  • Joined: 11 Jul 2005
My primary use for this would be to get information from the menu....

for example, in IE, the setting of the Status bar.... View >Status Bar

If I can determine the state of the menu setting, I can adjust it as necessary with postmessage.

So this limits the amount of pop up menus to only that of information retrieval.

Is there a better way of opening a specific menu target.... As in my example, "View > Status Bar", than using mouse position? or without using the mouse and keyboard at all?

In the Madcodehook hidepopup, the configuration allowed you to call out the menus as "View" "Status Bar"
Jeff Main

contextxyz
  • Members
  • 1 posts
  • Last active: Aug 08 2007 12:47 AM
  • Joined: 08 Aug 2007
I would also like to know how to create a keyboard shortcut, eg f12 to activate/select a context menu item. I in fact have a program where I need to select a context menu submenu, so its actually a bit more involved

It would be best as some others(lexikos) have mentioned that if this function could be hidden, ie not have to go through simulated mouse clicks to do this would be great, but I'll take anything.

jsmain
  • Members
  • 126 posts
  • Last active: Oct 23 2017 06:24 PM
  • Joined: 11 Jul 2005
If you have windows spy utility, or wininspector, you can identify the Menu ID(wParam), and then just use postmessage to seend the message to the window directly, unless of course you need to determine the checkstate....
Rajat's got a great article on this here.
Jeff Main