종료 되었지만 남아있는 tray 아이콘

 

C# 에서 정상적으로 프로그램이 종료되면 Tray 아이콘이 제거 되긴 합니다만,

프로세스 킬, 비정상적인 종료 방법이라면 위와 같이 트레이 아이콘이 남는 경우가 있습니다.

 

해당 아이콘은 mouse hover 이벤트 시 윈도우 내부적으로 해당 프로그램이 동작하고 있는지 판별하기에

mouse move 이벤트를 통해 해당 tray들을 정리할 수 있습니다. 아래는 코드!

 

[생성, 정리 후]

생성 버튼 클릭 (8번)
정리 버튼 클릭

 

 

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        class TrayRefresher
        {
            [StructLayout(LayoutKind.Sequential)]
            struct RECT
            {
                public int Left;
                public int Top;
                public int Right;
                public int Bottom;
            }

            [DllImport("user32.dll")]
            static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, string windowName);

            [DllImport("user32.dll")]
            static extern bool GetWindowRect(IntPtr hWnd, out RECT rect);

            [DllImport("user32.dll")]
            static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);

            const int WM_MOUSEMOVE = 0x0200;
            
            public void RefreshTray()
            {
                /*
                Shell_TrayWnd (하단의 작업 표시줄)
                 ├─ TrayNotifyWnd (시스템 트레이 영역 - 시계, 네트워크 아이콘, 볼륨 조절 등)
                 │    └─ SysPager (아이콘을 스크롤하거나 관리)
                 │         └─ ToolbarWindow32 (보이는 트레이 아이콘들)
                 │
                 └─ NotifyIconOverflowWindow (숨겨진 아이콘 영역) - 숨기기/펼치기 영역
                      └─ ToolbarWindow32 (숨겨진 아이콘들)
                */

                //보여지는 영역
                IntPtr toolbar = FindToolbar("Shell_TrayWnd", "TrayNotifyWnd", "SysPager", "ToolbarWindow32");
                SendMouseMove(toolbar);

                //감춰진 영역도 추가
                toolbar = FindToolbar("NotifyIconOverflowWindow", "ToolbarWindow32");
                SendMouseMove(toolbar);
            }

            private IntPtr FindToolbar(params string[] targets)
            {
                IntPtr hWnd = IntPtr.Zero;

                foreach (string target in targets)
                {
                    hWnd = FindWindowEx(hWnd, IntPtr.Zero, target, null);

                    if (hWnd == null)
                    {
                        break;
                    }
                }

                return hWnd;
            }

            private void SendMouseMove(IntPtr hWnd)
            {
                if (hWnd == null)
                {
                    return;
                }

                if (GetWindowRect(hWnd, out RECT rect))
                {
                    int width = rect.Right - rect.Left;
                    int height = rect.Bottom - rect.Top;
                    int offset = 5; //아이콘 간격

                    for (int x = 0; x < width; x += offset)
                    {
                        for (int y = 0; y < height; y += offset)
                        {
                            //4byte lParam에 2byte씩 마우스 위치를 넣어줍니다.
                            //mouse y 2byte, mouse x 2byte
                            IntPtr lParam = (IntPtr)((y << 16) | (x & 0xFFFF));
                            SendMessage(hWnd, WM_MOUSEMOVE, IntPtr.Zero, lParam);
                        }
                    }
                }
            }
        }

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Task.Run(async () => //테스트용 트레이 아이콘 생성!
            {
                //테스트용 프로그램            
                Process process = Process.Start(@"...\trayTest.exe");
                await Task.Delay(1000);
                process.Kill(); //강제 종료 시 트레이 아이콘이 남습니다.
            });
        }

        private void button2_Click(object sender, EventArgs e)
        {
            new TrayRefresher().RefreshTray(); //트레이 아이콘 정리!
        }
    }
}

+ Recent posts