Windows applications often add an icon to the system tray to remove themselves from the task bar when minimized, and use that icon in the system tray for a context menu and/or changing image to represent application state.  Unfortunately, it is common for the system tray icon to be “left behind” after the application terminates.  It leaves the user with the false impression that the application is still running.

This can even happen when the application is gracefully shutdown: not just when the app is terminated by the task manager or another non-human-initiated event.  The icon will appear in the system tray until the mouse hovers over the tray, at which time the orphaned icons are cleaned up. What exact event is triggered by hovering the mouse pointer there to remove it, I don’t know.

Strangely, even Microsoft’s Visual Studio IDE will display the same behavior when the internal web server runs to execute/debug a Web Application.  If you select Debug / Terminate while the app is still running, the gear-in-a-page icon representing the internal web service will remain in the tray.  Start the app again (with F5, etc), and another icon will appear.  Start and Terminate the app enough times, and you’ll fill the tray with those icons.  And hovering the mouse over the tray just once will clean out all the icons which were already terminated.  Note: If you right click the tray icon while it is active, and select “Stop”, the internal web server will stop and properly remove the icon from the system tray.

The problem appears to be that the NotifyIcon object must have its Dispose() method called by the WinForm to terminate it and (get this) Windows will not properly remove the icon from the system tray unless an idle state occurs after the Dispose() method call, and before the application is closed.

If you’re confused, don’t worry.  There is evidently one or more events not firing after the TrayNotificationIcon’s Dispose() method when the application is closing, but the event(s) do fire if the form returns to an idle state after the Dispose() method.

That being said, here is how to force an idle state to prevent orphaning the icon in the system tray.

This project on GitHub contains a single Windows form with a tray icon.  That tray icon has a context menu associated to it, which you activate with a right-click.  It contains options to restore the window when it is hidden, and an option to terminate the application.  The application can also be closed by a button on the form, or the standard close button in the form’s upper right corner. To test it, you can also terminate the application from Task Manager.  Note: I coded the closing event so that it will detect whether a user requested the form closure, and will prompt for confirmation if so.  Anything else closing the form will not generate a user prompt: this includes Task Manager.

The Form has a timer object, which is used to actually close the application.  The timer in combination with the module-level boolean variable Terminating (defaulting to false), is used in the FormClosing event to execute the Dispose() method, then return to the FormClosing event after the idle state occurs. See lines 29-76 of Form1.cs for the critical code which makes it work.

I set the timer for a 100ms delay before activating the shutdown.  In reality, this delay could be lower.  I just choose 1/10th of a second because users won’t really notice the delay.

I have seen other solutions that attempt to dive down into manually managing handles.  In reality, a C# programmer should not need to go to this level except in very rare circumstances.