Windows Menus and the WM_MENUCOMMAND message
-
Hello, I'm working with/on an application with a resource based menu. This application is more or less driven by the programming (script) language tcl and has a lot of APIs introducing C++ functionality to tcl. Now I built an API to manage (popup) menus and their menu items, to create new items with tcl script callbacks, etc. The main point in my "construct" is to use a template menu item from a template menu resource. I created a menu in the resources as template containing a menu item as template. Everything was fine until I realized, that I seemed to have no chance to differentiate between the new commands having all the same id, but different application defined data - changed via the MENUITEMINFO and the related functions Get/SetMenuItemInfo. I changed the menus after their creation to send a WM_MENUCOMMAND message instead of a WM_COMMAND message by setting the dwStyle flag MNS_NOTIFYBYPOS inside the MENUINFO structure via a call to SetMenuInfo. Now - finally to the problem and the question - why I never receive the WM_MENUCOMMAND message? I patched CMainFrame::PreTranslateMessage method to catch the WM_MENUCOMMAND message, I tried with CMainFrame::WindowProc, and by registering a ON_MESSAGE handler inside the message map, but ... nothing worked! Thanks for any reaction in advance! Best regards, Martin Lemburg
-
Hello, I'm working with/on an application with a resource based menu. This application is more or less driven by the programming (script) language tcl and has a lot of APIs introducing C++ functionality to tcl. Now I built an API to manage (popup) menus and their menu items, to create new items with tcl script callbacks, etc. The main point in my "construct" is to use a template menu item from a template menu resource. I created a menu in the resources as template containing a menu item as template. Everything was fine until I realized, that I seemed to have no chance to differentiate between the new commands having all the same id, but different application defined data - changed via the MENUITEMINFO and the related functions Get/SetMenuItemInfo. I changed the menus after their creation to send a WM_MENUCOMMAND message instead of a WM_COMMAND message by setting the dwStyle flag MNS_NOTIFYBYPOS inside the MENUINFO structure via a call to SetMenuInfo. Now - finally to the problem and the question - why I never receive the WM_MENUCOMMAND message? I patched CMainFrame::PreTranslateMessage method to catch the WM_MENUCOMMAND message, I tried with CMainFrame::WindowProc, and by registering a ON_MESSAGE handler inside the message map, but ... nothing worked! Thanks for any reaction in advance! Best regards, Martin Lemburg
Can you provide code ? how you are creating menu, using
SetMenuInfo
etc. ?Prasad Notifier using ATL | Operator new[],delete[][^]
-
Can you provide code ? how you are creating menu, using
SetMenuInfo
etc. ?Prasad Notifier using ATL | Operator new[],delete[][^]
Yes - here's some "reduced" code: 1. the creation of a popup menu:
DWORD style = 0; CMenu \*popup = (CMenu \*) NULL; MENUINFO menuInfo; // see below (1) memset( &menuInfo, 0x0, sizeof( MENUINFO ) ); // (1) // create the new menu object // popup = new CMenu(); // create the new popup menu // if ( ( popup != NULL ) && ( popup->CreatePopupMenu() == TRUE ) ) { // initialize the menu info structure to get its dwStyle member // menuInfo.cbSize = sizeof( MENUINFO ); menuInfo.fMask = MIM\_STYLE; if ( popup->GetMenuInfo( &menuInfo ) == TRUE ) { style = menuInfo.dwStyle; // (re)initialize the menu info structure to change only its dwStyle member // memset( &menuInfo, 0x0, sizeof( MENUINFO ) ); menuInfo.cbSize = sizeof( MENUINFO ); menuInfo.fMask = MIM\_APPLYTOSUBMENUS | MIM\_STYLE; menuInfo.dwStyle = style | MNS\_NOTIFYBYPOS; if ( popup->SetMenuInfo( &menuInfo ) == TRUE ) { tcl\_publishResult( ip, (char \*) create\_popup\_token( popup ) ); return TCL\_OK; } } } system\_error\_message( ip, "couldn't create popup menu" ); if ( popup != NULL ) { delete popup; } return TCL\_ERROR;
2. the insertion of a created popup menu:
int itemPosition = 0; char \*label = (char \*) NULL; CWnd \*root = (CWnd \*) NULL; CMenu \*menu = (CMenu \*) NULL, \*popup = (CMenu \*) NULL; MENUITEMINFO menuInfo; // see below (1) Menu\_if\_tcl\_MenuItem\_s\_t \*menuItem = (Menu\_if\_tcl\_MenuItem\_s\_t \*) NULL; memset( &menuInfo, 0x0, sizeof( MENUITEMINFO ) ); // (1) menuInfo.cbSize = sizeof( MENUITEMINFO ); // get the popup menu pointer and the label ... // // ... // // access the main window // root = AfxGetApp()->m\_pMainWnd; menu = get\_owning\_menu( ip, root->GetMenu(), positionsList, itemPosition ); if ( ( menu != NULL ) && ( itemPosition != -1 ) && ( (UINT) itemPosition <= menu->GetMenuItemCount() ) ) { if ( menu->InsertMenu( itemPosition, MF\_BYPOSITION | MF\_STRING | MF\_POPUP, (UINT) popup->m\_hMenu, label ) == TRUE )
-
Yes - here's some "reduced" code: 1. the creation of a popup menu:
DWORD style = 0; CMenu \*popup = (CMenu \*) NULL; MENUINFO menuInfo; // see below (1) memset( &menuInfo, 0x0, sizeof( MENUINFO ) ); // (1) // create the new menu object // popup = new CMenu(); // create the new popup menu // if ( ( popup != NULL ) && ( popup->CreatePopupMenu() == TRUE ) ) { // initialize the menu info structure to get its dwStyle member // menuInfo.cbSize = sizeof( MENUINFO ); menuInfo.fMask = MIM\_STYLE; if ( popup->GetMenuInfo( &menuInfo ) == TRUE ) { style = menuInfo.dwStyle; // (re)initialize the menu info structure to change only its dwStyle member // memset( &menuInfo, 0x0, sizeof( MENUINFO ) ); menuInfo.cbSize = sizeof( MENUINFO ); menuInfo.fMask = MIM\_APPLYTOSUBMENUS | MIM\_STYLE; menuInfo.dwStyle = style | MNS\_NOTIFYBYPOS; if ( popup->SetMenuInfo( &menuInfo ) == TRUE ) { tcl\_publishResult( ip, (char \*) create\_popup\_token( popup ) ); return TCL\_OK; } } } system\_error\_message( ip, "couldn't create popup menu" ); if ( popup != NULL ) { delete popup; } return TCL\_ERROR;
2. the insertion of a created popup menu:
int itemPosition = 0; char \*label = (char \*) NULL; CWnd \*root = (CWnd \*) NULL; CMenu \*menu = (CMenu \*) NULL, \*popup = (CMenu \*) NULL; MENUITEMINFO menuInfo; // see below (1) Menu\_if\_tcl\_MenuItem\_s\_t \*menuItem = (Menu\_if\_tcl\_MenuItem\_s\_t \*) NULL; memset( &menuInfo, 0x0, sizeof( MENUITEMINFO ) ); // (1) menuInfo.cbSize = sizeof( MENUITEMINFO ); // get the popup menu pointer and the label ... // // ... // // access the main window // root = AfxGetApp()->m\_pMainWnd; menu = get\_owning\_menu( ip, root->GetMenu(), positionsList, itemPosition ); if ( ( menu != NULL ) && ( itemPosition != -1 ) && ( (UINT) itemPosition <= menu->GetMenuItemCount() ) ) { if ( menu->InsertMenu( itemPosition, MF\_BYPOSITION | MF\_STRING | MF\_POPUP, (UINT) popup->m\_hMenu, label ) == TRUE )
The problem , I see in your code is, you have applied
MENUINFO
, before inserting menu and submenus. I tried on simple SDI application, creating a pop - up menu in resource, and usingSetMenuInfo
. It work like charm.Prasad Notifier using ATL | Operator new[],delete[][^]
-
The problem , I see in your code is, you have applied
MENUINFO
, before inserting menu and submenus. I tried on simple SDI application, creating a pop - up menu in resource, and usingSetMenuInfo
. It work like charm.Prasad Notifier using ATL | Operator new[],delete[][^]
Hello Prasad, thanks for your engagement! I changed my sources, so that ... 1. no MENUINFO action happens while creating the popup 2. the insertion is slightly changed: 2.1 menu->InsertMenu( itemPosition, ..., popup->m_hMenu, ... ) 2.2 popup = menu->GetSubMenu( itemPosition ) 2.3 requesting with popup->GetMenuInfo the MENUINFO to get the current dwStyle member => oldStyle 2.4 changing and setting with popup->SetMenuInfo the MENUINFO with dwStyle = oldStyle | MNS_NOTIFYBYPOS I have introduced the ON_MESSAGE handler for WM_MENUCOMMAND, extended the CMainFrame::WindowProc and the CMainFrame::PreTranslateMessage, but none of them gets a WM_MENUCOMMAND message. Even if a CMainFrame instance is the owner of the menu I insert my popups in, can I really be sure, that CMainFrame instance really receives the WM_MENUCOMMAND messages? Thanks again and best regards, Martin Lemburg
-
Hello Prasad, thanks for your engagement! I changed my sources, so that ... 1. no MENUINFO action happens while creating the popup 2. the insertion is slightly changed: 2.1 menu->InsertMenu( itemPosition, ..., popup->m_hMenu, ... ) 2.2 popup = menu->GetSubMenu( itemPosition ) 2.3 requesting with popup->GetMenuInfo the MENUINFO to get the current dwStyle member => oldStyle 2.4 changing and setting with popup->SetMenuInfo the MENUINFO with dwStyle = oldStyle | MNS_NOTIFYBYPOS I have introduced the ON_MESSAGE handler for WM_MENUCOMMAND, extended the CMainFrame::WindowProc and the CMainFrame::PreTranslateMessage, but none of them gets a WM_MENUCOMMAND message. Even if a CMainFrame instance is the owner of the menu I insert my popups in, can I really be sure, that CMainFrame instance really receives the WM_MENUCOMMAND messages? Thanks again and best regards, Martin Lemburg
Hi Martin, You must be using
TrackPopupMenu
for invoking pop up menu, isn't it ? Now, I'll tell you ,steps I followed to got it working. 1. Create a menu in resource editor with pop-up style. 2. Use CMenu::LoadMenu to load that menu. 3. UseSetMenuInfo
as described in your original post. 4. UseTrackPopupMenu
, set its parent as main frame window. 5. OverrideDefWndProc
ofCMainFrame
(or you can usePreTranslateMessage
). 6. I can seeWM_MENUCOMMAND
sent when clicked this menu. May this give you some idea.Prasad Notifier using ATL | Operator new[],delete[][^]
-
Hi Martin, You must be using
TrackPopupMenu
for invoking pop up menu, isn't it ? Now, I'll tell you ,steps I followed to got it working. 1. Create a menu in resource editor with pop-up style. 2. Use CMenu::LoadMenu to load that menu. 3. UseSetMenuInfo
as described in your original post. 4. UseTrackPopupMenu
, set its parent as main frame window. 5. OverrideDefWndProc
ofCMainFrame
(or you can usePreTranslateMessage
). 6. I can seeWM_MENUCOMMAND
sent when clicked this menu. May this give you some idea.Prasad Notifier using ATL | Operator new[],delete[][^]
Ok - got it working - a bit different than you told me to, and with a lot of consequences. The current state: 1. we have a MFC application 2. our MFC application has a resource based menu, with one
ON_COMMAND
handler per menu item 3. I wrote a tcl API to access the MFC application menu, which allows to create new sub menus and menu items. 4. before I insert a popup menu, I switch the MFC application menuMENUINFO
toMNS_NOTIFYBYPOS
, so thatWM_MENUCOMMAND
messages are used, instead ofWM_COMMAND
messages 5. after inserting a popup menu, I switch the new sub menu to useWM_MENUCOMMAND
messages 6. I registered aON_MESSAGE
handler forWM_MENUCOMMAND
messages The consequences: 1. None of the resource based menu items, withON_COMMAND
handlers, will work anymore, because there are no WM_COMMAND messages anymore 2. every selected menu item causes the wanted WM_MENUCOMMAND message 3. every menu in the "path" of (sub)menus seem to have thisMENUINFO
style flagMNS_NOTIFYBYPOS
, so that this model is really working. If I don't switch all containing menus of a sub menus to sendWM_MENUCOMMAND
messages, than the whole model won't work. The suggestion above to useTrackPopupMenu
must work, if the popup menu is used all alone, but if it is used inside a "path" of cascaded menus, than this would be more problematic. 4. the whole menu system ofON_COMMAND
handlers must be rewritten, or I must find a way to get rid of the dummy or template menu item and create resources on the fly, but ... I don't know how! A change of question: How do I create on-the-fly resources or menu items with new, non-existing IDs? If I'd know, how to create on-the-fly resources, than I could stay with the normal WM_COMMAND system. Thanks to you Prasad! Best regards, and a happy weekend! Martin Lemburg