2

I want to remove the Move and Close commands from the system menu in a Windows dialog-based application without losing the functionality of those commands or removing the system menu. (I'm using MFC, but open to a pure c++ solution.)

The following will remove the Move command from the system menu:

CMenu* pSysMenu = GetSystemMenu(FALSE);
if(pSysMenu != NULL)
        pSysMenu->RemoveMenu(SC_MOVE, MF_BYCOMMAND);

But the window can no longer be dragged.

Similarly, this will remove the Close command.

pSysMenu->RemoveMenu(SC_CLOSE, MF_BYCOMMAND);

But it also disables the close button ("x") in the window's title bar.

Thanks!

(I know a few of you are tempted to tell me I shouldn't remove those commands. I hear you, but this is a unique situation. Thanks.)

Steve A
  • 1,798
  • 4
  • 16
  • 34
  • 1
    Create your own-drawn window which draws such buttons manually and executes the requested feature manually as well. – Michael Chourdakis Oct 10 '20 at 14:42
  • 1
    Take a look at [WM_NCHITTEST](https://learn.microsoft.com/en-us/windows/win32/inputdev/wm-nchittest). By returning the appropriate values (depending on the location of the click) you might be able to get the behaviour you want. Return `HTCAPTION` to drag the window. – Paul Sanders Oct 10 '20 at 14:54
  • 1
    @Steve - please see my P.S. to the answer below; will make it a bit easier – Vlad Feinstein Oct 11 '20 at 02:11

1 Answers1

3

You can remove those items from the system menu in the WM_INITMENUPOPUP handler.

This, as you noticed, will disable related functionality in your window.

However, you can restore those items in the handler of lesser-known WM_UNINITMENUPOPUP message.

Add this to your dialog's class definition:

afx_msg void OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu);
afx_msg void OnUnInitMenuPopup(CMenu *pPopupMenu, UINT nFlags);

Add this to its message map:

ON_WM_INITMENUPOPUP()
ON_WM_UNINITMENUPOPUP()

You would then remove those items (just like you did) in OnInitMenuPopup:

pPopupMenu->RemoveMenu(SC_MOVE, MF_BYCOMMAND);

and re-insert them in OnUnInitMenuPopup:

pPopupMenu->InsertMenuItemW(SC_MOVE, &m_mii);

Please note &m_mii that I've defined in the class:

MENUITEMINFO m_mii = {sizeof MENUITEMINFO};

You would declare that structure for each of the menu items you need to hide, and populate it with GetMenuItemInfo right before you remove them. Likely, you only need to do it once and save for later use. This will allow you to save the menu string, bitmap, etc. However, there is no direct way (as I know of) to get the position of your items in the original menu; I would iterate through all items by-position looking for those commands. A bit tedious...

P.S. Just realize that you would NEVER display those items in the system menu, so you don't need to bother with saving their position, bitmap, or even string. Just append them to the bottom, command IDs only.

Vlad Feinstein
  • 10,960
  • 1
  • 12
  • 27
  • 2
    Thanks, @Vlad Feinstein! After your **outstanding** solution, I ended up removing the caption bar, displaying my own "system menu" icon when the mouse is over the app, and allowing dragging of the window from the client area. In this particular app, that actually ended up feeling more Microsoft-like. I'm mentioning this as an option in case others end up in this thread, but it's bitch'n that your documented the OnInitMenuPopup() approach. Thanks again! – Steve A Oct 11 '20 at 16:11
  • I want to remove the system close button on a simple generated MFC program with a Properties bar. It looks like calling sysMenu->RemoveMenu( SC_CLOSE, MF_BYCOMMAND ); after calling GetSystemMenu() is correct for me, but ...::OnInitMenuPopup never gets called. From which handler should I be calling GetSystemMenu()? – rtischer8277 Mar 02 '22 at 17:49