StrSplit MaxParts and WinMenuSelectItem sysmenu

Report problems with documented functionality
User avatar
jeeswg
Posts: 5129
Joined: 19 Dec 2016, 01:58
Location: UK

StrSplit MaxParts and WinMenuSelectItem sysmenu

Post by jeeswg » 10 Jan 2018, 09:54

Re.
Preview of v1.1.28.00 - AutoHotkey Community
https://autohotkey.com/boards/viewtopic ... 24&t=42628

- StrSplit with positive MaxParts but no delimiter, appears to do StrSplit as normal, as if MaxParts were equal to -1.
- WinMenuSelectItem applied to the sysmenu, works once, but not multiple times on a window, it appears that control of the sysmenu is not being reverted to the window.
E.g. DllCall("user32\GetSystemMenu", Ptr,hSysMenu, Int,1, Ptr) to revert.
E.g. you can see that the sysmenu looks 'old school', after applying WinMenuSelectItem.
[EDIT:] It appears that the problem occurs when the script is closed/reloaded.

- General test code applied to v1.1.28.00 preview:

Code: Select all

;ERROR: StrSplit MaxParts: when no delimiter
;ERROR: WinMenuSelectItem sysmenu: doesn't revert

;Hotstring(":*:abc", "hello world")

;#Include C:\Users\%A_UserName%\Desktop\z include.txt
;file contains:
;MsgBox, % "hey"

;ListLines, On
;MsgBox, % A_ListLines
;ListLines, Off
;MsgBox, % A_ListLines

;MsgBox, % A_ComSpec

;:*E:abc::MsgBox

;MaxParts = 5: 4 items and 1 guff item
;oTemp := StrSplit("a,b,c,d,e,f,g,h,i,j", ",",, 5)
;MsgBox, % oTemp.4
;MsgBox, % oTemp.5
;MsgBox, % oTemp.Length()
;oTemp := StrSplit("abcdefghij",,, 5)
;MsgBox, % oTemp.4
;MsgBox, % oTemp.5
;MsgBox, % oTemp.6
;MsgBox, % oTemp.Length()

;vDir1 := A_ScriptDir
;Loop, Files, % vDir1 "\*", F
;{
;	vPath := A_LoopFilePath
;	MsgBox, % vPath
;}

;#InstallKeybdHook
;#InstallMouseHook
;Loop, 1000
;{
;	ToolTip, % A_TimeIdleKeyboard "`r`n" A_TimeIdleMouse
;	Sleep, 1000
;}

;WinWaitActive, ahk_class Notepad
;Sleep, 2000
;WinMenuSelectItem, A,, 0&, Maximize
;SoundBeep
- @lexikos: Thank you for these updates.
lexikos
Posts: 6175
Joined: 30 Sep 2013, 04:07
GitHub: Lexikos

Re: StrSplit MaxParts and WinMenuSelectItem sysmenu

Post by lexikos » 12 Jan 2018, 05:15

jeeswg wrote:StrSplit with positive MaxParts but no delimiter, appears to do StrSplit as normal, as if MaxParts were equal to -1.
That is because I haven't implemented it yet. Thanks for reminding me.
WinMenuSelectItem applied to the sysmenu, works once, but not multiple times on a window, it appears that control of the sysmenu is not being reverted to the window.
I believe this is not true if you follow the advice in the documentation.
If the window does not already have a custom system menu, a copy of the standard system menu will be created as a side effect. Therefore, avoid using 0& for the standard items which appear on all windows.
Source: The included documentation.
As for what happens if the window does not have a custom sysmenu,
I would say it is by design: Every window with the WS_SYSMENU style has a system menu, but it’s not there until it needs to be – The Old New Thing

If GetSystemMenu() has never been called for that window, it doesn't have a system menu. When you call it for the first time, GetSystemMenu() creates the menu. It may assign ownership to the calling process, which in your case is AutoHotkey. Or it may not.
Source: DllCall: Calling GetSystemMenu() on an external program will corrupt its System Menu when the Script exits - AutoHotkey Community
When your script exits, the menu it created is destroyed. Subsequent calls to GetSystemMenu return the same (now invalid) handle. The window manager is presumably not designed to handle this because a window and its system menu should be owned by the same process. As far as I can tell, there is no way to detect:
  • which process owns a menu handle;
  • whether GetSystemMenu created a new menu; or
  • whether a window has a custom system menu.
I may clarify the consequences in the documentation, or I may reconsider including this feature.
User avatar
jeeswg
Posts: 5129
Joined: 19 Dec 2016, 01:58
Location: UK

Re: StrSplit MaxParts and WinMenuSelectItem sysmenu

Post by jeeswg » 12 Jan 2018, 05:27

- Thanks for the information.
- I had tried to create a variant of MenuSelect in the past, that could also handle the sysmenu. I'm now unsure whether my function can also fail under certain conditions.
- I like the 0& idea, it would have saved me 1 parameter.
- I now believe the comment about trying it multiple times to be untrue, it was probably the reloading/closing that caused the problem.

Code: Select all

;vOpt: a - apply this to the sysmenu
;vOpt: x - do not invoke item
JEE_MenuSelect(hWnd, vOpt, oArray*)
{
	if !hWnd
		hWnd := WinExist("A")
	if InStr(vOpt, "a")
		hMenu := DllCall("user32\GetSystemMenu", Ptr,hWnd, Int,0, Ptr)
	else
		hMenu := DllCall("user32\GetMenu", Ptr,hWnd, Ptr)
	Loop, % oArray.Length()
	{
		vIndex2 := A_Index, vIsMatch := 0, vTemp := oArray[A_Index]
		vCount := DllCall("user32\GetMenuItemCount", Ptr,hMenu)
		if RegExMatch(vTemp, "^\d+&$")
		{
			vIndex := SubStr(vTemp, 1, -1) - 1
			if (vIndex >= vCount)
				return InStr(vOpt, "a") ? 0*DllCall("user32\GetSystemMenu", Ptr,hWnd, Int,1, Ptr) : 0
			vID := DllCall("user32\GetMenuItemID", Ptr,hMenu, Int,vIndex, UInt)
			if (vID = 0)
			|| ((vID = 0xFFFFFFFF) && (A_Index = oArray.Length()))
			|| (!(vID = 0xFFFFFFFF) && !(A_Index = oArray.Length()))
				return InStr(vOpt, "a") ? 0*DllCall("user32\GetSystemMenu", Ptr,hWnd, Int,1, Ptr) : 0
			vIsMatch := 1
		}
		else
		{
			vTemp := StrReplace(vTemp, "&")
			Loop, % vCount
			{
				vIndex := A_Index-1
				vID := DllCall("user32\GetMenuItemID", Ptr,hMenu, Int,vIndex, UInt)
				if (vID = 0)
				|| ((vID = 0xFFFFFFFF) && (vIndex2 = oArray.Length()))
				|| (!(vID = 0xFFFFFFFF) && !(vIndex2 = oArray.Length()))
					continue
				vChars := DllCall("user32\GetMenuString", Ptr,hMenu, UInt,vIndex, Ptr,0, Int,0, UInt,0x400) + 1
				VarSetCapacity(vText, vChars << !!A_IsUnicode)
				DllCall("user32\GetMenuString", Ptr,hMenu, UInt,vIndex, Str,vText, Int,vChars, UInt,0x400) ;MF_BYPOSITION := 0x400
				vText := StrReplace(vText, "&")
				if (vTemp = StrReplace(vText, "&"))
				|| (vTemp = RegExReplace(vText, "`t.*"))
				{
					vIsMatch := 1
					break
				}
			}
		}
		if !vIsMatch
			return InStr(vOpt, "a") ? 0*DllCall("user32\GetSystemMenu", Ptr,hWnd, Int,1, Ptr) : 0
		if !(A_Index = oArray.Length())
			hMenu := DllCall("user32\GetSubMenu", Ptr,hMenu, Int,vIndex, Ptr)
	}
	if InStr(vOpt, "a")
	{
		DllCall("user32\GetSystemMenu", Ptr,hWnd, Int,1, Ptr)
		if !InStr(vOpt, "x")
			SendMessage(0x112, vID,,, "ahk_id " hWnd) ;WM_SYSCOMMAND := 0x112
	}
	else if !InStr(vOpt, "x")
		SendMessage(0x111, vID,,, "ahk_id " hWnd) ;WM_COMMAND := 0x111
	return vID
}
- Link:
DllCall: Calling GetSystemMenu() on an external program will corrupt its System Menu when the Script exits - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=33966
Post Reply

Return to “Bug Reports”