如何用C#在Windows10/11任务栏右侧显示文本?
Windows 10/11任务栏右侧图标旁显示文本的实现详解
核心思路
Stack Overflow上那个答案的本质是绕开原生托盘图标的限制——原生托盘只支持图标,要显示文本得自己在托盘区域绘制。具体是通过Windows API找到系统托盘的窗口,然后在我们的图标旁边用GDI+绘制文本,同时处理托盘刷新的问题。
一步步拆解实现
1. 先搞定基础托盘图标
不管要不要显示文本,先得有个能在托盘显示的图标,这是基础:
[DllImport("shell32.dll")] private static extern bool Shell_NotifyIcon(uint dwMessage, ref NOTIFYICONDATA lpData); [StructLayout(LayoutKind.Sequential)] public struct NOTIFYICONDATA { public uint cbSize; public IntPtr hWnd; public uint uID; public uint uFlags; public uint uCallbackMessage; public IntPtr hIcon; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] public string szTip; } // 初始化托盘图标 private void InitTrayIcon() { NOTIFYICONDATA nid = new NOTIFYICONDATA(); nid.cbSize = (uint)Marshal.SizeOf(nid); nid.hWnd = this.Handle; nid.uID = 1; nid.uFlags = 0x00000001 | 0x00000002; // NIF_ICON | NIF_MESSAGE nid.uCallbackMessage = 0x0400; // WM_USER nid.hIcon = SystemIcons.Application.Handle; nid.szTip = "任务栏文本演示"; Shell_NotifyIcon(0x00000000, ref nid); // NIM_ADD:添加托盘图标 }
2. 找到系统托盘的窗口句柄
要在托盘上画东西,得先找到托盘对应的窗口,Windows的托盘窗口是嵌套的,需要一层层找:
[DllImport("user32.dll")] private static extern IntPtr FindWindow(string lpClassName, string lpWindowName); [DllImport("user32.dll")] private static extern IntPtr FindWindowEx(IntPtr hWndParent, IntPtr hWndChildAfter, string lpClassName, string lpWindowName); // 获取托盘的Toolbar窗口句柄 private IntPtr GetTrayToolbarWnd() { IntPtr trayWnd = FindWindow("Shell_TrayWnd", null); if (trayWnd != IntPtr.Zero) { trayWnd = FindWindowEx(trayWnd, IntPtr.Zero, "TrayNotifyWnd", null); trayWnd = FindWindowEx(trayWnd, IntPtr.Zero, "SysPager", null); trayWnd = FindWindowEx(trayWnd, IntPtr.Zero, "ToolbarWindow32", null); } return trayWnd; }
3. 在托盘区域绘制文本
拿到托盘窗口后,确定文本绘制的位置,用GDI+直接在窗口上画文本:
[DllImport("user32.dll")] private static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect); [StructLayout(LayoutKind.Sequential)] public struct RECT { public int Left; public int Top; public int Right; public int Bottom; } private void DrawCustomText() { IntPtr trayToolbar = GetTrayToolbarWnd(); if (trayToolbar == IntPtr.Zero) return; // 获取托盘窗口的屏幕位置 GetWindowRect(trayToolbar, out RECT trayRect); // 这里的坐标是估算值,你可以根据自己图标的位置调整,比如往右偏移图标宽度+间距 int textX = trayRect.Right - 140; int textY = trayRect.Top + 4; // 用GDI+绘制文本 using (Graphics g = Graphics.FromHwnd(trayToolbar)) { using (Font font = new Font("微软雅黑", 9)) { g.DrawString("你的自定义文本", font, Brushes.Black, textX, textY); } } }
4. 处理托盘刷新问题
系统托盘会经常刷新(比如点击开始菜单、任务栏布局变化),刷新后我们画的文本会消失,所以要监听托盘的刷新消息,重新绘制:
protected override void WndProc(ref Message m) { base.WndProc(ref m); // 监听窗口绘制消息,托盘刷新时会触发这个消息 if (m.Msg == 0x000F) // WM_PAINT { DrawCustomText(); } }
避坑提示
- Windows 10和11的托盘窗口结构可能有细微差别,比如Windows 11的某些版本类名调整,需要自己测试调整
GetTrayToolbarWnd里的类名 - 这种方式属于“非常规实现”,系统更新可能导致失效,做好兼容测试
- 高DPI屏幕下要处理缩放,否则文本位置会偏,建议用
Graphics.DpiX/Graphics.DpiY来计算坐标 - 退出程序时记得调用
Shell_NotifyIcon(0x00000002, ref nid)(NIM_DELETE)删除托盘图标,避免残留
内容的提问来源于stack exchange,提问作者LeleEdits
相关产品推荐
相关产品推荐

