Search code examples
delphiwinapisystemmenu

System Menu: How to hide/move standard menuitems


In the system menu (topleft of titlebar), I can add my own menu items. I can also delete e.g.DeleteMenu(SysMenu, SC_MINIMIZE, MF_BYCOMMAND) ;

However if I delete the standard ones [restore,minimize,maximize,size,close] their functionality is lost (i.e. maximize button no longer works)

Is there a way to hide these menuitems or move them off the first rank of the system menu? a) make them not visible b) move to a submenu c) delete but still get button messages


Solution

  • a) make them not visible

    The API has no concept of a hidden/invisible menu item.

    b) move to a submenu

    You can move (or rather delete and add) items to a submenu without effecting functionality.

    E.g. move "minimize" to a submenu:

    var
      SysMenu, SubMenu: HMENU;
      StrMin: string;
      StrMinLen: Integer;
    begin
      SysMenu := GetSystemMenu(Handle, False);
      StrMinLen := GetMenuString(SysMenu, SC_MINIMIZE, nil, 0, MF_BYCOMMAND);
      if StrMinLen > 0 then begin
        Inc(StrMinLen);
        SetLength(StrMin, StrMinLen);
        GetMenuString(SysMenu, SC_MINIMIZE, PChar(StrMin), StrMinLen, MF_BYCOMMAND);
        SubMenu := CreateMenu;
        if SubMenu <> 0 then begin
          DeleteMenu(SysMenu, SC_MINIMIZE, MF_BYCOMMAND);
          AppendMenu(SubMenu, MF_STRING, SC_MINIMIZE, PChar(StrMin));
          InsertMenu(SysMenu, 0, MF_BYPOSITION or MF_POPUP, SubMenu, 'Minimize->');
          InsertMenu(SysMenu, 1, MF_BYPOSITION or MF_SEPARATOR, 0, nil);
        end;
      end;
    

    Destroy the submenu before restoring the system menu:

    var
      Info: TMenuItemInfo;
    begin
      Info.cbSize := SizeOf(Info);
      Info.fMask := MIIM_SUBMENU;
      if GetMenuItemInfo(GetSystemMenu(Handle, False), 0, True, Info) then
        DestroyMenu(Info.hSubMenu);
      GetSystemMenu(Handle, True);
    

    c) delete but still get button messages

    If you delete, f.i., the "minimize" item, the system does not send WM_SYSCOMMAND messages for the minimize command to the window. So there won't be any command to respond to.

    You can still listen for button messages, f.i. a left button down. But a button down/up message is not actually the same thing with a button click. A button click consists of three actions, mouse down, capture and up again on the button. If you want to do it anyway an example can be:

    procedure TForm1.WMNCLButtonDown(var Message: TWMNCLButtonDown);
    begin
      inherited;
      if (Message.HitTest = HTMINBUTTON) and not IsIconic(Handle) then
        ShowWindow(Handle, SW_MINIMIZE);
    end;