AutoHotkey Community

It is currently May 27th, 2012, 5:09 am

All times are UTC [ DST ]




Post new topic Reply to topic  [ 22 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: January 9th, 2010, 2:34 am 
Online

Joined: April 8th, 2009, 7:49 pm
Posts: 6073
Location: San Diego, California
We have mousegetpos, winget and a host of other commands.

mousegetpos can tell the window, x,y, control
Winget can tell the active window and a list of windows & controls
with these I can even detect the press of a button on n external program.

Is there a command that allows detecting when a menu item is selected?

We can create a menu in a gui, we can select a menu item, etc

But can we capture the selection, or must that be coded by hand ?

What I am really looking for is script recorder that captures menu and application button clicks .

Any ideas ? Thanks in advance

Leef_me


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: January 9th, 2010, 7:43 am 
Offline

Joined: December 30th, 2005, 5:01 pm
Posts: 448
There's a SetWinEventHook API. Have a look at [tool] WinEventHook Messages.
It doesn't seem to detect a selection though. I'm quite sure there will be a message sent with a selection, but to the program that owns the menu.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: January 11th, 2010, 9:32 pm 
Online

Joined: April 8th, 2009, 7:49 pm
Posts: 6073
Location: San Diego, California
Peter's answer doesn't address my question.

Perhaps a rewording of my question would help:

Does someone have an example that allows detecting when a menu item is selected, in an external program?


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: January 11th, 2010, 11:16 pm 
Offline

Joined: October 9th, 2006, 8:19 pm
Posts: 236
Location: Finland
Leef_me wrote:
...in an external program?

I modified Micha's Get Info from Context Menu to do the job, but as Micha stated it works on...
Quote:
standard windows context menus only!!!

Code:
;constants
MFS_HILITE = 0x80

#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases.
SendMode Input ; Recommended for new scripts due to its superior speed and reliability.
#Persistent
Return

#IfWinExist ahk_class #32768

LButton::
Enter::
NumPadEnter::
WinGet, hwnd, ID, A
WinGet, hMenu, ID, ahk_class #32768

;Get count of menu items
ContextMenCnt := GetContextMenuCount(hMenu)
If ContextMenCnt < 1
{
   Tooltip
   Return
}
item =
Loop %ContextMenCnt%
{
   IsEnabled := GetContextMenuState(hMenu, A_Index-1)
      If (IsEnabled & MFS_HILITE)
         item := A_Index
}
If item
   Tooltip, % GetContextMenuText(hMenu, item - 1)
Else
   ToolTip

If A_ThisHotkey = LButton
   Click Down
Else
   ControlSend, , {%A_ThisHotkey%}, ahk_id %hwnd%
Return

~LButton Up:: Return

/***************************************************************
 * returns the count of menu items
 ***************************************************************
*/
GetContextMenuCount(hMenu)
{
  WinGetClass, WindowClass, ahk_id %hMenu%
  ;All popups should have the window class #32768
  if WindowClass <> #32768
  {
   return 0
  }
  ;Retrieve menu handle from window
  SendMessage, 0x01E1, , , , ahk_id %hMenu%
  ;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(hMenu, Position)
{
  WinGetClass, WindowClass, ahk_id %hMenu%
  if WindowClass <> #32768
  {
   return -1
  }
  SendMessage, 0x01E1, , , , ahk_id %hMenu%
  ;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(48, MenuItemInfo, 0, 4)
  ;Get only Flags from dllcall GetMenuItemInfo MIIM_TYPE = 1
  NumPut(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)
  ; GetMenuItemInfoRes0 := ExtractInteger(MenuItemInfo, 12, false, 4)
  GetMenuItemInfoRes := NumGet(MenuItemInfo, 12)
  GetMenuItemInfoRes0a := NumGet(MenuItemInfo, 12)
  /*
  IsEnabled = 1
  if GetMenuItemInfoRes > 0
     IsEnabled = 0
  return IsEnabled
  */
  return GetMenuItemInfoRes
}

/***************************************************************
 * returns the text of a menu entry (standard windows context menus only!!!)
 ***************************************************************
*/
GetContextMenuText(hMenu, Position)
{
  Global GetMenuItemInfoRes0
  Global GetMenuItemInfoRes0a
  Global GetMenuItemInfoRes1
  Global GetMenuItemInfoRes1a

  WinGetClass, WindowClass, ahk_id %hMenu%
  if WindowClass <> #32768
  {
   return -1
  }
  SendMessage, 0x01E1, , , , ahk_id %hMenu%
  ;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(48, MenuItemInfo, 0, 4)
  ;Retrieve string MIIM_STRING = 0x40 = 64 (/ MIIM_TYPE = 0x10 = 16)
  NumPut(64, MenuItemInfo, 4, 4)
  ;Set type - Get only size of string we need to allocate
  ;NumPut(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)
  ; GetMenuItemInfoRes1 := ExtractInteger(MenuItemInfo, 40, false, 4)
  GetMenuItemInfoRes := NumGet(MenuItemInfo, 40)
  GetMenuItemInfoRes1a := NumGet(MenuItemInfo, 40)
  ;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, 40, 4)
  NumPut(&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
}

PrintScreen::reload

Also, it doesn't work on Explorer's / IE's favorites (it's really not a menu), and some submenus (like Explorer's File -> Send) show "{Empty String}".

It's not perfect: it works if the item is selected by a mouse or Enter key, but not when it's selected by a key, which is underlined in the menu (preceded by an ampersand in the tooltip).

_________________
Pekka Vartto


Last edited by svi on January 12th, 2010, 11:19 am, edited 1 time in total.

Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: January 12th, 2010, 4:06 am 
Online

Joined: April 8th, 2009, 7:49 pm
Posts: 6073
Location: San Diego, California
Thank you svi.

I'll have to study this for a while.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: January 12th, 2010, 8:14 am 
Offline

Joined: December 30th, 2005, 5:01 pm
Posts: 448
Leef_me wrote:
Does someone have an example that allows detecting when a menu item is selected, in an external program?
External programs is not the problem. Perhaps a rewording of my answer :) .
It detects when a standard menu opens and closes on any program, but not when/which a selection is made. But the latter can maybe solved with some hotkeys waiting after the menu opened?!


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: January 12th, 2010, 6:37 pm 
Offline

Joined: October 9th, 2006, 8:19 pm
Posts: 236
Location: Finland
It can be done in much simplier way.
You can use the following script to test which menu item selections [EDIT: actually highlightings] are detected (beep if not) by pressing q (I use that as a hotkey on my test scripts, because it's not used in the Finnish language).
Esc to exit.
Code:
q::

SendMessage, 0x1E1, 0, 0, , ahk_class #32768
hMenu := ErrorLevel
Loop, % DllCall("GetMenuItemCount", "UInt", hMenu)
{
   idx := A_Index - 1
   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)
   VarSetCapacity(mii, 48, 0)
   NumPut(48, mii, 0)
   NumPut(1, mii, 4)
   DllCall("GetMenuItemInfo", "UInt", hMenu, "UInt", idx, "UInt", 1, "UInt", &mii)
   fState := NumGet(mii, 12)
   If (fState & 0x80 And sString)
   {
      ToolTip % sString
      Return
   }
   Else
      ToolTip
}
SoundBeep
Return

Esc:: ExitApp

Detecting mnemonic key usage seems too complicated :?

_________________
Pekka Vartto


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: May 13th, 2010, 9:33 pm 
Offline

Joined: August 21st, 2006, 7:07 pm
Posts: 2925
Location: The Shell
Pekka, very nice way to simplify ;)

Errorlevel returns fail from the message sent.
Working on another approach to conjure the menu.

Iam I to assume that errorlevel should be 32768's handle?

Again, great work.

_________________
Imageparadigm.shift:=(•_•)┌П┐RTFM||^.*∞


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: May 16th, 2010, 11:31 pm 
Offline

Joined: October 9th, 2006, 8:19 pm
Posts: 236
Location: Finland
TLM wrote:
Pekka, very nice way to simplify ;)

Unfortunately I simplified a little too much: the script doesn't show the last character of the item!
Maybe it's line 160 in my first script (originally Micha's):
Code:
159 ;+1 should be enough, we'll use 2
160 GetMenuItemInfoRes += 2
, which should have been left :roll:

Increasing nSize with 1 seems to work:
Code:
q::

SendMessage, 0x1E1, 0, 0, , ahk_class #32768
hMenu := ErrorLevel
Loop, % DllCall("GetMenuItemCount", "UInt", hMenu)
{
   idx := A_Index - 1
   nSize := DllCall("GetMenuString", "UInt", hMenu, "Int", idx, "UInt", 0, "Int", 0, "UInt", 0x400) + 1
   VarSetCapacity(sString, nSize)
   DllCall("GetMenuString", "Uint", hMenu, "Int", idx, "Str", sString, "Int", nSize, "UInt", 0x400)
   VarSetCapacity(mii, 48, 0)
   NumPut(48, mii, 0)
   NumPut(1, mii, 4)
   DllCall("GetMenuItemInfo", "UInt", hMenu, "UInt", idx, "UInt", 1, "UInt", &mii)
   fState := NumGet(mii, 12)
   If (fState & 0x80 And sString)
   {
      ToolTip % sString
      Return
   }
   Else
      ToolTip
}
SoundBeep
Return

Esc:: ExitApp

... but I should really update the script to use GetMenuItemInfo:
MSDN wrote:
[The GetMenuString function has been superseded. Use the GetMenuItemInfo function to retrieve the menu item text.]

I (and hopefully some others, too) tested my script (as all scripts I've posted), but not well enough.
Thanks to SifJar for reporting.

_________________
Pekka Vartto


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: May 18th, 2010, 5:30 pm 
Offline

Joined: August 21st, 2006, 7:07 pm
Posts: 2925
Location: The Shell
I have yet to get the initial message to not return false.
I think it has something to do with the class name.
For some reason the system I work on does not see #327 series classes.

I had to use GetDesktopWindow to get the upper most handle here.
Code:
setformat, integerFast, H
msgbox % dllCall("GetDesktopWindow")+0
Rather than just being able to call it from the classname 32769 :roll:

I'm assuming this is why the message is not working..

_________________
Imageparadigm.shift:=(•_•)┌П┐RTFM||^.*∞


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: May 18th, 2010, 6:55 pm 
Offline

Joined: February 13th, 2010, 3:52 pm
Posts: 175
svi: Does this line:

Code:
SendMessage, 0x1E1, 0, 0, , ahk_class #32768


set error level to the current menu item's handle? I'm trying to understand your code...

EDIT: If you could also explain this line, it'd be handy:
Quote:
If (fState & 0x80 And sString)


I think I understand that it checks if fState and sString exist, but why the " & 0x80 "?

One last query: Why do you need to get the MenuInfo, if it seems it is not used? Why not just GetMenuString, then use it?


Last edited by SifJar on May 18th, 2010, 7:14 pm, edited 1 time in total.

Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: May 18th, 2010, 7:12 pm 
Offline

Joined: August 21st, 2006, 7:07 pm
Posts: 2925
Location: The Shell
SifJar wrote:
svi: Does this line:

Code:
SendMessage, 0x1E1, 0, 0, , ahk_class #32768


set error level to the current menu item's handle? I'm trying to understand your code...

Yes it does if all goes well. It returns the handle for the class name.
If not it returns fail.

_________________
Imageparadigm.shift:=(•_•)┌П┐RTFM||^.*∞


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: May 18th, 2010, 7:18 pm 
Offline

Joined: February 13th, 2010, 3:52 pm
Posts: 175
TLM wrote:
SifJar wrote:
svi: Does this line:

Code:
SendMessage, 0x1E1, 0, 0, , ahk_class #32768


set error level to the current menu item's handle? I'm trying to understand your code...

Yes it does if all goes well. It returns the handle for the class name.
If not it returns fail.


i.e. returns the handle for a menu, generally, not a specific item? OK. Not 100% sure what a handle is, but I think I get the idea. If you happen to know the answers to my other questions I added to my post, it'd be great if you could answer...Thanks for answering that one btw.

EDIT: Also, am I correct in thinking any variable in a struct like this one is 4 bytes long? I'm trying to understand DLL calls here too...


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: May 18th, 2010, 7:43 pm 
Offline

Joined: August 21st, 2006, 7:07 pm
Posts: 2925
Location: The Shell
SifJar wrote:
Not 100% sure what a handle is, but I think I get the idea.
A handle is a HEX ID for a window, menu etc in memory.

SifJar wrote:
EDIT: Also, am I correct in thinking any variable in a struct like this one is 4 bytes long? I'm trying to understand DLL calls here too...


To understand structs you should 1st understand pointers (text from C++ ebook).

To your question of adding variables to a struct, yes each member can be added per 4bytes like this:
Code:
; Rect struct
VarSetCapacity( R, 16, 0 )
NumPut( x1, &R+0 )
NumPut( y1, &R+4 )
NumPut( x2, &R+8 )
NumPut( y2, &R+12)

Just remember to call the pointer from memory with a preceding &.

_________________
Imageparadigm.shift:=(•_•)┌П┐RTFM||^.*∞


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: May 18th, 2010, 7:56 pm 
Offline

Joined: February 13th, 2010, 3:52 pm
Posts: 175
TLM wrote:

To your question of adding variables to a struct, yes each member can be added per 4bytes like this:
Code:
; Rect struct
VarSetCapacity( R, 16, 0 )
NumPut( x1, &R+0 )
NumPut( y1, &R+4 )
NumPut( x2, &R+8 )
NumPut( y2, &R+12)

Just remember to call the pointer from memory with a preceding &.


Instead of writing say
Code:
NumPut( x1, &R+0 )
could you also use
Code:
NumPut( x1, &R, 0 )

:?: This seems to be what svi did in his code, when adding members to "mii" (MenuItemInfo), are they pretty much the same thing, or is there an important difference?

EDIT: Also, he didn't have the preceding &, he just had the variable name, and according the to help file, that works better...

EDIT: I 'm fairly sure I understand pointers now, do you know of a good reference for structs?

EDIT: I found some stuff, I think I kinda get the idea.A struct contains a set of members and each member is a pointer to a variable, am I right?


Report this post
Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 22 posts ]  Go to page 1, 2  Next

All times are UTC [ DST ]


Who is online

Users browsing this forum: Google Feedfetcher and 74 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