 |
AutoHotkey Community Let's help each other out
|
| View previous topic :: View next topic |
| Author |
Message |
skrommel
Joined: 30 Jul 2004 Posts: 178
|
Posted: Mon Mar 06, 2006 11:23 pm Post subject: Extracting Menus |
|
|
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 |
|
 |
Invalid User
Joined: 14 Feb 2005 Posts: 442 Location: Texas, Usa
|
Posted: Wed Mar 08, 2006 11:20 am Post subject: |
|
|
thanks, a nice solution. I was looking for this some time back. _________________ my lame sig  |
|
| Back to top |
|
 |
PhiLho
Joined: 27 Dec 2005 Posts: 6721 Location: France (near Paris)
|
Posted: Wed Mar 08, 2006 2:39 pm Post subject: |
|
|
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 |
|
 |
HuBa
Joined: 24 Feb 2007 Posts: 172 Location: Budapest, Hungary
|
Posted: Sun Mar 25, 2007 5:24 pm Post subject: |
|
|
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 |
|
 |
jsmain
Joined: 11 Jul 2005 Posts: 81
|
Posted: Fri Aug 03, 2007 8:49 pm Post subject: |
|
|
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 |
|
 |
HuBa
Joined: 24 Feb 2007 Posts: 172 Location: Budapest, Hungary
|
Posted: Fri Aug 03, 2007 9:00 pm Post subject: |
|
|
| 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 |
|
 |
HugoV
Joined: 27 May 2007 Posts: 499
|
Posted: Sat Aug 04, 2007 9:00 pm Post subject: |
|
|
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 |
|
 |
HugoV
Joined: 27 May 2007 Posts: 499
|
Posted: Tue Aug 07, 2007 3:58 pm Post subject: |
|
|
| bump: see post above... |
|
| Back to top |
|
 |
Superfraggle
Joined: 02 Nov 2004 Posts: 848 Location: London, UK
|
Posted: Tue Aug 07, 2007 4:26 pm Post subject: |
|
|
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 |
|
 |
|
|
You can post new topics in this forum You can reply to topics in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|