AutoHotkey Homepage AutoHotkey Community
Let's help each other out
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Extracting Menus

 
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions
View previous topic :: View next topic  
Author Message
skrommel



Joined: 30 Jul 2004
Posts: 178

PostPosted: Mon Mar 06, 2006 11:23 pm    Post subject: Extracting Menus Reply with quote

I was looking for how to handle menus, but I couldn't find it anywhere, so here's some code for getting the menus from Calculator.

Anyone know why the menu ids in some programs change for every run?

Skrommel


Code:
#SingleInstance,Force
SetBatchLInes,-1
AutoTrim,Off

WinGet,hWnd,ID,Calculator

wholemenu=
space=
hMenu:=DllCall("user32\GetMenu","UInt",hWnd)
GETMENU(hMenu)
MsgBox,%wholemenu%
Return

GETMENU(hMenu)
{
  nMaxCount=100
  uFlag:=0x0400  ;MF_BYPOSITION
  global wholemenu
  global space

  menuitemcount:=DllCall("GetMenuItemCount",UInt,hMenu)
  Loop,%menuitemcount%
  {
    nPos:=A_Index-1
    VarSetCapacity(lpString,100,0) ; line inserted on 2008-02-18 -- Hope you dont mind Skrommel! - Moderator!
    length:=DllCall("user32\GetMenuString","UInt",hMenu,"UINT",nPos,"STR",lpString,"int",nMaxCount,"UINT",uFlag)
    wholemenu=%wholemenu%%hMenu% - %nPos%%A_Tab%%space%%lpString%`n
    hSubMenu:=DllCall("user32\GetSubMenu","UInt",hMenu,"int",nPos)
    If hSubMenu>-1
    {
      Loop,3
        space=%space%%A_Space%
      GETMENU(hSubMenu)
      StringTrimRight,space,space,3
    }
  }
  Return
}
Back to top
View user's profile Send private message Visit poster's website
Invalid User



Joined: 14 Feb 2005
Posts: 442
Location: Texas, Usa

PostPosted: Wed Mar 08, 2006 11:20 am    Post subject: Reply with quote

thanks, a nice solution. I was looking for this some time back.
_________________
my lame sig Smile
Back to top
View user's profile Send private message Send e-mail Yahoo Messenger
PhiLho



Joined: 27 Dec 2005
Posts: 6721
Location: France (near Paris)

PostPosted: Wed Mar 08, 2006 2:39 pm    Post subject: Reply with quote

I like that! It can be useful.
Note you don't display the menu ID but the menu handle, which isn't fixed.
I wanted to change the code to avoid the use of a global variable, but I discovered that ByRef parameters don't mix with recursive functions!
So I still use a global variable to store the result, sigh!
Here is my version, works with the active window:
Code:
WinGet hWnd, ID, A

hMenu := DllCall("GetMenu", "UInt", hWnd)

#wholeMenu =
GetMenu(hMenu)

Clipboard = %#wholeMenu%
MsgBox %#wholeMenu%
Return

GetMenu(_hMenu, _menuLevel=0)
{
   local menuItemCount, indent, nPos, length, lpString, id, hSubMenu

   menuItemCount := DllCall("GetMenuItemCount", "UInt", _hMenu)
   indent =
   If _menuLevel > 0
   {
      indent := "|   "
      Loop % _menuLevel - 1
      {
         indent := indent "|   "
      }
      indent := indent "+-- "
   }
   Loop %menuItemCount%
   {
      nPos := A_Index - 1
      length := DllCall("GetMenuString"
         , "UInt", _hMenu
         , "UInt", nPos
         , "UInt", 0   ; NULL
         , "Int", 0   ; Get length
         , "UInt", 0x0400)   ; MF_BYPOSITION
      VarSetCapacity(lpString, length + 1)   ; I don't check the result...
      length := DllCall("GetMenuString"
         , "UInt", _hMenu
         , "UInt", nPos
         , "Str", lpString
         , "Int", length + 1
         , "UInt", 0x0400)
      id := DllCall("GetMenuItemID", "UInt", _hMenu, "Int", nPos)
      If length > 0   ; 0 if not of MFT_STRING type or error
      {
         If id < 0
         {
            If _menuLevel = 0
               id = _____
            Else
               id = 00000
         }
         Else
         {
            id = 00000%id%
         }
         StringRight id, id, 5
         #wholeMenu = %#wholeMenu%%id% - %indent%%lpString%`n
      }
      hSubMenu := DllCall("GetSubMenu", "UInt", _hMenu, "Int", nPos)
      If hSubMenu != 0
      {
         GetMenu(hSubMenu, _menuLevel + 1)
      }
   }
}

I tried to slightly improve the indentation display, too.

Update: Chris corrected the crash bug (in v1.0.42.07), so I can give the version without global variables:
Code:
WinGet hWnd, ID, A

hMenu := DllCall("GetMenu", "UInt", hWnd)

wholeMenu =
GetMenu(hMenu, wholeMenu)

Clipboard = %wholeMenu%
MsgBox %wholeMenu%
Return

GetMenu(_hMenu, ByRef @wholeMenu, _menuLevel=0)
{
   local menuItemCount, indent, nPos, length, lpString, id, hSubMenu

   menuItemCount := DllCall("GetMenuItemCount", "UInt", _hMenu)
   indent =
   If _menuLevel > 0
   {
      indent := "|   "
      Loop % _menuLevel - 1
      {
         indent := indent "|   "
      }
      indent := indent "+-- "
   }
   Loop %menuItemCount%
   {
      nPos := A_Index - 1
      length := DllCall("GetMenuString"
         , "UInt", _hMenu
         , "UInt", nPos
         , "UInt", 0   ; NULL
         , "Int", 0   ; Get length
         , "UInt", 0x0400)   ; MF_BYPOSITION
      VarSetCapacity(lpString, length + 1)   ; I don't check the result...
      length := DllCall("GetMenuString"
         , "UInt", _hMenu
         , "UInt", nPos
         , "Str", lpString
         , "Int", length + 1
         , "UInt", 0x0400)
      id := DllCall("GetMenuItemID", "UInt", _hMenu, "Int", nPos)
      If length > 0   ; 0 if not of MFT_STRING type or error
      {
         If id < 0
         {
            If _menuLevel = 0
               id = _____
            Else
               id = 末末0
         }
         Else
         {
            id = 00000%id%
         }
         StringRight id, id, 5
         @wholeMenu = %@wholeMenu%%id% - %indent%%lpString%`n
      }
      hSubMenu := DllCall("GetSubMenu", "UInt", _hMenu, "Int", nPos)
      If hSubMenu != 0
      {
         GetMenu(hSubMenu, @wholeMenu, _menuLevel + 1)
      }
   }
}

I left the first version as some people may want something working for an older version of AHK.
Code:
_____ - &File
65400 - |   +-- &Reload Script   Ctrl+R
65401 - |   +-- &Edit Script   Ctrl+E
65402 - |   +-- &Window Spy
65403 - |   +-- &Pause Script   Pause
65404 - |   +-- &Suspend Hotkeys
65405 - |   +-- E&xit (Terminate Script)
_____ - &View
65406 - |   +-- &Lines most recently executed   Ctrl+L
65407 - |   +-- &Variables and their contents   Ctrl+V
65408 - |   +-- &Hotkeys and their methods   Ctrl+H
65409 - |   +-- &Key history and script info   Ctrl+K
65410 - |   +-- &Refresh   F5
_____ - &Help
65411 - |   +-- &User Manual   F1
65412 - |   +-- &Web Site

The function fails on Firefox and Windows Explorer.
Code:
_____ - &File
57600 - |   +-- &New   Ctrl+N
57601 - |   +-- &Open...   Ctrl+O
57603 - |   +-- &Save   Ctrl+S
57604 - |   +-- Save &As...
57616 - |   +-- Recent File
40071 - |   +-- Password...
32786 - |   +-- &User Preferences...
40064 - |   +-- Filters...
40065 - |   +-- Apply filters   F7
40066 - |   +-- Friends
57665 - |   +-- E&xit
_____ - &Edit
57642 - |   +-- Select A&ll   Ctrl+A
32791 - |   +-- Ne&w Mailbox...   Ins
32790 - |   +-- &Delete   Del
32783 - |   +-- P&roperties...   Alt+Enter
40061 - |   +-- Mark all as read
_____ - &View
59393 - |   +-- &Status Bar
59392 - |   +-- &Tool Bar
末末0 - |   +-- Language
40005 - |   |   +-- Default English
32773 - |   +-- Lar&ge Icons
59408 - |   +-- S&mall Icon
59410 - |   +-- &List
32776 - |   +-- &Details
32792 - |   +-- &Refresh   F5
32821 - |   +-- Sto&p   Esc
_____ - Quick&Tools
32799 - |   +-- Quick &View
32798 - |   +-- Quick R&eply
32797 - |   +-- Quick &Send
32800 - |   +-- Quick &Delete   Shift+Del
40001 - |   +-- &Load
40002 - |   +-- View as &text
40004 - |   +-- Preview
末末0 - |   +-- Filters
40067 - |   |   +-- Add to friends
40068 - |   |   +-- Domain to friends
32792 - |   +-- &Check Now!   F5
32822 - |   +-- S&uspend
32821 - |   +-- Sto&p   Esc
_____ - &Help
57670 - |   +-- &Help   F1
40583 - |   +-- Test RegExp
57664 - |   +-- &About the program...

_________________
vPhiLho := RegExReplace("Philippe Lhoste", "^(\w{3})\w*\s+\b(\w{3})\w*$", "$1$2")
Back to top
View user's profile Send private message Visit poster's website
HuBa



Joined: 24 Feb 2007
Posts: 172
Location: Budapest, Hungary

PostPosted: Sun Mar 25, 2007 5:24 pm    Post subject: Reply with quote

Good job!

But how can I get the hwnd of an AHK menu (like Tray menu)?

This doesn't works:
ControlGet, hMenu, Hwnd, , Tray, A


BTW, a little optimization can be made here in the script abowe:
Code:
Else
{
  id = 00000%id%
  StringRight id, id, 5
}         
@wholeMenu = %@wholeMenu%%id% - %indent%%lpString%`n
Back to top
View user's profile Send private message Visit poster's website
jsmain



Joined: 11 Jul 2005
Posts: 81

PostPosted: Fri Aug 03, 2007 8:49 pm    Post subject: Reply with quote

This is great stuff right here!
Too bad it doesn't also show the check states of the menu items....
_________________
Jeff Main
Back to top
View user's profile Send private message
HuBa



Joined: 24 Feb 2007
Posts: 172
Location: Budapest, Hungary

PostPosted: Fri Aug 03, 2007 9:00 pm    Post subject: Reply with quote

jsmain wrote:
This is great stuff right here!
Too bad it doesn't also show the check states of the menu items....

It can be easily achieved with a little coding. You can query the check state via WinAPI.
Back to top
View user's profile Send private message Visit poster's website
HugoV



Joined: 27 May 2007
Posts: 499

PostPosted: Sat Aug 04, 2007 9:00 pm    Post subject: Reply with quote

I was wondering if you could use this to mimick something from
Quicksilver: Application Menus, you can watch a video here:
http://www.themerlinshow.com/ep/008-howto-quicksilver-application-menus

Basically:
- you press a hotkey
- you get a list of the entire menu from the current application
- you can do a incremental search (like iswitch)
- execute selected menu

Edit: to answer my own question, yes it is possible, at least
with some applications. I've tested the script briefly below with
Total Commander and Notepad++ and it seems to work.

- Run the script
- Press capslock
- Type to search menu item
- Hit enter
- and if all goes well it the chosen menu item should be executed

(Doesn't seem to work with UltraEdit, MS Word...)

Warning: The code below has been merged from two scripts just to
make it work, so it can be improved a lot I think. Would be nice if
MS Word and other applications could be supported...

Code:

; ---------------------------------------------------------------------
; merged two scripts:
; 1)
; Name: Incremental Listbox       
; http://www.autohotkey.com/forum/viewtopic.php?t=2534
; 2)
; name: extracting menus
; http://www.autohotkey.com/forum/viewtopic.php?t=8492
; ---------------------------------------------------------------------
; -- Configuration: ---------------------------------------------------
; ---------------------------------------------------------------------
;Your favourite hotkey:
CapsLock::                  ;Hotkey CapsLock to start

ControlGetFocus, control, A

;The name of the textfile containing the contents of the listbox:
GetMenuListTexts()

; ---------------------------------------------------------------------
; -- Autoexecute ------------------------------------------------------
; ---------------------------------------------------------------------

Gui, Add, ListBox, vChoice gListBoxClick w400 h250 vscroll
Gui, Add, Text, x10 y264 w50 h20, Search`:
Gui, Add, Edit, x66 y261 w343 h20

Gosub RefreshListBox

search =   

;The input-command in this loop processes keys pressed by the user
;or send by the "send"-command
Loop
{
    Input, input, L1, {enter}{esc}{backspace}{up}{down}{pgup}{pgdn}{tab}{left}{right}
       if ErrorLevel = EndKey:escape
      {
         Gui, cancel
         Gosub GuiClose
      }
       if ErrorLevel = EndKey:enter
      {
         GoSub, WordRetrieve
         continue
      }
       if ErrorLevel = EndKey:backspace
      {
         GoSub, DeleteSearchChar
         continue
      }
       if ErrorLevel = EndKey:up
      {
         Send, {up}
         continue
      }
       if ErrorLevel = EndKey:down
      {
         Send, {down}
         continue
      }
      if ErrorLevel = EndKey:pgup
      {
         Send, {pgup}
         continue
      }
      if ErrorLevel = EndKey:pgdn
      {
         Send, {pgdn}
         continue
      }
   
    search = %search%%input%               ;Assembles the search string
    GuiControl,, Edit1, %search%            ;Displays the search string in the edit control
    StringLen,SearchLength,Search
    Gosub RefreshListBox
    continue
}

return


; ---------------------------------------------------------------------
; -- Subroutines ------------------------------------------------------
; ---------------------------------------------------------------------

;Assigns the chosen item to the variable "Choice".
WordRetrieve:
Gui, submit, noHide
GuiControlGet, Choice ; Retrieve the ListBox's current selection.
Gui, Destroy
Loop, parse, MenuListTexts, |
{
 If (Choice = A_LoopField)
       {
       SendMenuItem:=command%A_Index%-1
       ControlGetFocus, control, HWND
   PostMessage, 0x111, %SendMenuItem%, %Control%,,A
   ;MsgBox %Choice%, %SendMenuItem%
   ExitApp
   }
      continue
}

Return
; ---------------------------------------------------------------------

;Exits the script on closing the GUI
GuiClose:
GuiEscape:
   ExitApp
; ---------------------------------------------------------------------

;Refreshes the listbox according to the search criteria:
RefreshListBox:
Wordlist=

Loop, parse, MenuListTexts, |
{
   IfInString, A_LoopField,%Search%     
      Wordlist=%Wordlist%|%A_LoopField%   
   Else
      continue
}

Gui, Show,
GuiControl,, ListBox1, %wordlist%
GuiControl, Choose, ListBox1, 1
return
;-------------------------------------------------------------------------

;Delete the last character and update Listbox:
DeleteSearchChar:

if search =
    return

StringTrimRight, search, search, 1
GuiControl,, Edit1, %search%
GoSub, RefreshListBox

return
;-------------------------------------------------------------------------

; Handle mouse click events on the list box:
ListBoxClick:

if A_GuiControlEvent = DoubleClick
    send, {enter}

return

; --------------

GetMenuListTexts()
{
global

WinGet hWnd, ID, A

hMenu := DllCall("GetMenu", "UInt", hWnd)

wholeMenu =
GetMenu(hMenu, wholeMenu)

Clipboard = %wholeMenu%
StringReplace, clipboard, clipboard, `&, , All
StringReplace, clipboard, clipboard, %A_Tab%, %A_Space%-%A_Space%, All
StringReplace, clipboard, clipboard, +, -, All
;MsgBox %clipboard%
test=0
command=0
MenuText=0
Loop, parse, clipboard, `n
   {
   StringLeft, command%A_Index%,A_LoopField, 5
   StringTrimLeft, MenuText%A_Index%, A_LoopField, 6
   tmp:=MenuText%A_Index%
   MenuListTexts=%MenuListTexts%|%tmp%
   }
}

GetMenu(_hMenu, ByRef @wholeMenu, _menuLevel=0)
{
   local menuItemCount, indent, nPos, length, lpString, id, hSubMenu
   menuItemCount := DllCall("GetMenuItemCount", "UInt", _hMenu)
   indent =
   Loop %menuItemCount%
   {
      nPos := A_Index - 1
      length := DllCall("GetMenuString"
         , "UInt", _hMenu
         , "UInt", nPos
         , "UInt", 0   ; NULL
         , "Int", 0   ; Get length
         , "UInt", 0x0400)   ; MF_BYPOSITION
      VarSetCapacity(lpString, length + 1)   ; I don't check the result...
      length := DllCall("GetMenuString"
         , "UInt", _hMenu
         , "UInt", nPos
         , "Str", lpString
         , "Int", length + 1
         , "UInt", 0x0400)
      id := DllCall("GetMenuItemID", "UInt", _hMenu, "Int", nPos)
      If length > 0   ; 0 if not of MFT_STRING type or error
      {
         If id < 0
         {
            If _menuLevel = 0
               id = _____
            Else
               id = 末末0
         }
   Else
   {
     id = 00000%id%
     StringRight id, id, 5
   }         
   @wholeMenu = %@wholeMenu%%id%|%lpString%`n
      }
      hSubMenu := DllCall("GetSubMenu", "UInt", _hMenu, "Int", nPos)
      If hSubMenu != 0
      {
         GetMenu(hSubMenu, @wholeMenu, _menuLevel + 1)
      }
   }

Back to top
View user's profile Send private message
HugoV



Joined: 27 May 2007
Posts: 499

PostPosted: Tue Aug 07, 2007 3:58 pm    Post subject: Reply with quote

bump: see post above...
Back to top
View user's profile Send private message
Superfraggle



Joined: 02 Nov 2004
Posts: 848
Location: London, UK

PostPosted: Tue Aug 07, 2007 4:26 pm    Post subject: Reply with quote

I dont know about ultra edit, but MSWord uses non standard menus, the information might be extractable someway but not the standard way.


afaik anyway
_________________
Steve F AKA Superfraggle

http://r.yuwie.com/superfraggle
Back to top
View user's profile Send private message MSN Messenger
Display posts from previous:   
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions All times are GMT
Page 1 of 1

 
Jump to:  
You can post new topics in this forum
You can reply to topics in this forum


Powered by phpBB © 2001, 2005 phpBB Group