要使NotifyIcon 工作,您必须启动一个消息循环,通常调用Application.Run()。调用方法通常也标记为单线程([STAThread])。
差不多就是这样。
▶ 当然,您需要处理您创建的对象。在这种情况下,NotifyIcon 对象和ContextMenu。您还可以在 Icon 对象上调用 Dispose(),以防它在内部 NativeWindow 中设置为 null。
在此处的示例中,ConsoleNotifyIcon 类对象用于运行消息循环并接收 ContextMenu 项鼠标事件。
在这种情况下,退出单击处理程序向主线程发出信号,退出请求已排队。它还会从通知区域中删除 NotifyIcon。
然后主线程可以确认请求并终止。
它还确保在退出之前 NotifyIcon 已被释放。
▶ 您可以在CloseRequest 事件处理程序中使用Environment.Exit()。
在这里,AppDomain.ProcessExit 事件被处理以响应Environment.Exit(),SetConsoleCtrlHandler 处理其他退出原因(参见代码中的注释)。
在任何情况下,都会调用 CleanUp() 方法,以删除剩余的事件处理程序并释放 NotifyIcon 对象分配的资源。
private static readonly object closeLocker = new object();
private static ConsoleEventDelegate closeHandler;
private delegate bool ConsoleEventDelegate(ExitReason closeReason);
private enum ExitReason
{
ControlC = 0,
ControlBreak = 1,
UserClose = 2,
UserLogoff = 5,
SystemShutdown = 6
}
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetConsoleCtrlHandler(ConsoleEventDelegate HandlerRoutine, bool Add);
[STAThread]
static void Main(string[] args)
{
// Handles Close Button, CTRL-C, CTRL-Break, Logoff and ShutDown
closeHandler = new ConsoleEventDelegate(ConsoleEventHandler);
SetConsoleCtrlHandler(closeHandler, true);
// Handles Environment.Exit()
AppDomain.CurrentDomain.ProcessExit += OnProcessExit;
// Add a handler to the NotifyIcon CloseRequest event
ConsoleNotifyIcon.CloseRequest += NotifyIconCloseRequest;
// Create the NotifyIcon Icon in the Tray Notification Area
ConsoleNotifyIcon.GenerateTrayIcon();
// [...]
// Other processes
Console.ReadLine();
// Raises the ProcessExit event
Environment.Exit(0);
}
// Event raised by the NotifyIcon's Exit routine.
// Causes OnProcessExit to fire
private static void NotifyIconCloseRequest(object sender, EventArgs e) => Environment.Exit(0);
// Fires when Environment.Exit(0) is called
private static void OnProcessExit(object sender, EventArgs e) => CleanUp();
// Handles - Console Close Button, Control-C, Control-Break
// - System Log-off event, System ShutDown event
static bool ConsoleEventHandler(ExitReason reason)
{
SetConsoleCtrlHandler(closeHandler, false);
CleanUp();
return true;
}
// All Console Exit reasons end up here
private static void CleanUp()
{
// This is called from a different Thread
lock (closeLocker) {
AppDomain.CurrentDomain.ProcessExit -= OnProcessExit;
ConsoleNotifyIcon.CloseRequest -= NotifyIconCloseRequest;
if (!ConsoleNotifyIcon.IsDisposed) {
ConsoleNotifyIcon.Dispose();
}
}
}
ConsoleNotifyIcon 类(NotifyIcon Handler):
using System.Threading.Tasks;
using System.Windows.Forms;
public class ConsoleNotifyIcon
{
public static event EventHandler<EventArgs> CloseRequest;
// Store these objects as private Fields
private static NotifyIcon trayIcon;
private static ContextMenu trayContextMenu;
// The main public method starts a new Thread, in case a STA Thread is needed
// If not, you can just Task.Run() it
public static void GenerateTrayIcon()
{
var thread = new Thread(StartTrayIcon);
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
[STAThread] // Reminder
private static void StartTrayIcon() {
trayContextMenu = new ContextMenu();
trayContextMenu.MenuItems.Add(0, new MenuItem("Show", Show_Click));
trayContextMenu.MenuItems.Add(1, new MenuItem("Exit", Exit_Click));
trayIcon = new NotifyIcon() {
ContextMenu = trayContextMenu
Icon = [Some Icon], // Possibly, use an Icon Resource
Text = "Cursor is locked to primary screen",
Visible = true,
};
// Setup completed. Starts the Message Loop
Application.Run();
}
public static bool IsAppCloseRequest { get; private set; }
public static bool IsDisposed { get; private set; }
static void Exit_Click(object sender, EventArgs e) {
// Sets the public property, it can be used to check the status
IsAppCloseRequest = true;
// Signals the Exit request, raising the CloseRequest event.
// The application may decide to delay the exit process, so calling Dispose()
// is handled by the subscribers of the event, as shown in the Console code
CloseRequest?.Invoke(null, EventArgs.Empty);
}
static void Show_Click(object sender, EventArgs e) {
// Do something
}
public static void Dispose() {
if (IsDisposed) return;
Application.ExitThread();
trayIcon?.Icon?.Dispose();
trayIcon?.Dispose();
trayContextMenu?.Dispose();
IsDisposed = true;
}
}