Modifying the System Menu in C++ with MFC - Modifying the System Menu in C with MFC (Page 2 of 3 )
Adding Commands Firstly, we need to define a unique variable to represent each menu item. This can be done in the resource.h file or in any standard header file. If the commands already exist as part of another standard or context menu, then this step can be skipped because the definitions already exist.
However, it's important to note that even if we use the pre-existing definitions, the message handlers for the commands on the regular menu will NOT be automatically called. System commands are routed differently. So, in the end, it doesn't matter if we use the existing definition or create our own.
For our example, we'll define:
#define IDM_ABOUT 16 #define IDM_EXIT 17
The IDM just means that this variable is a menu-item ID. We add these commands in our window's initializing function (OnInitDialog(), OnCreate()). My example is in a dialog class, so this is what the function looks like:
The first thing that you should notice is a couple of ASSERT statements for each command. The first one deals with a bug present in Windows 95 and the second ensures the custom command is below the range used by pre-defined system commands.
Here's an exert from the MFC documentation:
"All predefined Control-menu items have ID numbers greater than 0xF000. If an application adds items to the Control menu, it should use ID numbers less than F000."
Next, we get a pointer to the system menu with the GetSystemMenu function. We call it with an argument of FALSE to get the pointer. If we call it with TRUE, then it will reset the menu to its default state.
If the pointer is valid, we call some commands to add to the bottom of the menu, passing the IDs and the string we want to show up when the menu is viewed.
Simple, isn't it?
Processing Custom Commands In order to have those commands do anything, we can't rely on the normal message-handling mechanism -- even if we have handlers for the same items in other menus. We have to handle the WM_SYSCOMMAND message in our dialog/window class:
void CBabelOnDlg::OnSysCommand(UINT nID, LPARAM lParam) { //trap our own system menu messages if ((nID & 0xFFF0) == IDM_ABOUTBOX) { CAboutDlg dlgAbout; dlgAbout.DoModal(); } else if ((nID & 0xFFF0)==SC_CLOSE){ OnClose(); } else if ((nID & 0xFFF0)==IDM_EXIT) { ::PostQuitMessage(0); }
else {
CDialog::OnSysCommand(nID, lParam); } }
This is the height of simplicity herself. We compare the code that was passed in with the system to our commands, again ANDing them to account for the Windows 95 bug. Then we can call whatever code we want to handle it.
If we selected Exit, then we post a message to quit the application.
Take a look at the second test: SC_CLOSE is a predefined menu constant. It's one normally handled by Windows, but we can still add custom processing to it if we want. In this application I didn't want it to exit, but to merely hide the application (with an icon in the system tray). Because of this, I just call a handler that I wrote to do exactly that. If the ID doesn't equal anything that we want to custom-process, then we just pass it on up the hierarchy for default processing.
The IDs for the most common system commands are:
SC_CLOSE Close the CWnd object. SC_MAXIMIZE (or SC_ZOOM) Maximize the CWnd object. SC_MINIMIZE (or SC_ICON) Minimize the CWnd object. SC_MOVE Move the CWnd object. SC_RESTORE Restore window to normal position and size. SC_SIZE Size the CWnd object.
There are others system commands used in special situations that you can learn about in the documentation for WM_SYSCOMMAND.
Modifying Existing Commands We just saw that it's possible to change the default handling of built-in system commands, but it's also possible to modify existing menu items by changing their text, or to entirely remove them.
To modify the text of a command, use the ModifyMenu() function on pSysMenu in the above example. For example, to change "Close" to "Hide", I could do this:
The MF_BYCOMMAND argument tells the function to interpret SC_CLOSE as a command ID. IDM_HIDE is a new command ID. The last option is the text we want to show on the menu item.
Alternatively, we can call ModifyMenu() on the menu item: