You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 21:36:30