This is another in my "techniques I really use" series of short articles that don't discuss theory or design patterns, or any of that other hoity-toity stuff. This article is about simply getting something done. So without wasting any more of your time than I already have, let's get started.
Doing the Deed
The system menu is the menu that's displayed when you right-click on the application's icon in the title bar. The act of changing the system menu is fairly simple. Just add the following code to your application's
HMENU pSysMenu = ::GetSystemMenu(m_pMainWnd->GetSafeHwnd(), FALSE);
::InsertMenu(pSysMenu, 0, MF_BYPOSITION | MF_STRING, ID_SHOW_MESSAGE1, <BR> "Show Message 1");
::InsertMenu(pSysMenu, 1, MF_BYPOSITION | MF_STRING, ID_SHOW_MESSAGE2, <BR> "Show Message 2");
::InsertMenu(pSysMenu, 2, MF_BYPOSITION | MF_SEPARATOR, 0, "");
After you retrieve the menu, simply add your new menu items to it. In the code above, we added our custom menu items to the top of the menu, and separated them from the original menu items with a menu separator. Easy stuff.
Next, we need to add our menu item IDs to the resource.h file. The easiest way to do this is to simply add a new menu to the application's resources, and create the menu items in the new menu. Well, it's kind of easy. When I did this, and then tried to add handlers via the Properties view for the
CMainFrame message map, the ID's showed up as 32771 and 32772 instead of the names I gave them. I had to go back and re-display the menu resource and change the ID names in order for them to show up properly in the message map. Sometimes VS2005 just pisses me off. In any case, I then went back and added the handlers in
The final step is to add a handler for the Windows
WM_SYSCOMMAND message. Once you've added the handler, replace the contents of the function with the following code:
void CMainFrame::OnSysCommand(UINT nID, LPARAM lParam)
bool bRecognized = false;
case ID_SHOW_MESSAGE1 :
case ID_SHOW_MESSAGE2 :
PostMessage(WM_COMMAND, nID, 0);
First, we need to allow the function to process system commands. If none were processed we'll stillbe in the function (after handling a system command, the first switch statement returns to the calling function) and we can process our own commands. Since we have handlers for them in our
CMainFrame class, we'll just post the message to our
Note About OnSysCommand
In VS2005 (or maybe even VS2003), the
OnSysCommand() function was modified as follows:
- In prior versions of the function, the first parameter was a
WPARAM, with the lower word containing information used by Windows, and the upper word being the actual ID of the selected command, and it was up to the programmer to strip the lower word in order to determine the command being sent. In VS2005, MFC strips the lower word for you, thus leading to the next change.
- The first parameter passed to the function was changed from a
WPARAM to a
UINT. This made things on our side of the function just a wee bit simpler because in the case of the function above, we no longer had to do this -
switch(wParam&0xFFF0) - in order to get to the message ID.
- The list of handled messages appears to have shrunk. As you can see in the code above, I commented out several message IDs not specifically listed in MSDN.
- It seems that MSDN does not provide a complete list of system messages that this command can process. While testing the sample app, I discovered the following issues:
- Left-clicking the application icon will not present the system menu to the user. Right-clicking will, and the menu items will work.
- The user will be unable to click in the main menu of your application.
- The user will not be able to resize the window by click/dragging the borders of the main window.
- The buttons that minimize/maximize/close the window (on the right side of the title bar) appear to work normally.
To fix all of these issues, you have to add the following case items to the code above:
I did a search in the MFC/SDK source code, and these two message ID's are NOT defined anywhere. C'mon Microsoft - you're sure getting sloppy in your old age.
Disclaimers and Other Notes
As usual, I'm asking that you vote the article and not my politics. Isn't it a dirty rotten shame that I have to add this to every one of my articles? No, it's not because of my politics, but because some people here don't know when to leave their politics out of their voting considerations.