[Solved] OnMessage(WM_INITPOPUPMENU) doesn't work

Get help with using AutoHotkey and its commands and hotkeys
wz520
Posts: 28
Joined: 01 Jan 2014, 21:32
Location: China

[Solved] OnMessage(WM_INITPOPUPMENU) doesn't work

08 Jun 2016, 07:26

Hi,

I want to enable/disable some items before a menu is about to be shown.

I know how to do this in a classic Win32 application: Process the WM_INITPOPUPMENU message.
This works in my C++ application.

But when I set a message handler for WM_INITMENUPOPUP using OnMessage() in an AHK script, it seems not work.

Here is my test script:

Code: Select all

#NoEnv

WM_INITMENUPOPUP(wParam, lParam, msg, hwnd) {
	listvars
}

WM_CONTEXTMENU(wParam, lParam, msg, hwnd) {
	Menu, mymenu, Show
}

Menu, mymenu, Add, exit1, GuiClose
Menu, mymenu, Add, exit2, GuiClose
Gui, Show, w400 h300, Title

OnMessage(0x117, "WM_INITMENUPOPUP")
OnMessage(0x7B, "WM_CONTEXTMENU")

return


GuiClose:
GuiEscape:
	ExitApp
When I right click the mouse button, "mymenu" is shown, but "listvars" is never called, neither "MsgBox" is called if I change "listvars" to "msgbox".

I am using the currently latest version: v1.1.24.00

Any help please? :?:
Last edited by wz520 on 11 Jun 2016, 04:44, edited 1 time in total.
User avatar
lifeweaver
Posts: 144
Joined: 10 May 2014, 05:57
GitHub: lifeweaver
Location: OH

Re: OnMessage(WM_INITPOPUPMENU) doesn't work

08 Jun 2016, 13:18

Hi wz520,

Looks like you're not the only one that has had this problem, here is a link in which Chris metions:
Chris wrote: When the GUI window procedure receives WM_ENTERMENULOOP from any menu (even one the script displays via DllCall), the script is marked uninterruptible. Consequently, messages below 0x312 like WM_MENUSELECT are not monitored (as mentioned in the docs).

A possible workaround is to add an OnMessage handler for WM_ENTERMENULOOP that returns a number to prevent AutoHotkey from seeing the message.
Here is an implemention for his suggested workaround:

Code: Select all

OnMessage(0x211, "WM_ENTERMENULOOP")

WM_ENTERMENULOOP(){
Return 100
}
I recomend to try searching on some keywords when you run into trouble, i.e. 'AutoHotkey onmessage WM_INITMENUPOPUP'
wz520
Posts: 28
Joined: 01 Jan 2014, 21:32
Location: China

Re: OnMessage(WM_INITPOPUPMENU) doesn't work

08 Jun 2016, 22:07

Thanks for your reply.

Unfortunately, this doesn't work for me...

I have read the thread you mentioned, and another thread referred by that thread. Then I changed the script to something like this:

Code: Select all

#NoEnv
 
WM_INITMENUPOPUP(wParam, lParam, msg, hwnd) {
	MsgBox
}
 
WM_CONTEXTMENU(wParam, lParam, msg, hwnd) {
	Menu, mymenu, Show
}

WM_ENTERMENULOOP(){
Return 100
}
 
Menu, mymenu, Add, exit1, GuiClose
Menu, mymenu, Add, exit2, GuiClose
Gui, Show, w400 h300, Title
 
OnMessage(0x211, "WM_ENTERMENULOOP")
OnMessage(0x117, "WM_INITMENUPOPUP")
OnMessage(0x7B, "WM_CONTEXTMENU")
return


 
GuiClose:
GuiEscape:
	ExitApp
But no "msgbox" shows when I right click the mouse inside the GUI window.

However, a 'msgbox' appears by this way:
1. Double click the tray icon
2. The AHK's main window is opened
3. Click any menu in the main window
4. A 'msgbox' appears.

So it seems that the main window receives the message, but not the window created by GUI command.

I also tried:
1. Put "Critical" at the beginning of WM_ENTERMENULOOP()
2. Undo step 1, put "Critical" at the beginning of WM_INITPOPUPMENU()
3. Delete "OnMessage(0x7B, "WM_CONTEXTMENU")", Add a global hotkey label to show mymenu, like "x::Menu, mymenu, Show"

All of above steps didn't work(no 'msgbox' appears)...
User avatar
lifeweaver
Posts: 144
Joined: 10 May 2014, 05:57
GitHub: lifeweaver
Location: OH

Re: OnMessage(WM_INITPOPUPMENU) doesn't work

09 Jun 2016, 08:26

Now that I think on it, I believe AutoHotkey doesn't use/ignores those messages when it's handling the menu.
I can't remember where I saw the information though.

Try the below to illistrate me point:

Code: Select all

#NoEnv
 
WM_INITMENUPOPUP(wParam, lParam, msg, hwnd) {
	MsgBox
}
 
WM_CONTEXTMENU(wParam, lParam, msg, hwnd) {
	Menu, mymenu, Show
}
 
WM_ENTERMENULOOP(){
Return 100
}
 
;~ Menu, mymenu, Add, exit1, GuiClose
;~ Menu, mymenu, Add, exit2, GuiClose
Gui, Show, w400 h300, Title
 
OnMessage(0x211, "WM_ENTERMENULOOP")
OnMessage(0x117, "WM_INITMENUPOPUP")
OnMessage(0x7B, "WM_CONTEXTMENU")
return
 
 
 
GuiClose:
GuiEscape:
	ExitApp
When you rightclick you'll see an error on 'Menu, mymenu, Show'.
I it looks like 'WM_INITMENUPOPUP' is not sent for all menus.
just me
Posts: 6522
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: OnMessage(WM_INITPOPUPMENU) doesn't work

09 Jun 2016, 08:34

wz520 wrote:I want to enable/disable some items before a menu is about to be shown.
So why don't you do it using the Menu command?
wz520
Posts: 28
Joined: 01 Jan 2014, 21:32
Location: China

Re: OnMessage(WM_INITPOPUPMENU) doesn't work

09 Jun 2016, 23:34

Sorry but maybe you misunderstood something. The WM_CONTEXTMENU message is sent when the user rightclicks in a window, not when a menu is opened.

I have no idea about your script. You commented out the menu creation commands, so when the user rightclicks the window,
WM_CONTEXTMENU() is called, then since no menu called "mymenu" is created, the error occurs...anything weird? :wtf:
wz520
Posts: 28
Joined: 01 Jan 2014, 21:32
Location: China

Re: OnMessage(WM_INITPOPUPMENU) doesn't work

09 Jun 2016, 23:34

just me wrote:
wz520 wrote:I want to enable/disable some items before a menu is about to be shown.
So why don't you do it using the Menu command?
Sorry, but I don't know how to do it...any idea please? :shock:
just me
Posts: 6522
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: OnMessage(WM_INITPOPUPMENU) doesn't work

10 Jun 2016, 00:49

Enable, MenuItemName: Allows the user to once again select MenuItemName if was previously disabled (grayed).

Disable, MenuItemName: Changes MenuItemName to a gray color to indicate that the user cannot select it.

ToggleEnable, MenuItemName: Disables MenuItemName if it was previously enabled; otherwise, enables it.

Source: Add or Change Items in a Menu
wz520
Posts: 28
Joined: 01 Jan 2014, 21:32
Location: China

Re: OnMessage(WM_INITPOPUPMENU) doesn't work

10 Jun 2016, 02:23

just me wrote:
Enable, MenuItemName: Allows the user to once again select MenuItemName if was previously disabled (grayed).

Disable, MenuItemName: Changes MenuItemName to a gray color to indicate that the user cannot select it.

ToggleEnable, MenuItemName: Disables MenuItemName if it was previously enabled; otherwise, enables it.

Source: Add or Change Items in a Menu

I know this. The point is: I want to do these things when a menu is about to be shown.

Like this:
1. The line "Menu, mymenu, Show" is read by AHK.
2. AHK calls proper Windows APIs to complete this request(Maybe using the TrackPopupMenu() API)
3. (I want to do something here)
4. The menu is actually shown on the screen.

WM_INITMENUPOPUP does this work well in my C++ application, but runs into touble with AHK.
User avatar
lifeweaver
Posts: 144
Joined: 10 May 2014, 05:57
GitHub: lifeweaver
Location: OH

Re: OnMessage(WM_INITPOPUPMENU) doesn't work

10 Jun 2016, 06:31

wz520 wrote:Sorry but maybe you misunderstood something. The WM_CONTEXTMENU message is sent when the user rightclicks in a window, not when a menu is opened.

I have no idea about your script. You commented out the menu creation commands, so when the user rightclicks the window,
WM_CONTEXTMENU() is called, then since no menu called "mymenu" is created, the error occurs...anything weird? :wtf:
I pointed out that
Lifeweaver wrote: Now that I think on it, I believe AutoHotkey doesn't use/ignores those messages when it's handling the menu.
I can't remember where I saw the information though.
Ergo the OnMessage isn't goning to work with the menu in this case, I wanted to show that the OnMessage does work when you're not using having AutoHotkey create the menu.
qwerty12
Posts: 468
Joined: 04 Mar 2016, 04:33
GitHub: qwerty12

Re: OnMessage(WM_INITPOPUPMENU) doesn't work

10 Jun 2016, 09:42

Hi,

Commenting out the

Code: Select all

if (!INTERRUPTIBLE_IN_EMERGENCY)
		return false;
lines in MsgMonitor() inside AutoHotkey_L's application.cpp is actually enough to make OnMessage(WM_INITMENUPOPUP) work. INTERRUPTIBLE_IN_EMERGENCY checks to see if a menu is being displayed at that point, which what lifeweaver was demonstrating. I won't embarrass myself by pretending to understand why it's there - I assume its presence ensures AHK's excellent stability and it's there for a good reason I can't comprehend because of my limited knowledge - but here's a workaround that doesn't require a recompile of AutoHotkey. I can't promise it doesn't bring in any side effects - I've not tested this long enough to check (and GUIs aren't my thing anyway).

Code: Select all

#NoEnv

WM_INITMENUPOPUP(wParam, lParam, msg, hwnd) {
	Menu, mymenu, Disable, 1&
}

CallWndProc(nCode, wParam, lParam)
{
	if (nCode >= 0) ; HC_ACTION
	{
		static msgOffset := 8 * (A_PtrSize == 8 ? 2 : 1)
		static hwndOffset := 12 * (A_PtrSize == 8 ? 2 : 1)
		cwpMessage := NumGet(lParam+0, msgOffset, "UInt")
		cwpHwnd := NumGet(lParam+0, hwndOffset, "Ptr")
		if (cwpMessage == 0x117 && cwpHwnd == A_ScriptHwnd)
		{
			static wParamOffset := 4 * (A_PtrSize == 8 ? 2 : 1)
			cwpLparam := NumGet(lParam+0,, "UPtr") ; after looking at the WPARAM and LPARAM typedefs, I figured UPtr was the closest, but I could be wrong
			cwpWparam := NumGet(lParam+0, wParamOffset, "UPtr")

			WM_INITMENUPOPUP(cwpWparam, cwpLparam, cwpMessage, cwpHwnd)
			; assuming 0 returned; pass onto next hook
		}
	}
	return DllCall("CallNextHookEx", "Ptr", 0, "Int", nCode, "UPtr", wParam, "UPtr", lParam, "UPtr")
}
 
WM_CONTEXTMENU(wParam, lParam, msg, hwnd) {
	Menu, mymenu, Show
}

Menu, mymenu, Add, exit1, GuiClose
Menu, mymenu, Add, exit2, GuiClose
Gui, Show, w400 h300, Title
 
;OnMessage(0x117, "WM_INITMENUPOPUP")
; WH_CALLWNDPROCRET may be better, but as far as I know, in this case, there's no noticable difference
hook := DllCall("SetWindowsHookEx", "Int", WH_CALLWNDPROC := 4, "Ptr", wndlpfn := RegisterCallback("CallWndProc", ""), "Ptr", 0, "UInt", DllCall("GetCurrentThreadId", "UInt"), "Ptr")
OnMessage(0x7B, "WM_CONTEXTMENU")
return
 
 
GuiClose:
GuiEscape:
	if (hook)
		DllCall("UnhookWindowsHookEx", "Ptr", hook), hook := 0
	if (wndlpfn)
		DllCall("GlobalFree", "Ptr", wndlpfn, "Ptr"), wndlpfn := 0
	ExitApp
EDIT: I do have to echo just me's thoughts after looking beyond the topic's title: if you know when you're going to call Menu, Show to show the menu, why not just do Menu, Disable before that? You don't need to listen to WM_INITMENUPOPUP then in the first place...
wz520
Posts: 28
Joined: 01 Jan 2014, 21:32
Location: China

Re: OnMessage(WM_INITPOPUPMENU) doesn't work

11 Jun 2016, 04:43

qwerty12 wrote:Hi,

Commenting out the

Code: Select all

if (!INTERRUPTIBLE_IN_EMERGENCY)
		return false;
lines in MsgMonitor() inside AutoHotkey_L's application.cpp is actually enough to make OnMessage(WM_INITMENUPOPUP) work. INTERRUPTIBLE_IN_EMERGENCY checks to see if a menu is being displayed at that point, which what lifeweaver was demonstrating. I won't embarrass myself by pretending to understand why it's there - I assume its presence ensures AHK's excellent stability and it's there for a good reason I can't comprehend because of my limited knowledge - but here's a workaround that doesn't require a recompile of AutoHotkey. I can't promise it doesn't bring in any side effects - I've not tested this long enough to check (and GUIs aren't my thing anyway).
Hi, qwerty12, I tested your script and it works well. Thank you so much!

Actually, I finally figured out that if I was using menu bar, the solution would be simpler: WM_INITMENU & WM_INITMENUPOPUP msgs not monitored
Chris wrote: Chances are, the messages for the system (icon) menu are being sent to some other window than the GUI window (perhaps some system/child window), assuming they are being sent at all. AutoHotkey contains no special handling for WM_INITMENU/WM_INITMENUPOPUP anywhere, so I can't think of any reason you wouldn't be able to monitor them.

Perhaps if you created a menu bar for the GUI window, the act of accessing the menu bar would produce a message that could be monitored via OnMessage.
So I tried to create a menu bar on a GUI window:

Code: Select all

#NoEnv
 
WM_INITMENUPOPUP(wParam, lParam, msg, hwnd) {
	MsgBox
}
 
WM_CONTEXTMENU(wParam, lParam, msg, hwnd) {
	; Menu, mymenu, Show
}
 
WM_ENTERMENULOOP(){
Return 100
}
 
; Menu, mymenu, Add, exit1, GuiClose
; Menu, mymenu, Add, exit2, GuiClose

; from AHK help
Menu, FileMenu, Add, &Open`tCtrl+O, GuiClose
Menu, FileMenu, Add, E&xit, GuiClose
Menu, HelpMenu, Add, &About, GuiClose
Menu, MyMenuBar, Add, &File, :FileMenu
Menu, MyMenuBar, Add, &Help, :HelpMenu
Gui, Menu, MyMenuBar

Gui, Show, w400 h300, Title
 
OnMessage(0x211, "WM_ENTERMENULOOP")
OnMessage(0x117, "WM_INITMENUPOPUP")
OnMessage(0x7B, "WM_CONTEXTMENU")
return
 
GuiClose:
GuiEscape:
	ExitApp
Just create a menu bar based on the test script I wrote, and it works!


qwerty12 wrote: EDIT: I do have to echo just me's thoughts after looking beyond the topic's title: if you know when you're going to call Menu, Show to show the menu, why not just do Menu, Disable before that? You don't need to listen to WM_INITMENUPOPUP then in the first place...
It was just a test script. I just wanted to figure out why the message handler won't be called. now I get it with your help, thank you!
qwerty12
Posts: 468
Joined: 04 Mar 2016, 04:33
GitHub: qwerty12

Re: OnMessage(WM_INITPOPUPMENU) doesn't work

11 Jun 2016, 08:49

Hey,
wz520 wrote:[It was just a test script. I just wanted to figure out why the message handler won't be called. now I get it with your help, thank you!
Ah, fair enough. Thanks for coming back and sharing a far nicer fix!

EDIT: Lexikos, thank you very much for taking the time to explain in detail why AutoHotkey does what it does in that situation. I apologise for just editing this post, esp. considering the time you put into that reply, but since there's not anything for me to say (I did learn a lot, though), a reply with just "thanks" seems like a waste of time for all involved...
Last edited by qwerty12 on 12 Jun 2016, 06:45, edited 1 time in total.
lexikos
Posts: 6653
Joined: 30 Sep 2013, 04:07
GitHub: Lexikos

Re: OnMessage(WM_INITPOPUPMENU) doesn't work

11 Jun 2016, 18:31

qwerty12 wrote:INTERRUPTIBLE_IN_EMERGENCY checks to see if a menu is being displayed at that point, which what lifeweaver was demonstrating. I won't embarrass myself by pretending to understand why it's there - ...
The reasons aren't very clear, but the main one in this case is:

Launching a new thread while a menu is visible "would produce strange effects because any timed subroutine that took a long time to run might keep us away from the "menu loop", which would result in the menu becoming temporarily unresponsive while the user is in it (and probably other undesired effects)."
There are other reasons for g_MenuIsVisible (which is usually set by the WM_ENTERMENULOOP handler and Menu command):

AutoHotkey implements "buffering" of messages when the thread is uninterruptible (e.g. Critical) by filtering which messages it retrieves from the message queue. If some code outside our control (e.g. TrackPopupMenu) is running a message loop, this filtering doesn't happen and the message is immediately dispatched to the window procedure. There are two ways that such messages can be handled:
  1. Drop the message.
  2. Re-post the message to the message queue.
If the message is re-posted but there's a message loop running outside our control, the message will just be dispatched back to the window procedure. This can cause a loop which can lock up the GUI or otherwise hurt performance. The comments in the source code describe it as a "bouncing effect":
"v1.0.36.03: The g_MenuIsVisible check was added as a means to discard the message. Otherwise MSG_FILTER_MAX would result in a bouncing effect or something else that disrupts a popup menu, namely a context menu shown by an AltSubmit ListView (regardless of whether it's shown by GuiContextMenu or in response to a RightClick event)."

The mouse hook uses g_MenuIsVisible as a shortcut for detecting menus, for correct handling of mouse buttons in some specific cases. However, if g_MenuIsVisible is false it will double-check by searching for a menu window anyway.

The keyboard hook uses g_MenuIsVisible when collecting input to pass the correct flags to ToUnicodeEx/ToAsciiEx. I'm not sure what effect the flag has.
g_MenuIsVisible is set by the WM_ENTERMENULOOP handler and the Menu command. Therefore, if you block the message and bypass the Menu command, AutoHotkey won't know there's a message open, so the safety measures and other things that rely on detecting menus won't work, but you'll be able to launch new threads.
wz520 wrote:I know this. The point is: I want to do these things when a menu is about to be shown.
You want to do things the difficult and risky way? Why?

Since you were directly showing the menu yourself, it would have made a lot more sense to just make your changes to the menu before showing it.

Return to “Ask For Help”

Who is online

Users browsing this forum: AHKStudent, johnliem, leo007, Mike_Fay, ShadowMaster85, yezilaoda and 72 guests