Home > Enterprise >  ContextMenu disappears/closes immediately after being opened
ContextMenu disappears/closes immediately after being opened

Time:10-03

I'm trying to figure out why the System.Windows.Forms.ContextMenu in my Windows Forms application is closing immediately after I open it by right-clicking its tray icon on the taskbar.

This doesn't occur immediately, but strangely starts happening on the second opening. (i.e. I can open/close the context menu once normally and from then on the issue crops up) After right-clicking, I can visually see the menu appear for a fraction of a second before it automatically closes.

described behavior

Here is the C# code in Program.cs. I was originally following one of Barnacules Nerdgasm's "#Codegasm" tutorials (YouTube, GitHub), which involved removing the forms aspect of the project, leaving this as the only source file.

using System;
using System.Windows.Forms;
using System.Threading;
using System.Net.NetworkInformation;
using System.Net;
using System.Drawing;

namespace PingGoogleDNS
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            PingGoogleDNS pinger = new PingGoogleDNS();
            pinger.StartPinger();
        }

        class PingGoogleDNS
        {
            #region Global Thread and Icon Objects
            NotifyIcon pingIcon;
            Icon goodIcon = new Icon("good_connection.ico");
            Icon weakIcon = new Icon("weak_connection.ico");
            Icon noIcon = new Icon("no_connection.ico");

            Thread pingWorker;
            MenuItem roundTripTime;
            #endregion

            #region StartPinger
            public void StartPinger()
            {
                pingIcon = new NotifyIcon();
                pingIcon.Icon = noIcon /*disconnectedIcon*/;
                pingIcon.Visible = true;

                MenuItem quit = new MenuItem("Quit / Exit");
                MenuItem name = new MenuItem("Pings Google DNS (8.8.8.8)");
                roundTripTime = new MenuItem("Latest ping: N/A ms");
                ContextMenu contextMenu = new ContextMenu();
                contextMenu.MenuItems.Add(quit);
                contextMenu.MenuItems.Add(name);
                contextMenu.MenuItems.Add(roundTripTime);
                pingIcon.ContextMenu = contextMenu;

                quit.Click  = Quit_Click;

                pingWorker = new Thread(new ThreadStart(PingSenderThread));
                pingWorker.Start();
                Console.WriteLine("Start");
            }
            #endregion

            #region Quit Button Handler
            private void Quit_Click(object sender, EventArgs e)
            {
                pingIcon.Dispose();
                pingWorker.Abort();
                Environment.Exit(1);
            }
            #endregion

            #region Ping Sender Thread
            public void PingSenderThread()
            {
                Ping pingSender = new Ping();

                IPAddress iPAddress;
                IPAddress.TryParse("8.8.8.8", out iPAddress);

                PingReply reply;
                try
                {
                    while (true)
                    {
                        try
                        {
                            reply = pingSender.Send(iPAddress, 2000 /* ms */);

                            if (reply.Status == IPStatus.Success)
                            {
                                if (reply.RoundtripTime < 500 /* ms */)
                                    pingIcon.Icon = goodIcon /*connectedIcon*/;
                                else
                                    pingIcon.Icon = weakIcon;

                                pingIcon.Text = "8.8.8.8 ping success! ("   reply.RoundtripTime   " ms)";
                                roundTripTime.Text = "Latest ping: "   reply.RoundtripTime   " ms";

                                Thread.Sleep(1000);
                            }
                        }
                        catch (PingException pe)
                        {
                            pingIcon.Icon = noIcon /*disconnectedIcon*/;
                            pingIcon.Text = "8.8.8.8 ping failure.";
                            roundTripTime.Text = "Latest ping: N/A ms";
                        }
                    }
                }
                catch (ThreadAbortException)
                {
                    // No need to do anything, just catch the ThreadAbortException.
                }
            }
            #endregion
        }
    }
}

I tried to debug this by dummy adding listeners to the ContextMenu's Popup and Collapse events to see if it registered being closed:

// In StartPinger()
contextMenu.Popup  = ContextMenu_Popup;
contextMenu.Collapse  = ContextMenu_Collapse;

// ...

private void ContextMenu_Popup(object sender, EventArgs e)
{
     Console.WriteLine("menu opened");
}

private void ContextMenu_Collapse(object sender, EventArgs e)
{
     Console.WriteLine("menu closed");
}

But, I only see "menu opened" being printed to the console when I right-click and no "menu closed" are ever printed. This has confused me and I don't know what to try next. Has anyone else encountered this before?

CodePudding user response:

You're missing a message loop (a.k.a. message pump). Add this to the end of your Main function.

Application.Run();

Every event driven application such as yours enters an endless loop at some point where it processes events and dispatches to handlers. That's what Application.Run() is. (Read the Remarks section of the documentation for a more complete description.) You need this message loop for any UI-related stuff to work properly, such as mouse clicks, windows, and popup menus.

Also, it looks like you are going to have cross-thread issues trying to update icons from your pinger thread. All UI-related stuff should happen on that Main thread. If you need another thread to do network IO such as your ping, then you will have to Invoke any code that updates UI so that that code runs in the context of your Main thread.

  • Related