Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

Dropped messages


  • Please log in to reply
7 replies to this topic
majkinetor
  • Moderators
  • 4512 posts
  • Last active: May 20 2019 07:41 AM
  • Joined: 24 May 2006
Ok, we discussed this already several times.

It appears that AHK doesn't report all messages if you used OnMessage.

I will supply here example code.
It is implementation of Menu command via API functions to support some things not currently supported by AHK - modalless menus, menu item identifieers, menu iteration, transparency, adding menu items on arbitrary position (AHK just appends), solving problems with menus loosing focus, adding tooltips to menu items etc..

How to see the problem:
Run the menuTest.ahk script and start to monitor messages of main GUI via Winspector. When you display the menu and move selection, the GUI will receive WM_MENUSELECT message and Winspector will catch it. AHK hower doesn't. I tried all messages generated by menu and non of them works.

MenuTest.ahk
#SingleInstance, force
	WM_MENUSELECT	= 0x11F
	OnMessage(WM_MENUSELECT, "OnMenuMessage")

	Gui, Add, Text, ,This gui is menu parent`nPress ALT m to open menu
	Gui, +LastFound +Toolwindow
	G := WinExist()
	Gui, Show, h200 w200
return


!m::
;create menu and insert some items
	myMenu := Menu_Create()
	Menu_Insert(myMenu, "API_item1", 1)
	Menu_Insert(myMenu, "API_item2", 2)
	Menu_Insert(myMenu, "API_item3", 3, 0)

;create submenu
	submenu := Menu_Create()
	Menu_Insert(submenu, "submenu item", 4)
	Menu_InsertSub(myMenu, submenu, "submenu", 0)
	
	Menu_SetModeLess( myMenu )
	
	Menu_Show(myMenu, 100, 100)
	ToolTip, non modal menu is shown
return



OnMenuMessage()
{
	MsgBox, Message received
}


;------------------------------------------------------------------------
ExtractInteger(ByRef pSource, pOffset = 0, pIsSigned = false, pSize = 4)
{
	Loop %pSize%  ; Build the integer by adding up its bytes.
		result += *(&pSource + pOffset + A_Index-1) << 8*(A_Index-1)
	if (!pIsSigned OR pSize > 4 OR result < 0x80000000)
		return result  ; Signed vs. unsigned doesn't matter in these cases.
	return -(0xFFFFFFFF - result + 1)
}

InsertInteger(pInteger, ByRef pDest, pOffset = 0, pSize = 4)
{
	Loop %pSize%  ; Copy each byte in the integer into the structure as raw binary data.
		DllCall("RtlFillMemory", "UInt", &pDest + pOffset + A_Index-1, "UInt", 1, "UChar", pInteger >> 8*(A_Index-1) & 0xFF)
}


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

Menu_SetModeLess( pHandle )
{
	VarSetCapacity(sMENUINFO, 28, 0)
	InsertInteger(28, sMENUINFO, 0)				;cbsize
	InsertInteger(0x10, sMENUINFO, 4)			;fmask MIM_STYLE=0x10
	InsertInteger(0x40000000, sMENUINFO, 8)		;MNS_DRAGDROP=0x20000000	 MNS_MODELESS = 0x40000000  MNS_NOTIFYBYPOS=0x8000000

	return DllCall("SetMenuInfo", "uint", pHandle, "uint", &sMENUINFO)
}

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

Menu_GetItemCount( pHandle )
{
	return DllCall("GetMenuItemCount", "uint", pHandle)
}

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

Menu_Destroy( pHandle )
{
	DllCall("DestroyMenu", "uint", pHandle)
}


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

Menu_Delete( pHandle, pItem)
{
	DllCall("DeleteMenu"
			,"uint", pHandle
			,"uint", pItem
			,"uint", 0x400)
}

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

Menu_Show( pHandle, pX, pY )
{
	global
	TPM_RETURNCMD := 0x100
	res := DllCall("TrackPopupMenu"
					, "uint", pHandle
					, "uint", TPM_RETURNCMD
					, "int", pX
					, "int", pY
					, "uint", 0
					, "uint", G
					, "uint", 0)
	return, %res%
}

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

Menu_Insert( pHandle, pTitle, pId, pPos=-1)
{
	res := DllCall("InsertMenu"
				,"uint", pHandle
				,"uint", pPos
				,"uint", 0x400
				,"uint", pId
				,"str",  pTitle)
}	
	
;------------------------------------------------------
Menu_InsertSub( pHandle, pSubHandle, pTitle, pPos=-1)
{
	res := DllCall("InsertMenu"
				,"uint", pHandle
				,"uint", pPos
				,"uint", 0x410
				,"uint", pSubHandle
				,"str",  pTitle)

}

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

Menu_Create()
{
	return DllCall("CreatePopupMenu")
}

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

Menu_GetAHKMenuId( pMenu )
{

	Menu, menuDummy, Add 
	Menu, menuDummy, DeleteAll 
       
	Gui, 99:Menu, menuDummy 
	Gui, 99:Show, Hide, guiDummy 

	old_DetectHiddenWindows := A_DetectHiddenWindows 
	DetectHiddenWindows, on 
	Process, Exist 
	h_menuDummy := DllCall( "GetMenu", "uint", WinExist( "guiDummy ahk_class AutoHotkeyGUI ahk_pid " ErrorLevel ) ) 
	DetectHiddenWindows, %old_DetectHiddenWindows% 

	Gui, 99:Menu 
      Gui, 99:Destroy 

	Menu, menuDummy, Add, :%pMenu% 
    
	h_menu := DllCall( "GetSubMenu", "uint", h_menuDummy, "int", 0 ) 
	success := DllCall( "RemoveMenu", "uint", h_menuDummy, "uint", 0, "uint", 0x400 ) 
	Menu, menuDummy, Delete, :%pMenu% 

	return h_menu
}

Posted Image

majkinetor
  • Moderators
  • 4512 posts
  • Last active: May 20 2019 07:41 AM
  • Joined: 24 May 2006
Interesting thing just happened.

The message is received this way:

1. Start the script
2. Display menu (press ALT m)
3. Right click AHK tray icon
4. Press ESC to close the menu

You will receive the message.
I guess this is AHK tray menu interuppting but what is strange is that I don't get this if I don't call the shortcut....



EDIT:
Interesting thing again...
This message is received: WM_ENTERMENULOOP = 0x211
Posted Image

Chris
  • Administrators
  • 10727 posts
  • Last active:
  • Joined: 02 Mar 2004
I'm not sure, but I think this is all due to the following from the help file:

If a monitored message that is numerically less than 0x312 arrives while the script is absolutely uninterruptible -- such as while a menu is displayed, a KeyDelay/MouseDelay is in progress, or the clipboard is being opened -- the message's function will not be called and the message will be treated as unmonitored. By contrast, a monitored message of 0x312 or higher will be buffered during these uninterruptible periods; that is, its function will be called when the script becomes interruptible.

I'm planning to provide a "Menu AllowInterrupt" command or similar that will allow a script to get around this. However, it requires some careful analysis and testing.

majkinetor
  • Moderators
  • 4512 posts
  • Last active: May 20 2019 07:41 AM
  • Joined: 24 May 2006
I see that you didn't try the script.
Like I said, I don't use AHK Menu command, but I created API interface.
I also set it to be modalless, and commands after Menu_Show are executed imediatelly (pop up in this example). So, I really don't know what is blocking the script although all signs tell me that it is blocked (like, there is no right click menu on the AHK tray icon).

In this script, menu is displayed non modal, and it stay open like any other GUI. Commands after showing the menu are executed, so script stops on return. At that moment, the script should be iddle AFAIK but for some reason its not. Menu is shown using TrackPopUpMenu witch will display the menu non-modal, and send related messages to GUI in the centar of the screen. So I am very interested to know what blocked the script and how it can be fixed (is it possible to set threed priority somewhere for instance)
Posted Image

ahklerner
  • Members
  • 1386 posts
  • Last active: Oct 08 2014 10:29 AM
  • Joined: 26 Jun 2006

(is it possible to set threed priority somewhere for instance)


http://www.autohotke...ands/Thread.htm

Thread, Priority, n: Specify for n an integer between -2147483648 and 2147483647 (or an expression) to indicate the current thread's new priority. This has no effect on other threads. See Threads for details.

Due to its ability to buffer events, the command "Critical" is generally superior to "Thread Priority".

On a related note, the OS's priority level for the entire script can be changed as in this example: Process, Priority,, High



majkinetor
  • Moderators
  • 4512 posts
  • Last active: May 20 2019 07:41 AM
  • Joined: 24 May 2006
I know that.

But where do I put the command in above script ?
Posted Image

Chris
  • Administrators
  • 10727 posts
  • Last active:
  • Joined: 02 Mar 2004
Thanks for being persistent. I'd overlooked the fact that you aren't using built-in AHK menus. I think I see the problem now:

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. However, allowing script threads to run during the display of a menu also allows the program's main event loop to process messages intended for TrackPopupMenu's internal use. This can cause the menu to fail to respond properly to mouse clicks (and possibly keystrokes), though the effect might be hardly noticeable if the your OnMessage function finishes quickly.

I'm planning to test out a solution in which mouse click messages are postponed whenever a menu is displayed so that TrackPopupMenu() will receive them when it regains control of the thread (it loses control when a script thread is launched, such as a timer or OnMessage function).

Thanks.

majkinetor
  • Moderators
  • 4512 posts
  • Last active: May 20 2019 07:41 AM
  • Joined: 24 May 2006

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

Hej, this acctually works :D
With this even acctual AHK menus can be set to be non modal. It can be even done with functions in the script above :

OnMessage(WM_ENTERMENULOOP, "OnMenuLoop")
...
Menu_SetModeLess(  Menu_GetAHKMenuId( myAHKmenuName) )
...
OnMenuLoop()
{
	return 100
}

Then people can watch various menu messages.
Posted Image