How can I customize the system menu of a Windows Form?

Windows makes it fairly easy to get a handle to a copy of the form’s system menu for customization purposes with the GetSystemMenu function. The hard part is that you’re on your own to perform the appropriate modifications to the menu it returns, using functions such as AppendMenu, InsertMenu, and DeleteMenu just as you would if you were programming directly against the Win32 API.

However, if all you want to do is add a simple menu item, it’s really not all that difficult. For example, you would only need to use the AppendMenu function because all you want to do is add an item or two to the end of the menu. Doing anything more advanced (like inserting an item in the middle of the menu, displaying a bitmap on the menu item, showing menu items checked, setting a default menu item, etc.) requires a bit more work. But once you know how it’s done, you can go wild. The documentation on menu-related functions tells all.

Here’s the complete code for a form that adds a separator line and an “About” item to the bottom of its system menu (also called a window menu):

using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;

public class CustomForm : Form
{
    // P/Invoke constants
    private const int WM_SYSCOMMAND = 0x112;
    private const int MF_STRING = 0x0;
    private const int MF_SEPARATOR = 0x800;

    // P/Invoke declarations
    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern bool AppendMenu(IntPtr hMenu, int uFlags, int uIDNewItem, string lpNewItem);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern bool InsertMenu(IntPtr hMenu, int uPosition, int uFlags, int uIDNewItem, string lpNewItem);


    // ID for the About item on the system menu
    private int SYSMENU_ABOUT_ID = 0x1;

    public CustomForm()
    {
    }

    protected override void OnHandleCreated(EventArgs e)
    {
        base.OnHandleCreated(e);

        // Get a handle to a copy of this form's system (window) menu
        IntPtr hSysMenu = GetSystemMenu(this.Handle, false);

        // Add a separator
        AppendMenu(hSysMenu, MF_SEPARATOR, 0, string.Empty);

        // Add the About menu item
        AppendMenu(hSysMenu, MF_STRING, SYSMENU_ABOUT_ID, "&About…");
    }

    protected override void WndProc(ref Message m)
    {
        base.WndProc(ref m);

        // Test if the About item was selected from the system menu
        if ((m.Msg == WM_SYSCOMMAND) && ((int)m.WParam == SYSMENU_ABOUT_ID))
        {
            MessageBox.Show("Custom About Dialog");
        }

    }
}

And here’s what the finished product looks like:

  Form with custom system menu

Leave a Comment