为什么我看到了多个系统托盘图标?

13

我已经在我的应用程序中添加了通知图标,但很常见地在系统托盘中看到多达3个副本的通知图标。这是有原因的吗?

有没有办法阻止它发生。

通常,在我的应用程序关闭后,这种情况仍会持续存在,直到我将鼠标移到系统托盘上并展开和折叠系统托盘,然后它们都会消失。


你是怎么添加 NotifyIcon 的?是在设计器中还是在代码中? - OregonGhost
在应用程序关闭时,您如何删除它? - Rowland Shaw
设计师和我都不删除它,我应该吗? - Omar Kooheji
不,如果您是使用设计师完成的,则NotifyIcon由窗体拥有并由其释放。也就是说,如果窗体被正确释放且未停止,就像Richard Slaters的答案一样 :) - OregonGhost
4个回答

26

这是在你调试应用程序时出现的吗?如果是,那么这是因为从系统托盘中删除图标的消息仅在应用程序正常退出时发送,如果它由于异常或通过 Visual Studio 终止而终止,则图标将保留,直到你将鼠标悬停在其上。


我讨厌在系统托盘中有图标的应用程序上工作,并使用Visual Studio停止它们。如果我不将鼠标悬停在它们上面,最终会出现数十个这样的图标。 - Samuel
6
虽然时不时地活动一下手臂可能有益健康 :p - Svish
2
是的,但在24英寸的显示器上,这对鼠标来说是一次相当长的旅程。 ;) - Samuel
即使我的配置是“Release”而不是“debug”,我仍然面临着相同的问题:( - Swanand

11

你可以通过父窗口的关闭事件来结束图标。这在我的WPF应用程序中有效,即使在Visual Studio(我是2010版本)中测试也有效:

        parentWindow.Closing += (object sender, CancelEventArgs e) =>
        {
            notifyIcon.Visible = false;
            notifyIcon.Icon = null;
            notifyIcon.Dispose();
        };

这段代码应该放在哪里?如何访问parentwindow? - Demodave

2

我做了什么:

  1. Create a class library that updates the system tray.

    using System;
    using System.Diagnostics;
    using System.Runtime.InteropServices;
    
    namespace SystrayUtil
    {
        internal enum MessageEnum
        {
            WM_MOUSEMOVE = 0x0200,
            WM_CLOSE = 0x0010,
        }
    
        internal struct RECT
        {
            internal int Left;
            internal int Top;
            internal int Right;
            internal int Bottom;
    
            internal RECT(int left, int top, int right, int bottom)
            {
                Left = left;
                Top = top;
                Right = right;
                Bottom = bottom;
            }
        }
    
        public sealed class Systray
        {
            [DllImport("user32.dll", SetLastError = true)]
            private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
    
            [DllImport("user32.dll", SetLastError = true)]
            private static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, IntPtr lpszWindow);
    
            [DllImport("user32.dll", SetLastError = true)]
            private static extern IntPtr SendMessage(IntPtr hWnd, int message, uint wParam, long lParam);
    
            [DllImport("user32.dll", SetLastError = true)]
            private static extern bool GetClientRect(IntPtr hWnd, out RECT usrTray);
    
            public static void Cleanup()
            {
                RECT sysTrayRect = new RECT();
                IntPtr sysTrayHandle = FindWindow("Shell_TrayWnd", null);
                if (sysTrayHandle != IntPtr.Zero)
                {
                    IntPtr childHandle = FindWindowEx(sysTrayHandle, IntPtr.Zero, "TrayNotifyWnd", IntPtr.Zero);
                    if (childHandle != IntPtr.Zero)
                    {
                        childHandle = FindWindowEx(childHandle, IntPtr.Zero, "SysPager", IntPtr.Zero);
                        if (childHandle != IntPtr.Zero)
                        {
                            childHandle = FindWindowEx(childHandle, IntPtr.Zero, "ToolbarWindow32", IntPtr.Zero);
                            if (childHandle != IntPtr.Zero)
                            {
                                bool systrayWindowFound = GetClientRect(childHandle, out sysTrayRect);
                                if (systrayWindowFound)
                                {
                                    for (int x = 0; x < sysTrayRect.Right; x += 5)
                                    {
                                        for (int y = 0; y < sysTrayRect.Bottom; y += 5)
                                        {
                                            SendMessage(childHandle, (int)MessageEnum.WM_MOUSEMOVE, 0, (y << 16) + x);
                                        }
                                    }
                                }
                            }
                        }
                    } 
                }
            }
        }
    }
    
  2. Copy the dll to "%ProgramFiles%\Microsoft Visual Studio x.x\Common7\IDE\PublicAssemblies\SystrayUtil.dll"

    Where x.x is the version number of Visual Studio

  3. Record a macro and save it

  4. Edit the macro

    Add a reference to the created dll.

    Add Imports SystrayUtil to the list of imports at the top of Module EnvironmentEvents.

    Remove any unwanted items and add the following code to the EnvironmentEvents module

    Public Sub DebuggerEvents_OnEnterDesignMode(ByVal Reason As EnvDTE.dbgEventReason) Handles DebuggerEvents.OnEnterDesignMode
    Systray.Cleanup()
    MsgBox("Entered design mode!")
    End Sub
    
  5. If it works remove MsgBox("Entered design mode!") because it's annoying to have a message box popping up every time you return from a debugging session.


0

当您正常关闭应用程序时,这应该可以工作:

// in form's constructor
Application.ApplicationExit += new EventHandler(this.OnApplicationExit);

private void OnApplicationExit(object sender, EventArgs e)
{
    try
    {
        if (notifyIcon1!= null)
        {
            notifyIcon1.Visible = false;
            notifyIcon1.Icon = null;
            notifyIcon1.Dispose();
            notifyIcon1= null;
        }
    }
    catch { }
}

当您使用Visual Studio停止调试按钮停止应用程序时,进程将被终止,不会触发任何Dispose事件。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接