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

DPI变更后如何正确更新托盘图标?

托盘图标在任务栏DPI变更时模糊的解决方案

问题描述

我用以下标准C++代码创建托盘图标:

NOTIFYICONDATA data;
// ...
data.hIcon = (HICON)LoadImage(inst, L"name", IMAGE_ICON,
    GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_DEFAULTCOLOR);
// ...
Shell_NotifyIcon(NIM_ADD, &data);

但当任务栏所在显示器的DPI变更时,图标会被拉伸变得模糊,如图所示:
模糊的托盘图标

网上建议用WM_DPICHANGED消息处理,但根据文档:

当窗口的有效每英寸点数(dpi)发生变化时发送此消息。DPI是窗口的缩放因子,多种事件会导致DPI变化,包括:

  • 窗口被移动到DPI不同的新显示器;
  • 窗口所在显示器的DPI发生变化。

这意味着如果主显示器DPI变更,但我的窗口在另一台显示器上,就收不到该消息。我试过创建隐藏窗口并固定在主显示器,但不够优雅;也考虑过定时器轮询,但同样不理想。请问有没有始终有效的推荐解决方案?

推荐解决方案

1. 标记应用为高DPI感知

首先确保你的应用被系统识别为高DPI感知,避免系统自动缩放图标。可以通过以下两种方式实现:

  • 添加应用清单(Manifest):在清单中加入高DPI感知声明:
    <application xmlns="urn:schemas-microsoft-com:asm.v3">
      <windowsSettings>
        <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/PM</dpiAware>
        <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor</dpiAwareness>
      </windowsSettings>
    </application>
    
  • 代码设置:在程序启动时调用SetProcessDpiAwarenessContext设置感知模式:
    SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
    

2. 根据任务栏所在显示器的DPI加载图标

不要直接用SM_CXSMICON固定尺寸加载图标,而是获取任务栏所在显示器的DPI,计算对应图标尺寸:

// 获取任务栏所在的显示器句柄
HWND hTaskbar = FindWindow(L"Shell_TrayWnd", nullptr);
HMONITOR hMonitor = MonitorFromWindow(hTaskbar, MONITOR_DEFAULTTOPRIMARY);

// 获取显示器的DPI
UINT dpiX, dpiY;
GetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY);

// 计算图标尺寸:系统默认小图标尺寸是16x16(96DPI),按DPI比例缩放
int iconSize = MulDiv(16, dpiX, 96);

// 加载对应尺寸的图标
data.hIcon = (HICON)LoadImage(inst, L"name", IMAGE_ICON,
    iconSize, iconSize, LR_DEFAULTCOLOR | LR_LOADFROMFILE);

3. 监听全局DPI变更通知

使用WM_SETTINGCHANGE消息代替WM_DPICHANGED,该消息会在系统DPI设置变更时广播给所有窗口,不受窗口所在显示器限制:

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
        case WM_SETTINGCHANGE:
            if (wParam == SPI_SETNONCLIENTMETRICS)
            {
                // DPI设置变更,更新托盘图标
                UpdateTrayIcon(hWnd);
            }
            break;
        // 处理TaskbarCreated消息,防止资源管理器重启后图标消失
        case WM_TASKBARCREATED:
            Shell_NotifyIcon(NIM_ADD, &data);
            break;
        // ...其他消息处理
    }
    return DefWindowProc(hWnd, msg, wParam, lParam);
}

4. 更新托盘图标

当需要更新图标时,调用Shell_NotifyIcon(NIM_MODIFY, &data)替换新的图标,无需重新添加。

这样无论任务栏在哪个显示器,只要DPI变更,你的程序都能收到通知并加载对应尺寸的图标,避免模糊问题。

内容的提问来源于stack exchange,提问作者Paul

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 20:13:29