WildByDesign Posted Tuesday at 06:25 PM Posted Tuesday at 06:25 PM I am needing to obtain the CtrlID for menus created with _GUICtrlMenu_CreateMenu() but not having any luck. _WinAPI_GetDlgCtrlID() does not seem to be able to get CtrlID's from menus at all. Whether it's from _GUICtrlMenu_CreateMenu() or GUICtrlCreateMenu(). I have found other ways to obtain the CtrlID for menus created with GUICtrlCreateMenu() but I am still stuck on _GUICtrlMenu_CreateMenu(). I even created a loop to loop through 10,000 CtrlID's to find a match but they don't seem to exist. Thanks for your time.
MattyD Posted Tuesday at 10:34 PM Posted Tuesday at 10:34 PM (edited) Yep, so with menus you have a HMENU handle, its not a window handle - this is why _WinAPI_GetDlgCtrlID wont work. I realise GUICtrlCreateMenu gives you a "control id" but I think this is just an internal autoit thing. You can retrieve the IDM of items if you have the parent HMENU and its position. This is normally used if you're handling WM_MENUCOMMAND (menus created with the $MNS_NOTIFYBYPOS style). Otherwise you'd just go by the IDM and handle notifications via WM_COMMAND. IDMs aren't necessarily unique either - you can reuse the ID in multiple menus. (Maybe a command appears in a context menu as well as the main menu for eg.) expandcollapse popup#include <guimenu.au3> #include <guiconstants.au3> #include <windowsconstants.au3> Global $hGUI = GUICreate("test", 400, 200) GUIRegisterMsg($WM_COMMAND, "WM_COMMAND") GUIRegisterMsg($WM_MENUCOMMAND, "WM_MENUCOMMAND") GUISetState() Global Enum $idmNew = 1000, $idmOpen, $idmSave, $idmExit ;1st Menu Global $hFile = _GUICtrlMenu_CreateMenu() _GUICtrlMenu_InsertMenuItem($hFile, 0, "New", $idmNew) _GUICtrlMenu_InsertMenuItem($hFile, 1, "Open", $idmOpen) _GUICtrlMenu_InsertMenuItem($hFile, 2, "Save", $idmSave) _GUICtrlMenu_InsertMenuItem($hFile, 3, "", 0) _GUICtrlMenu_InsertMenuItem($hFile, 4, "Exit", $idmExit) ;2nd Menu Global $hFile2 = _GUICtrlMenu_CreateMenu() _GUICtrlMenu_InsertMenuItem($hFile2, 0, "New", $idmNew) _GUICtrlMenu_InsertMenuItem($hFile2, 1, "Open", $idmOpen) _GUICtrlMenu_InsertMenuItem($hFile2, 2, "Save", $idmSave) _GUICtrlMenu_InsertMenuItem($hFile2, 3, "", 0) _GUICtrlMenu_InsertMenuItem($hFile2, 4, "Exit", $idmExit) ;Main Global $hMain = _GUICtrlMenu_CreateMenu(BitOR($MNS_NOTIFYBYPOS, $MNS_CHECKORBMP)) ;Use this one for WM_MENUCOMMAND ;~ Global $hMain = _GUICtrlMenu_CreateMenu() ;Use this one instead for WM_COMMAND _GUICtrlMenu_InsertMenuItem($hMain, 0, "File 1", 0, $hFile) _GUICtrlMenu_InsertMenuItem($hMain, 1, "File 2", 0, $hFile2) _GUICtrlMenu_SetMenu($hGUI, $hMain) do until GUIGetMsg() = $GUI_EVENT_CLOSE Func WM_COMMAND($hWnd, $iMsg, $wParam, $lParam) #forceref $hWnd, $iMsg, $lParam Local $iItemID, $iNotifCode, $iCtrlID, $hCtrl Switch _WinAPI_HiWord($wParam) Case 0 ;menu $iItemID = _WinAPI_LoWord($wParam) ConsoleWrite("WM_COMMAND: " & $iItemID & @CRLF) Case 1 ;accelerator $iItemID = _WinAPI_LoWord($wParam) ConsoleWrite("WM_COMMAND: " & $iItemID & @CRLF) Case Else ;control-defined notif code $iNotifCode = _WinAPI_HiWord($wParam) ;e.g BN_CLICKED BN_DBLCLK for button $iCtrlID = _WinAPI_LoWord($wParam) $hCtrl = $lParam EndSwitch Return $GUI_RUNDEFMSG EndFunc ;==>WM_COMMAND Func WM_MENUCOMMAND($hWnd, $iMsg, $wParam, $lParam) #forceref $hWnd, $iMsg, $lParam Local $sText = _GUICtrlMenu_GetItemText($lParam, $wParam) Local $iItemID = _GUICtrlMenu_GetItemID($lParam, $wParam) ConsoleWrite("WM_MENUCOMMAND: " & $iItemID & " " & $sText & @CRLF) Return $GUI_RUNDEFMSG EndFunc ;==>WM_COMMAND Edited Tuesday at 10:49 PM by MattyD WildByDesign 1
Nine Posted Tuesday at 11:07 PM Posted Tuesday at 11:07 PM The real question is why you need control ID of a menu. I believe it is related to your Dark Mode UDF but if we cannot understand the fundamental of your research, you may pass way abord of a nicer solution. It is like your solution for distinguish between menu type, are you sure about it ? I doubt, but since we do not have a snippet of the code, all can really guess... WildByDesign 1 “They did not know it was impossible, so they did it” ― Mark Twain Spoiler Block all input without UAC Save/Retrieve Images to/from Text Monitor Management (VCP commands) Tool to search in text (au3) files Date Range Picker Virtual Desktop Manager Sudoku Game 2020 Overlapped Named Pipe IPC HotString 2.0 - Hot keys with string x64 Bitwise Operations Multi-keyboards HotKeySet Recursive Array Display Fast and simple WCD IPC Multiple Folders Selector Printer Manager GIF Animation (cached) Debug Messages Monitor UDF Screen Scraping Round Corner GUI UDF Multi-Threading Made Easy Interface Object based on Tag
WildByDesign Posted Wednesday at 12:44 PM Author Posted Wednesday at 12:44 PM 13 hours ago, Nine said: The real question is why you need control ID of a menu. I believe it is related to your Dark Mode UDF but if we cannot understand the fundamental of your research, you may pass way abord of a nicer solution. It is like your solution for distinguish between menu type, are you sure about it ? I doubt, but since we do not have a snippet of the code, all can really guess... My apologies. You are right, I wasn't as descriptive with my goal and intentions as I usually am and that does not assist anyone trying to help me. Same with no code snippet. So yes, it is related to the Dark Mode UDF. My goal is to make it easier for anyone who uses the UDF. I want to detect all controls automatically and I want to detect the menubar items as well. This way, the user only has to add one line to enable everything in the UDF and the UDF should then do it all. I put together the best from ModernMenuRaw UDF combined with the best from UEZ's recent dark menubar subclassing since both sources have certain parts that are better when combined together. The problem is that both solutions rely on subclassing the menubar with the CtrlID returned by GUICtrlCreateMenu. Here is the relevant code snippet which automatically gets the CtrlID's and text for the menubar: expandcollapse popupFunc _GUITopMenuTheme($hWnd) Local $sItemText Local $z = 0 $hGUI = $hWnd ;Local $aMenuInfo[0][3] ; get top menu handle Local $hMenu = _GUICtrlMenu_GetMenu($hWnd) If Not $hMenu Then Return False GUIRegisterMsg($WM_NCPAINT, "WM_NCPAINT") GUIRegisterMsg($WM_ACTIVATE, "WM_ACTIVATE_Handler") Local $iMenuCount = _GUICtrlMenu_GetItemCount($hMenu) Local $aMenuInfo[$iMenuCount][3] Global $g_aMenuText[$iMenuCount] Global $g_aMenuPos[$iMenuCount] For $i = 0 To UBound($g_aMenuText) - 1 $g_aMenuText[$i] = "" $g_aMenuPos[$i] = "" Next _ArrayColInsert($g_aMenuText, 0) ; run through the first 100 CtrlID's to determine which are valid menus For $i = 1 To 100 If _GUICtrlMenu_IsMenu(GUICtrlGetHandle($i)) Then $sItemText = _GUICtrlMenu_GetItemText($hMenu, $i, False) ; obtain text ; if it returns blank for text, discard because that would be context menu or other If $sItemText Then $g_aMenuText[$z][0] = $i ; nMenu (CtrlId) $g_aMenuText[$z][1] = $sItemText ; menu text $g_aMenuPos[$z] = $i $aMenuInfo[$z][0] = GUICtrlGetHandle($i) ; submenu handle $aMenuInfo[$z][1] = $i ; nMenu (CtrlId) $aMenuInfo[$z][2] = $sItemText ; menu text $z += 1 EndIf EndIf Next For $i = 0 To UBound($g_aMenuText) - 1 Local $nIdx = _GetNewItemIndex() If $nIdx = 0 Then Return 0 $MENULASTITEM = $aMenuInfo[$i][1] $arMenuItems[$nIdx][0] = $g_aMenuText[$i][0] $arMenuItems[$nIdx][1] = $g_aMenuText[$i][1] $arMenuItems[$nIdx][2] = -1 $arMenuItems[$nIdx][3] = "TOP" $arMenuItems[$nIdx][4] = 0 $arMenuItems[$nIdx][5] = FALSE $arMenuItems[$nIdx][6] = -1 $arMenuItems[$nIdx][7] = TRUE _GUICtrlMenu_SetItemType($hMenu, $i, $MFT_OWNERDRAW, True) MenuBarBKColor($hMenu, 0x202020) Next EndFunc I only run through the first 100 CtrlID's to check for menus. Possibly that could be a higher number, but generally they would be lower. If you want to see and test the full working code, GUIDarkTheme-0.4 includes it now.
WildByDesign Posted Wednesday at 01:19 PM Author Posted Wednesday at 01:19 PM 14 hours ago, MattyD said: Yep, so with menus you have a HMENU handle, its not a window handle - this is why _WinAPI_GetDlgCtrlID wont work. I realise GUICtrlCreateMenu gives you a "control id" but I think this is just an internal autoit thing. First of all, thank you for taking time to share that code. It is really helpful and I'm going to mess around with your example some more today. I don't understand all of it so I'm going to spend some time learning from it as well. I don't know much about whether it is a legitimate CtrlID or not, but that return from GUICtrlCreateMenu is what ModernMenuRaw and UEZ's dark menubar subclassing use for adding dark mode theme to the menubar. The text and menu position, from my understanding, are derived from that CtrlID in those implementations. 14 hours ago, MattyD said: You can retrieve the IDM of items if you have the parent HMENU and its position. This is normally used if you're handling WM_MENUCOMMAND (menus created with the $MNS_NOTIFYBYPOS style). Otherwise you'd just go by the IDM and handle notifications via WM_COMMAND. IDMs aren't necessarily unique either - you can reuse the ID in multiple menus. (Maybe a command appears in a context menu as well as the main menu for eg.) I definitely need to learn more about HMENU in general. What does IDM stand for? The only problem that I have is that I would need the ItemID/CtrlID (or whichever is right) prior to any user interaction. I apologize because I wasn't very descriptive in my first post as to what my goal was. Basically, I have made a UDF for dark theme and my goal is to auto-detect all controls, including menubar items. I am detecting most controls already and I am now (as of GUIDarkTheme-0.4) able to automatically detect text/CtrlID/Position for menubar created with GUICtrlCreateMenu and switching to ownerdraw and completing the theme-related stuff. My goal is essentially that the anyone with a GUI script can add the #include line for the UDF and only have to add the single line _ApplyDarkTheme($hGUI) right before GUISetState to send the GUI handle on to the UDF which will then auto-detect and subclass anything that needs it. So far it is working remarkably well. But if I can also get it to work with scripts that have a menubar created with _GUICtrlMenu_CreateMenu(), that would be a bonus. So far any of my attempts have failed.
Solution Nine Posted Wednesday at 05:05 PM Solution Posted Wednesday at 05:05 PM 4 hours ago, WildByDesign said: Here is the relevant code snippet Thank you. I now better understand what you are up to. As @MattyD said, there is not Control ID for those _GUICtrlMenu_* function. So you should consider switching to handles since all controls (whatsoever) has a windows handle. To extend Matty example : expandcollapse popup; From Nine #include <GUIConstantsEx.au3> #include <GuiMenu.au3> #include <WinAPI.au3> #include <Array.au3> Opt("MustDeclareVars", True) Global Enum $e_idOpen = 1000, $e_idSave, $e_idInfo, $e_idTest Global $hMenu Example() Func Example() Local $hGUI = GUICreate("Menu", 400, 300) GUISetState(@SW_SHOW) ; Context menu $hMenu = _GUICtrlMenu_CreatePopup() _GUICtrlMenu_InsertMenuItem($hMenu, 0, "Open", $e_idOpen) _GUICtrlMenu_InsertMenuItem($hMenu, 1, "Save", $e_idSave) _GUICtrlMenu_InsertMenuItem($hMenu, 3, "", 0) _GUICtrlMenu_InsertMenuItem($hMenu, 4, "Info", $e_idInfo) #cs Local $idContextmenu = GUICtrlCreateContextMenu() Local $idOpen = GUICtrlCreateMenuItem("Open", $idContextmenu) Local $idSave = GUICtrlCreateMenuItem("Save", $idContextmenu) GUICtrlCreateMenuItem("", $idContextmenu) Local $idInfo = GUICtrlCreateMenuItem("Info", $idContextmenu) Local $hMenu = GUICtrlGetHandle($idContextmenu) #ce ; GUI menu Local $hFile = _GUICtrlMenu_CreateMenu() _GUICtrlMenu_InsertMenuItem($hFile, 0, "&Open", $e_idOpen) Local $hRecent = _GUICtrlMenu_CreateMenu() _GUICtrlMenu_InsertMenuItem($hRecent, 0, "Test") _GUICtrlMenu_InsertMenuItem($hFile, 1, "&Recent", 0, $hRecent) Local $hMain = _GUICtrlMenu_CreateMenu() _GUICtrlMenu_InsertMenuItem($hMain, 0, "&File", 0, $hFile) _GUICtrlMenu_InsertMenuItem($hMain, 1, "Test", $e_idTest) _GUICtrlMenu_SetMenu($hGUI, $hMain) #cs Local $idFile = GUICtrlCreateMenu("&File") Local $idOpen = GUICtrlCreateMenuItem("&Open", $idFile) Local $idRecent = GUICtrlCreateMenu("&Recent", $idFile) GUICtrlCreateMenuItem("Test", $idRecent) Local $hFile = GUICtrlGetHandle($idFile) Local $hRecent = GUICtrlGetHandle($idRecent) GUICtrlCreateMenuItem("Test", -1) #ce ConsoleWrite("Main " & IsInMainMenu($hGUI, $hMain) & @CRLF) ConsoleWrite("Context " & IsInMainMenu($hGUI, $hMenu) & @CRLF) ConsoleWrite("File " & IsInMainMenu($hGUI, $hFile) & @CRLF) ConsoleWrite("Recent " & IsInMainMenu($hGUI, $hRecent) & @CRLF) Local $aItem = GetTopLevelMenuItems($hGUI) _ArrayDisplay($aItem) GUIRegisterMsg($WM_CONTEXTMENU, WM_CONTEXTMENU) Do Until GUIGetMsg() = $GUI_EVENT_CLOSE EndFunc ;==>Example Func WM_CONTEXTMENU($hWnd, $iMsg, $wParam, $lParam) _GUICtrlMenu_TrackPopupMenu($hMenu, $wParam) EndFunc ;==>WM_CONTEXTMENU Func IsInMainMenu($hGUI, $hMenu) Local $hMain = _GUICtrlMenu_GetMenu($hGUI) If $hMain = $hMenu Then Return 0 Return CheckSub($hMain, $hMenu, 1) EndFunc ;==>IsInMainMenu Func CheckSub($hMenu, $hCheck, $iLvl) Local $hSub For $i = 0 To _GUICtrlMenu_GetItemCount($hMenu) - 1 $hSub = _GUICtrlMenu_GetItemSubMenu($hMenu, $i) If $hSub Then If $hSub = $hCheck Then Return $iLvl Return CheckSub($hSub, $hCheck, $iLvl + 1) EndIf Next Return -1 EndFunc ;==>CheckSub Func GetTopLevelMenuItems($hWnd) Local $hMenu = _GUICtrlMenu_GetMenu($hWnd) Local $nItem = _GUICtrlMenu_GetItemCount($hMenu) Local $aList[$nItem][3], $tInfo For $i = 0 To $nItem - 1 $tInfo = _GUICtrlMenu_GetItemInfo($hMenu, $i) $aList[$i][0] = $tInfo.SubMenu $aList[$i][1] = $tInfo.ID $aList[$i][2] = _GUICtrlMenu_GetItemText($hMenu, $i) Next Return $aList EndFunc ;==>GetTopLevelMenuItems “They did not know it was impossible, so they did it” ― Mark Twain Spoiler Block all input without UAC Save/Retrieve Images to/from Text Monitor Management (VCP commands) Tool to search in text (au3) files Date Range Picker Virtual Desktop Manager Sudoku Game 2020 Overlapped Named Pipe IPC HotString 2.0 - Hot keys with string x64 Bitwise Operations Multi-keyboards HotKeySet Recursive Array Display Fast and simple WCD IPC Multiple Folders Selector Printer Manager GIF Animation (cached) Debug Messages Monitor UDF Screen Scraping Round Corner GUI UDF Multi-Threading Made Easy Interface Object based on Tag
WildByDesign Posted Wednesday at 06:50 PM Author Posted Wednesday at 06:50 PM 1 hour ago, Nine said: So you should consider switching to handles since all controls (whatsoever) has a windows handle. I agree and I like the idea of using handles in general for that exact reason. But there are some things that I don't fully understand. For example, many of the _GUICtrlMenu_* functions don't work with handles. Such as _GUICtrlMenu_SetItemType for setting ownerdraw. I'm not entirely sure how I would do that in this case. Another concern that I have is not all of the top menus in the _ArrayDisplay show a handle. The File menu shows a handle in the array but Test does not. I added a few more just to mess around with it and they didn't show a handle either. I'm sure that there are likely other ways to get the handles so I'm not too worried about that. I'll have to look into whether or not the subclassing part can be done with handles since both examples that I use to reference only do it with the CtrlIDs. Thank you for the extended example. I'm going to mess around with it some more later tonight.
Nine Posted Wednesday at 07:11 PM Posted Wednesday at 07:11 PM 11 minutes ago, WildByDesign said: Test does not Yes I voluntary created a non menu item at top level. Although it is not frequent, it is possible. 17 minutes ago, WildByDesign said: I added a few more just to mess around with it and they didn't show a handle either. You probably added non menu also. Tested adding another menu, and it works as expected : Local $hOther = _GUICtrlMenu_CreateMenu() _GUICtrlMenu_InsertMenuItem($hOther, 0, "Other") Local $hFile = _GUICtrlMenu_CreateMenu() _GUICtrlMenu_InsertMenuItem($hFile, 0, "&Open", $e_idOpen) Local $hRecent = _GUICtrlMenu_CreateMenu() _GUICtrlMenu_InsertMenuItem($hRecent, 0, "Test") _GUICtrlMenu_InsertMenuItem($hFile, 1, "&Recent", 0, $hRecent) Local $hMain = _GUICtrlMenu_CreateMenu() _GUICtrlMenu_InsertMenuItem($hMain, 0, "&File", 0, $hFile) _GUICtrlMenu_InsertMenuItem($hMain, 1, "Test", $e_idTest) _GUICtrlMenu_InsertMenuItem($hMain, 2, "Other", 0, $hOther) _GUICtrlMenu_SetMenu($hGUI, $hMain) WildByDesign 1 “They did not know it was impossible, so they did it” ― Mark Twain Spoiler Block all input without UAC Save/Retrieve Images to/from Text Monitor Management (VCP commands) Tool to search in text (au3) files Date Range Picker Virtual Desktop Manager Sudoku Game 2020 Overlapped Named Pipe IPC HotString 2.0 - Hot keys with string x64 Bitwise Operations Multi-keyboards HotKeySet Recursive Array Display Fast and simple WCD IPC Multiple Folders Selector Printer Manager GIF Animation (cached) Debug Messages Monitor UDF Screen Scraping Round Corner GUI UDF Multi-Threading Made Easy Interface Object based on Tag
WildByDesign Posted Wednesday at 08:04 PM Author Posted Wednesday at 08:04 PM From looking over the subclassing code, it seems like what I likely need is ItemID.
WildByDesign Posted Wednesday at 10:03 PM Author Posted Wednesday at 10:03 PM I have an idea and wondering if I can get any opinions on it. All menus created with _GUICtrlMenu_CreateMenu seem to have an ItemID of 0 by default. I am wondering about detecting these as top level menus and if they don't have an ItemID, assign one with _GUICtrlMenu_SetItemID. I seem to be able to pick that up in the subclassing code. I would have to also take note of its position in the menu and probably store both in an array to work with. Do you think this would be suitable? If yes, what would be an appropriate number to start assigning as ItemID?
argumentum Posted Wednesday at 10:24 PM Posted Wednesday at 10:24 PM (edited) 10,000 ( ten thousand ) because : GUI_MAXCONTROLS - 65532 - Maximum number of controls in GUI box. That way you're unlikely to use anybody else's ID. My 2 cents, I was just passing by Edited Wednesday at 10:24 PM by argumentum WildByDesign 1 Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting.
WildByDesign Posted Thursday at 12:06 AM Author Posted Thursday at 12:06 AM (edited) @Nine Can you please check and see if this makes sense and would be correct? Basically, if there is already an ItemID, good. If not, assign one. If everything is correct then I should have everything I need in a single array. EDIT: I am thinking that this should work for both GUICtrlCreateMenu and _GUICtrlMenu_CreateMenu. Func GetTopLevelMenuItems($hWnd) Local $iItemID = 10000 Local $hMenu = _GUICtrlMenu_GetMenu($hWnd) Local $nItem = _GUICtrlMenu_GetItemCount($hMenu) Local $aList[$nItem][3], $tInfo For $i = 0 To $nItem - 1 $tInfo = _GUICtrlMenu_GetItemInfo($hMenu, $i) $aList[$i][0] = $tInfo.SubMenu If Not $tInfo.ID Then _GUICtrlMenu_SetItemID($hMenu, $i, $iItemID) $aList[$i][1] = _GUICtrlMenu_GetItemID($hMenu, $i) $iItemID += 1 Else $aList[$i][1] = $tInfo.ID EndIf $aList[$i][2] = _GUICtrlMenu_GetItemText($hMenu, $i) Next Return $aList EndFunc ;==>GetTopLevelMenuItems Edited Thursday at 12:08 AM by WildByDesign
WildByDesign Posted Thursday at 12:48 AM Author Posted Thursday at 12:48 AM I am happy to report partial success on this so far. It's working with both GUICtrlCreateMenu and _GUICtrlMenu_CreateMenu. The only negative aspect so far is that the measurements for the custom drawing are off for _GUICtrlMenu_CreateMenu. But one step at a time. argumentum 1
WildByDesign Posted Thursday at 01:19 AM Author Posted Thursday at 01:19 AM Measurement problem was really a non-issue. It was my fault for forgetting the ampersand in each of the menus. As soon as I added a single ampersand to each, the problem was instantly solved. I'm not entirely sure why an ampersand made that much of a difference. Thank you again @MattyD and @Nine for your help. argumentum 1
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now