您可以向 Windows 资源管理器发送信号以使其刷新系统托盘图标吗?

Posted

技术标签:

【中文标题】您可以向 Windows 资源管理器发送信号以使其刷新系统托盘图标吗?【英文标题】:Can you send a signal to Windows Explorer to make it refresh the systray icons? 【发布时间】:2010-09-09 15:18:15 【问题描述】:

这个问题困扰了我好久,真的很烦。

每次我在重新启动/重启后登录时,资源管理器都需要一些时间才能显示出来。 我已经采取了等待所有服务启动然后登录的步骤,但这没有任何区别。 结果总是一样的:即使应用程序已经启动,某些图标也不会显示。

我已经对使一个应用程序在其中“粘贴”一个图标的代码进行了一些研究,但是是否有一个 API 调用可以执行,以便资源管理器重新读取所有这些图标信息?比如无效或重绘之类的?


显然,Jon 似乎是对的,但这是不可能的。

我按照 Bob Dizzle 和 Mark Ransom 的代码构建了这个(Delphi 代码):

procedure Refresh;
var
  hSysTray: THandle;
begin
  hSysTray := GetSystrayHandle;
  SendMessage(hSysTray, WM_PAINT, 0, 0);
end;

function GetSystrayHandle: THandle;
var
  hTray, hNotify, hSysPager: THandle;
begin
  hTray := FindWindow('Shell_TrayWnd', '');
  if hTray = 0 then
  begin
    Result := hTray;
    exit;
  end;

  hNotify := FindWindowEx(hTray, 0, 'TrayNotifyWnd', '');
  if hNotify = 0 then
  begin
    Result := hNotify;
    exit;
  end;

  hSyspager := FindWindowEx(hNotify, 0, 'SysPager', '');
  if hSyspager = 0 then
  begin
    Result := hSyspager;
    exit;
  end;

  Result := FindWindowEx(hSysPager, 0, 'ToolbarWindow32', 'Notification Area');
end;

但无济于事。

我什至尝试过

InvalidateRect()
仍然没有出现。

还有其他建议吗?

【问题讨论】:

【参考方案1】:

查看此博客条目:REFRESHING THE TASKBAR NOTIFICATION AREA。我正在使用此代码刷新系统托盘以摆脱孤立的图标,并且它运行良好。 该博客条目信息量很大,并且很好地解释了作者为发现他的解决方案所执行的步骤。

#define FW(x,y) FindWindowEx(x, NULL, y, L"")

void RefreshTaskbarNotificationArea()

    HWND hNotificationArea;
    RECT r;

    GetClientRect(
        hNotificationArea = FindWindowEx(
            FW(FW(FW(NULL, L"Shell_TrayWnd"), L"TrayNotifyWnd"), L"SysPager"),
            NULL,
            L"ToolbarWindow32",
            // L"Notification Area"), // Windows XP
            L"User Promoted Notification Area"), // Windows 7 and up
        &r);

    for (LONG x = 0; x < r.right; x += 5)
        for (LONG y = 0; y < r.bottom; y += 5)
            SendMessage(
                hNotificationArea,
                WM_MOUSEMOVE,
                0,
                (y << 16) + x);

【讨论】:

新挑战,但反过来:***.com/questions/1114887/… 我有点怀疑最后一个参数“通知区域”是否适用于国际版本的Windows。 dummzeuch 是正确的。 “通知区域”字符串需要针对适当的 Windows 语言进行本地化。 @dummzeuch 如果您传递NULL 而不是窗口标题,您将获得第一个子窗口,请参阅FindWindowEx 文档。这样你就有了一个独立于语言的版本。【参考方案2】:

在 Windows 7 或 Windows 8 上使用 Louis 的答案(来自 REFRESHING THE TASKBAR NOTIFICATION AREA)的任何人的两个重要细节:

首先,正如答案所反映的那样,XP 中标题为“通知区域”的窗口现在在 Windows 7(实际上可能是 Vista)及更高版本中标题为“用户推广通知区域”。

其次,此代码不会清除当前隐藏的图标。这些都包含在一个单独的窗口中。使用原代码刷新可见图标,如下刷新隐藏图标。

//Hidden icons
GetClientRect(
    hNotificationArea = FindWindowEx(
        FW(NULL, L"NotifyIconOverflowWindow"),
        NULL,
        L"ToolbarWindow32",
        L"Overflow Notification Area"),
    &r);

for (LONG x = 0; x < r.right; x += 5)
    for (LONG y = 0; y < r.bottom; y += 5)
        SendMessage(
            hNotificationArea,
            WM_MOUSEMOVE,
            0,
            (y << 16) + x);

对于只需要运行实用程序而不是代码来完成此操作的任何人,我使用此更新构建了一个简单的 exe:Refresh Notification Area

【讨论】:

Stephen 如果您的“刷新通知区域”工具能够向后兼容 XP,那就太好了。还没有找到处理隐藏图标的独立工具。 谢谢!即使在 Windows 更新 4 年后,这款出色的工具也能完美运行! 如果您传递NULL 而不是"Overflow Notification Area",您将获得第一个子窗口,请参阅FindWindowEx 文档。这样你就有了一个独立于语言的版本。【参考方案3】:

包含以下代码以刷新系统托盘。

public const int WM_PAINT = 0xF;
[DllImport("USER32.DLL")]
public static extern int SendMessage(IntPtr hwnd, int msg, int character,
                                     IntPtr lpsText);

Send WM_PAINT Message to paint System Tray which will refresh it.
SendMessage(traynotifywnd, WM_PAINT, 0, IntPtr.Zero);

【讨论】:

我实际上是一个有点像德尔福的人,但这是有道理的。所以基本上向下面找到的句柄发送 WM_PAINT 消息。我会测试它并报告。【参考方案4】:

据我所知,这是不可能的 Gustavo - 由每个应用程序将其 notifyicon 放入系统托盘,并确保它保持在正确的状态。

当 explorer.exe 崩溃时,您有时会注意到某些图标不会重新出现 - 这不是因为他们的进程已经崩溃,只是因为他们的应用程序在新的 explorer 实例时没有将 notifyicon 放入系统托盘.exe 启动。再次,它是负责的应用程序。

很抱歉没有更好的消息告诉你!

【讨论】:

【参考方案5】:

我去年在我的Codeaholic 博客上发表了一篇题为[Delphi] Updating SysTray 的文章。

我的解决方案是 Delphi ActiveX/COM DLL。下载链接仍然有效(尽管我不知道还能持续多久,因为我的PLUG 会员资格已失效。)

【讨论】:

很抱歉我没有选择你的答案,但路易斯的分数较低,而且你们的答案相同。 新挑战,但反过来:***.com/questions/1114887/…【参考方案6】:

我使用以下 C++ 代码来获取托盘窗口的窗口句柄。 注意:这仅在 Windows XP 上测试过。

HWND FindSystemTrayIcons(void)

    // the system tray icons are contained in a specific window hierarchy;
    // use the Spy++ utility to see the chain
    HWND hwndTray = ::FindWindow("Shell_TrayWnd", "");
    if (hwndTray == NULL)
        return NULL;
    HWND hwndNotifyWnd = ::FindWindowEx(hwndTray, NULL, "TrayNotifyWnd", "");
    if (hwndNotifyWnd == NULL)
        return NULL;
    HWND hwndSysPager = ::FindWindowEx(hwndNotifyWnd, NULL, "SysPager", "");
    if (hwndSysPager == NULL)
        return NULL;
    return ::FindWindowEx(hwndSysPager, NULL, "ToolbarWindow32", "Notification Area");

【讨论】:

感谢您提供的信息。我可能会拔出头发来寻找那种层次结构。【参考方案7】:

经过多次尝试,我发现有三个问题你必须知道:

隐藏托盘窗口的父级是NotifyIconOverflowWindow,而不是Shell_TrayWnd。 您不应该使用FindWindowExcaption 参数来查找窗口,因为这些是Windows 操作系统的许多语言版本,它们的标题并不总是相同。显然。 使用 Visual Studio 的 spy++ 查找或确定您想要的内容。

所以,我更改了 @Stephen Klancher 和 @Louis Davis 的代码,谢谢你们。

以下代码对我有用。

#define FW(x,y) FindWindowEx(x, NULL, y, L"")
void RefreshTaskbarNotificationArea()

    HWND hNotificationArea;
    RECT r;
    GetClientRect(hNotificationArea = FindWindowEx(FW(NULL, L"NotifyIconOverflowWindow"), NULL, L"ToolbarWindow32", NULL), &r);
    for (LONG x = 0; x < r.right; x += 5)
    
        for (LONG y = 0; y < r.bottom; y += 5)
        
            SendMessage(hNotificationArea, WM_MOUSEMOVE, 0, (y << 16) + x);
        
    

【讨论】:

【参考方案8】:

@Skip R,以及其他任何想在 C 中执行此操作的人,并在 Windows 10 64 位(但安装了 mingw 32 位软件包)上的最近(最新)mingw 中验证了此代码,这似乎可行在 Windows XP / 2003 中删除陈旧的通知区域图标。

我通过 Chocolatey 安装了 mingw,如下所示:

choco install mingw --x86 --force --params "/exception:sjlj"

(您的里程可能会有所不同,在我的系统上,编译器然后安装在这里:

C:\ProgramData\chocolatey\lib\mingw\tools\install\mingw32\bin\gcc.exe

然后是一个简单的

gcc refresh_notification_area.c

生成了一个 a.exe,它解决了我在 Windows 2003(32 位)上遇到的过时通知区域图标问题。

改编自上面@Stephen Klancher 的代码是(请注意,这可能仅适用于Windows XP/2003,它实现了我的目的):

#include <windows.h>

#define FW(x,y) FindWindowEx(x, NULL, y, "")

int main ()


    HWND hNotificationArea;
    RECT r;

    //WinXP
    // technique found at:
    // https://***.com/questions/74723/can-you-send-a-signal-to-windows-explorer-to-make-it-refresh-the-systray-icons#18038441
    GetClientRect(
        hNotificationArea = FindWindowEx(
            FW(FW(FW(NULL, "Shell_TrayWnd"), "TrayNotifyWnd"), "SysPager"),
            NULL,
            "ToolbarWindow32",
            "Notification Area"),
        &r);

    for (LONG x = 0; x < r.right; x += 5)
        for (LONG y = 0; y < r.bottom; y += 5)
            SendMessage(
                hNotificationArea,
                WM_MOUSEMOVE,
                0,
                (y << 16) + x);

  return 0;


【讨论】:

以上是关于您可以向 Windows 资源管理器发送信号以使其刷新系统托盘图标吗?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我不能从伊朗向 Fabric 发送数据?

如何使用 Swift 缩放 HTML 内容以使其适合手机宽度?

是否可以使用 Azure 资源管理器 API 向队列或主题发送和检索消息

C 使用 IPC 消息队列的信号使用和处理

有没有办法从另一个进程向线程发送信号?

Windows7中资源管理器的作用是啥?