Get Info from Context Menu (x64/x32 compatible)

Post your working scripts, libraries and tools
User avatar
jeeswg
Posts: 6904
Joined: 19 Dec 2016, 01:58
Location: UK

Get Info from Context Menu (x64/x32 compatible)

19 May 2017, 15:37

This is an AHK v1.1 x64/x32 compatible update of the script, by Micha, at:
Get Info from Context Menu - Scripts and Functions - AutoHotkey Community
https://autohotkey.com/board/topic/1975 ... text-menu/

There are two almost identical scripts there, this script is based on the one just after: 'Another code which retrieves the commandID', and unlike the other script contains the function GetContextMenuID(hWnd, Position).

The script to display WM_COMMAND IDs. When you hover over a menu item, a ToolTip appears displaying information about the menu items.

Code: Select all

;AHK v1.1 x64/x32 compatible update by jeeswg of:
;Get Info from Context Menu - Scripts and Functions - AutoHotkey Community
;https://autohotkey.com/board/topic/19754-get-info-from-context-menu/

;
; 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", Ptr,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
  NumPut(A_PtrSize=8?80:48, MenuItemInfo, 0, "UInt")
  ;Get only Flags from dllcall GetMenuItemInfo MIIM_TYPE = 1
  NumPut(1, MenuItemInfo, 4, "UInt")

  ;GetMenuItemInfo: Handle to Menu, Index of Position, 0=Menu identifier / 1=Index
  InfoRes := DllCall("user32.dll\GetMenuItemInfo", Ptr,hMenu, UInt,Position, Int,1, Ptr,&MenuItemInfo)

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

  ;Get Flag from struct
  GetMenuItemInfoRes := NumGet(MenuItemInfo, 12, "UInt")
  /*
  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", Ptr,hMenu, Int,Position, UInt)

  InfoResError := errorlevel
  LastErrorRes := DllCall("GetLastError", UInt)
  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
  NumPut(A_PtrSize=8?80:48, MenuItemInfo, 0, "UInt")
  ;Retrieve string MIIM_STRING = 0x40 = 64 (/ MIIM_TYPE = 0x10 = 16)
  NumPut(64, MenuItemInfo, 4, "UInt")
  ;Set type - Get only size of string we need to allocate
  ;NumPut(0, MenuItemInfo, 8, "UInt")
  ;GetMenuItemInfo: Handle to Menu, Index of Position, 0=Menu identifier / 1=Index
  InfoRes := DllCall("user32.dll\GetMenuItemInfo", Ptr,hMenu, UInt,Position, Int,1, Ptr,&MenuItemInfo)
  if InfoRes = 0
     return -1

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

  ;Get size of string from struct
  GetMenuItemInfoRes := NumGet(MenuItemInfo, A_PtrSize=8?64:40, "UInt")
  ;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 ;-)
  NumPut(GetMenuItemInfoRes, MenuItemInfo, A_PtrSize=8?64:40, "UInt")
  NumPut(&PopupText, MenuItemInfo, A_PtrSize=8?56:36, "Ptr")

  InfoRes := DllCall("user32.dll\GetMenuItemInfo", Ptr,hMenu, UInt,Position, Int,1, Ptr,&MenuItemInfo)
  if InfoRes = 0
     return -1

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

  return PopupText
}

;PrintScreen::reload
A script to show how to use the menu item IDs to invoke menu items.

Code: Select all

;- in the Notepad example we send the message to the main window
;- in the Explorer/Internet Explorer examples, we send the message
;to a specific control
;- I only knew that I should send the message to those particular
;controls, by trial and error, I tried sending the message to the
;different controls that belonged to the window, until I saw the
;desired action take place
;- note: this method will not always work, sometimes you can try
;sending an ID to the main window and all of its controls, and
;nothing happens
;- note: sometimes a menu item ID changes each time you display
;the context menu, the menu item IDs are not constant in such cases

q:: ;some WM_COMMAND examples
WinGetClass, vWinClass, A
if (vWinClass = "Notepad")
	PostMessage, 0x111, 4,,, A ;WM_COMMAND ;Save As...
if (vWinClass ~= "CabinetWClass|ExploreWClass")
	PostMessage, 0x111, 28706,, SHELLDLL_DefView1, A ;WM_COMMAND ;Invert Selection
if (vWinClass ~= "IEFrame")
	PostMessage, 0x111, 258,, Shell DocObject View1, A ;WM_COMMAND ;Save as...
return
A script to show how to send a menu item ID to every control that belongs to a window, to see if it invokes the menu item.

Code: Select all

q:: ;send a message to every control (e.g. an Explorer folder window: Invert Selection)
vID := 28706
WinGet, hWnd, ID, A
WinGet, vCtlList, ControlList, % "ahk_id " hWnd
vOutput := ""
Loop, Parse, vCtlList, `n
{
	vCtlClassNN := A_LoopField
	ToolTip, % vOutput .= vCtlClassNN "`r`n"
	PostMessage, 0x111, % vID,, % vCtlClassNN, % "ahk_id " hWnd ;WM_COMMAND
	Sleep 500
}
ToolTip
MsgBox, % "done"
return
See also:
[JEE_MenuGetText/JEE_MenuGetTextAll functions that retrieve menu text and ID numbers]
GUIs via DllCall: get/set internal/external control text - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=40514
Last edited by jeeswg on 24 Feb 2018, 02:58, edited 7 times in total.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
jmasiak
Posts: 22
Joined: 06 Aug 2019, 08:43
Location: Gdańsk

Re: Get Info from Context Menu (x64/x32 compatible)

08 Jan 2020, 02:14

Hi! I'm trying to use your script to call functions from context menu, but unfortunately it's working only for main menu. E.g. in Notepad for function Copy your script shows the code 769 and the same code is for function Copy in main menu. But function Select All in context menu has code 177 and in main menu the code is 25.
So when i'm trying to use SendMessage with the code 177, it isn't working. Do you have any idea why?
I attach screenshots of the Notepad (sorry, that they're in Polish)
My OS is Windows 10 Enterprise 64-bit and AHK version 1.1.30.01.
Attachments
screen1.png
Copy
screen1.png (67.19 KiB) Viewed 607 times
screen3.png
Select All (context menu)
screen3.png (97.6 KiB) Viewed 607 times
screen4.png
Select All (main menu)
screen4.png (55.18 KiB) Viewed 607 times
jmasiak
Posts: 22
Joined: 06 Aug 2019, 08:43
Location: Gdańsk

Re: Get Info from Context Menu (x64/x32 compatible)

13 Jan 2020, 03:22

I also show you my code for Select All in Notepad.

Code: Select all

#SingleInstance,Force

F7::
	PostMessage, 0x111, 25, 0, , A ;it's working
return

F8::
	PostMessage, 0x111, 177, 0, , A ;it's not
return
I know that I could use ony the first option but I want to use functions from context menu in other programs like Total Commander.

Return to “Scripts and Functions”

Who is online

Users browsing this forum: Cuadrix, leosouza85, MTRNord and 36 guests