Safe way to quit Mission planner from plugin class?

Hi.

I’m developing a plugin to apply a “Modern style custom title bar” to mission planner.

It is working well(although it needs modification), but I’m curious about the way to safely quit mission planner from the custom title bar quit button.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MissionPlanner.Utilities;
using System.IO;
using System.Windows.Forms;
using System.Diagnostics;
using System.Threading.Tasks;
using MissionPlanner;
using System.Drawing; // Point Class
using System.Runtime.InteropServices; // dll import(Resizing Window)

namespace MissionPlannerUICustomizer
{
    public class MissionPlannerUICustomizer : MissionPlanner.Plugin.Plugin
    {
        private Panel titleBar;
        private Label titleLabel;
        private Button closeButtonOnTitle;
        private Button minimizeButtonOnTitle;
        private Button maximizeButtonOnTitle;

        // Because we set FormBorderStyle = None, custom dragging event needed.
        // Transparent Panel for dragging window(mouse down, move event)
        private Panel leftEdge;
        private Panel rightEdge;
        private Panel topEdge;
        private Panel bottomEdge;

        private Point mouseOffsetOnTitle;
        private bool isMouseDownOnTitle = false;

        // Win32 API for custom resizing event
        [DllImport("user32.dll")]
        public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);

        // Win32 API for custom resizing event
        [DllImport("user32.dll")]
        public static extern bool ReleaseCapture();

        // Costants for custom window resizing function
        private const int WM_SYSCOMMAND = 0x0112;
        private const int SC_SIZE = 0xF000;

        private const int RESIZE_LEFT = 1;
        private const int RESIZE_RIGHT = 2;
        private const int RESIZE_TOP = 3;
        private const int RESIZE_BOTTOM = 6;

        public override string Name
        {
            get { return "MissionPlannerUICustomizer"; }
        }
        public override string Version
        {
            get { return "2025.06. Developing."; }
        }
        public override string Author
        {
            get { return "Me"; }
        }

        public override bool Init()
        {
            MainV2.instance.FormBorderStyle = FormBorderStyle.None; // For custom title bar
            MainV2.instance.StartPosition = FormStartPosition.Manual; // Window jumps out on the screen
            MainV2.instance.Location = new Point(0, 0);
            // MainV2.instance.Size = new Size(Screen.PrimaryScreen.WorkingArea.Width, Screen.PrimaryScreen.WorkingArea.Height);
            // MainV2.instance.Padding = new Padding(3); // Sometimes putting padding enables dragging.
            return true;     
        }

        public override bool Loaded()
        {
            Console.WriteLine("Mission Planner UI Customizer Plugin Loaded.");
            titleBar = new Panel();
            titleBar.Dock = DockStyle.Top;
            titleBar.Height = 30;
            titleBar.MouseDown += titleBarMouseDown;
            titleBar.MouseMove += titleBarMouseMove;
            titleBar.MouseUp += titleBarMouseUp;
            // We have to implement maximize and minimize function by ourselves because we set FormBorderStyle = None
            titleBar.MouseDoubleClick += titleBarDoubleClick;
            MainV2.instance.Controls.Add(titleBar);

            closeButtonOnTitle = new Button();
            closeButtonOnTitle.FlatStyle = FlatStyle.Flat;
            closeButtonOnTitle.FlatAppearance.BorderSize = 0;
            closeButtonOnTitle.Size = new Size(30, 30);
            closeButtonOnTitle.Location = new Point(titleBar.Width - closeButtonOnTitle.Width, 0);
            closeButtonOnTitle.Anchor = AnchorStyles.Top | AnchorStyles.Right;
            // closeButtonOnTitle.Anchor = AnchorStyles.Right;
            closeButtonOnTitle.Text = "r"; // "x" in Webdings
            closeButtonOnTitle.Font = new Font("Webdings", 10, FontStyle.Bold);
            closeButtonOnTitle.Click += (s, e) =>
          {
              MainV2.instance.Close(); // ????
          };
            titleBar.Controls.Add(closeButtonOnTitle);

            minimizeButtonOnTitle = new Button();
            minimizeButtonOnTitle.FlatStyle = FlatStyle.Flat;
            minimizeButtonOnTitle.FlatAppearance.BorderSize = 0;
            minimizeButtonOnTitle.Size = new Size(30, 30);
            minimizeButtonOnTitle.Location = new Point(titleBar.Width - 90, 0);
            minimizeButtonOnTitle.Anchor = AnchorStyles.Top | AnchorStyles.Right;
            minimizeButtonOnTitle.Text = "0"; // Minimize button on "webdings"
            minimizeButtonOnTitle.Font = new Font("Webdings", 10, FontStyle.Bold);
            minimizeButtonOnTitle.Click += (s, e) =>
            {
                MainV2.instance.WindowState = FormWindowState.Minimized;
            };
            titleBar.Controls.Add(minimizeButtonOnTitle);

            maximizeButtonOnTitle = new Button();
            maximizeButtonOnTitle.FlatStyle = FlatStyle.Flat;
            maximizeButtonOnTitle.FlatAppearance.BorderSize = 0;
            maximizeButtonOnTitle.Size = new Size(30, 30);
            maximizeButtonOnTitle.Location = new Point(titleBar.Width - 60, 0);
            maximizeButtonOnTitle.Anchor = AnchorStyles.Top | AnchorStyles.Right;
            maximizeButtonOnTitle.Text = "1"; // maximize button in webdings
            maximizeButtonOnTitle.Font = new Font("Webdings", 10, FontStyle.Bold);
            maximizeButtonOnTitle.Click += (s, e) =>
            {
                if (MainV2.instance.Size == Screen.FromHandle(MainV2.instance.Handle).WorkingArea.Size)
                {
                    MainV2.instance.WindowState = FormWindowState.Normal;
                    maximizeButtonOnTitle.Text = "1"; // Maximize icon
                }
                else
                {
                    Rectangle workingArea = Screen.FromHandle(MainV2.instance.Handle).WorkingArea;
                    MainV2.instance.Location = workingArea.Location;
                    MainV2.instance.Size = workingArea.Size;
                    maximizeButtonOnTitle.Text = "2"; // Return to Normal Window Icon
                }
            };
            titleBar.Controls.Add(maximizeButtonOnTitle);

            titleLabel = new Label();
            titleLabel.Text = "Custom Title Bar 20250630";
            titleLabel.Font = new Font("Segoe UI", 10, FontStyle.Bold);
            titleLabel.TextAlign = ContentAlignment.MiddleLeft;
            titleLabel.AutoSize = false;
            titleLabel.Dock = DockStyle.Left;
            titleLabel.Width = titleBar.Width - 3 * closeButtonOnTitle.Width; // Calculation timing is important
            Console.WriteLine($"title label width : {titleLabel.Width}");
            titleBar.Controls.Add(titleLabel);
            titleLabel.BringToFront(); // No Overlap
            titleLabel.MouseDown += titleBarMouseDown;
            titleLabel.MouseUp += titleBarMouseUp;
            titleLabel.MouseMove += titleBarMouseMove;
            titleLabel.MouseDoubleClick += titleBarDoubleClick;

            int _BORDER_SIZE = 2;
            // Draw borderlines for mission planner program
            MainV2.instance.Paint += (s, e) =>
            {
                Color borderColor = Color.FromArgb(74, 83, 86);
                ControlPaint.DrawBorder(e.Graphics, MainV2.instance.ClientRectangle,
                    borderColor, _BORDER_SIZE, ButtonBorderStyle.Solid,
                    borderColor, _BORDER_SIZE, ButtonBorderStyle.Solid,
                    borderColor, _BORDER_SIZE, ButtonBorderStyle.Solid,
                    borderColor, _BORDER_SIZE, ButtonBorderStyle.Solid);
            };

            // Transparent panel for resizing window
            leftEdge = new Panel();
            leftEdge.Width = _BORDER_SIZE;
            leftEdge.Dock = DockStyle.Left;
            leftEdge.BackColor = Color.Transparent;
            leftEdge.Cursor = Cursors.SizeWE; // West & East
            MainV2.instance.Controls.Add(leftEdge);
            rightEdge = new Panel();
            rightEdge.Width = _BORDER_SIZE;
            rightEdge.Dock = DockStyle.Right;
            rightEdge.BackColor = Color.Transparent;
            rightEdge.Cursor = Cursors.SizeWE; // West & East
            MainV2.instance.Controls.Add(rightEdge);
            bottomEdge = new Panel();
            bottomEdge.Height = _BORDER_SIZE;
            bottomEdge.Dock = DockStyle.Bottom;
            bottomEdge.BackColor = Color.Transparent;
            bottomEdge.Cursor = Cursors.SizeNS; // North & South
            MainV2.instance.Controls.Add(bottomEdge);
            topEdge = new Panel();
            topEdge.Height = _BORDER_SIZE;
            topEdge.Dock = DockStyle.Top;
            topEdge.BackColor = Color.Transparent;
            topEdge.Cursor = Cursors.SizeNS; // North & South
            MainV2.instance.Controls.Add(topEdge);

            leftEdge.MouseDown += (s, e) =>
            {
                if (e.Button == MouseButtons.Left)
                {
                    ReleaseCapture();
                    SendMessage(MainV2.instance.Handle, WM_SYSCOMMAND, SC_SIZE + RESIZE_LEFT, 0);
                }
            };

            rightEdge.MouseDown += (s, e) =>
            {
                if (e.Button == MouseButtons.Left)
                {
                    ReleaseCapture();
                    SendMessage(MainV2.instance.Handle, WM_SYSCOMMAND, SC_SIZE + RESIZE_RIGHT, 0);
                }
            };

            topEdge.MouseDown += (s, e) =>
            {
                if (e.Button == MouseButtons.Left)
                {
                    ReleaseCapture();
                    SendMessage(MainV2.instance.Handle, WM_SYSCOMMAND, SC_SIZE + RESIZE_TOP, 0);
                }
            };

            bottomEdge.MouseDown += (s, e) =>
            {
                if (e.Button == MouseButtons.Left)
                {
                    ReleaseCapture();
                    SendMessage(MainV2.instance.Handle, WM_SYSCOMMAND, SC_SIZE + RESIZE_BOTTOM, 0);
                }
            };

            MainV2.instance.Resize += (s, e) =>
            {
                //closeButtonOnTitle.Location = new Point(titleBar.Width - closeButtonOnTitle.Width, 0); 
                //minimizeButtonOnTitle.Location = new Point(titleBar.Width - 90, 0);
                //maximizeButtonOnTitle.Location = new Point(titleBar.Width - 60, 0);
                titleLabel.Width = titleBar.Width - 3 * closeButtonOnTitle.Width;
            };

            ApplyCustomStyles();

            this.loopratehz = 0;
            return false;
        }

        // Implement dragging function
        private void titleBarMouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                if (MainV2.instance.Size == Screen.FromHandle(MainV2.instance.Handle).WorkingArea.Size)
                {
                    MainV2.instance.WindowState = FormWindowState.Normal;
                    maximizeButtonOnTitle.Text = "1";
                }

                mouseOffsetOnTitle = new Point(-e.X, -e.Y); 
                isMouseDownOnTitle = true;
            }
        }

        private void titleBarMouseMove(object sender, MouseEventArgs e)
        {
            if (isMouseDownOnTitle)
            {
                Point mousePos = Control.MousePosition;
                mousePos.Offset(mouseOffsetOnTitle.X, mouseOffsetOnTitle.Y);
                MainV2.instance.Location = mousePos;
            }
        }

        private void titleBarMouseUp(object sender, MouseEventArgs e)
        {
            isMouseDownOnTitle = false;
        }

        private void titleBarDoubleClick(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                if (MainV2.instance.Size == Screen.FromHandle(MainV2.instance.Handle).WorkingArea.Size)
                {
                    MainV2.instance.WindowState = FormWindowState.Normal;
                    maximizeButtonOnTitle.Text = "1";
                }
                else
                {
                    // MainV2.instance.WindowState = FormWindowState.Maximized;
                    Rectangle workingArea = Screen.FromHandle(MainV2.instance.Handle).WorkingArea;
                    MainV2.instance.Location = workingArea.Location;
                    MainV2.instance.Size = workingArea.Size;
                    maximizeButtonOnTitle.Text = "2"; 
                }
            }
        }

        private void ApplyCustomStyles()
        {
            titleBar.ForeColor = Color.FromArgb(70, 70, 70);
            titleBar.BackColor = Color.FromArgb(74, 83, 86);
            titleLabel.ForeColor = Color.DarkGray;
            titleLabel.BackColor = Color.Transparent;
            closeButtonOnTitle.ForeColor = Color.DarkGray; 
            closeButtonOnTitle.BackColor = Color.FromArgb(74, 83, 86);
            minimizeButtonOnTitle.ForeColor = Color.DarkGray;
            minimizeButtonOnTitle.BackColor = Color.FromArgb(74, 83, 86);
            maximizeButtonOnTitle.ForeColor = Color.DarkGray;
            maximizeButtonOnTitle.BackColor = Color.FromArgb(74, 83, 86);
        }

        public override bool Exit()
        {
            return false;
        }
    } // end of plugin class
} // end of namespace MissionPlannerUICustomizer

This code contains following line :

closeButtonOnTitle.Click += (s, e) =>
{
       MainV2.instance.Close(); // ????
};

This calls MainV2.instance.Close() directly when the custom close button is clicked.

I wonder this is correct way to quit mission planner.

There is Application.Quit() ,and other many ways to do this.

I tested turning on and off more than 50 times but there was no issue.

However, I’d really appreciate confirmation from an expert or someone familiar with Mission Planner’s internals