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

发布于 2024-07-04 17:53:31 字数 1135 浏览 8 评论 0 原文

这个问题困扰了我很长一段时间,真的很烦人。

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

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


显然,看起来乔恩是对的,但这是不可能的。

我遵循 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()
and still no show.

还有其他建议吗?

This problem has been afflicting me for quite a while and it's been really annoying.

Every time I login after a reboot/power cycle the explorer takes some time to show up.
I've taken the step of waiting for all the services to boot up and then I login, but it doesn't make any difference.
The result is always the same: Some of the icons do not show up even if the applications have started.

I've dug a bit on the code that makes one application "stick" an icon in there, but is there an API call that one can perform so explorer re-reads all that icon info? Like invalidate or redraw or something of the sort?


Apparently, it looks like Jon was right and it's not possible to do it.

I've followed Bob Dizzle and Mark Ransom code and build this (Delphi Code):

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;

But to no avail.

I've even tried with

InvalidateRect()

and still no show.

Any other suggestions?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

就此别过 2024-07-11 17:53:32

我使用以下 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");
}

I use the following C++ code to get the window handle to the tray window. Note: this has only been tested on 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");
}

一口甜 2024-07-11 17:53:32

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

  • 隐藏托盘窗口的父窗口是 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);
        }
    }
}

After lots of times trying I found that there are three issues you must to know:

  • The parent of hidden tray window is NotifyIconOverflowWindow, other than Shell_TrayWnd.
  • You shouldn't use caption parameter of FindWindowEx to find a window, because these is lots of langue versions of Windows OS, they are not always be the same title Obviously.
  • Use spy++ of Visual Studio to find or make assurance what you want.

So, I changed code from @Stephen Klancher and @Louis Davis, thank you guys.

The following code worked for me.

#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);
        }
    }
}
楠木可依 2024-07-11 17:53:32

@Skip R,以及任何其他想要用 C 语言执行此操作的人,此代码已在 Windows 10 64 位上的最近(最新)mingw 中验证编译(但安装了 mingw 32 位软件包),这似乎可以在 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://stackoverflow.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;

}

@Skip R, and anyone else wanting to do this in C, with this code verified compiled in a recent (most recent) mingw on Windows 10 64 bit (but with the mingw 32 bit package installed), this seems to work in Windows XP / 2003 to get rid of stale notification area icons.

I installed mingw via Chocolatey, like this:

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

(your mileage may vary on that, on my system, the compiler was then installed here:

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

and then a simple

gcc refresh_notification_area.c

yielded an a.exe which solved a stale notification area icon problem I was having on Windows 2003 (32 bit).

The code, adapted from @Stephen Klancher above is (note this may only work on Windows XP/2003, which fulfilled my purposes):

#include <windows.h>

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

int main ()
{

    HWND hNotificationArea;
    RECT r;

    //WinXP
    // technique found at:
    // https://stackoverflow.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;

}
你好,陌生人 2024-07-11 17:53:32

Powershell 解决方案,将其放入脚本中

Add-Type -AssemblyName System.Windows.Forms
Add-Type @"
using System;
using System.Runtime.InteropServices;

public struct RECT {
  public int left;
  public int top;
  public int right;
  public int bottom;
}

public class pInvoke {
  [DllImport("user32.dll")]
  public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

  [DllImport("user32.dll")]
  public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);

  [DllImport("user32.dll")]
  public static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect);

  [DllImport("user32.dll")]
  public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, int wParam, int lParam);
  
  public static void RefreshTrayArea() {
    IntPtr systemTrayContainerHandle = FindWindow("Shell_TrayWnd", null);
    IntPtr systemTrayHandle = FindWindowEx(systemTrayContainerHandle, IntPtr.Zero, "TrayNotifyWnd", null);
    IntPtr sysPagerHandle = FindWindowEx(systemTrayHandle, IntPtr.Zero, "SysPager", null);
    IntPtr notificationAreaHandle = FindWindowEx(sysPagerHandle, IntPtr.Zero, "ToolbarWindow32", "Notification Area");
    if (notificationAreaHandle == IntPtr.Zero) {
      notificationAreaHandle = FindWindowEx(sysPagerHandle, IntPtr.Zero, "ToolbarWindow32", "User Promoted Notification Area");
      IntPtr notifyIconOverflowWindowHandle = FindWindow("NotifyIconOverflowWindow", null);
      IntPtr overflowNotificationAreaHandle = FindWindowEx(notifyIconOverflowWindowHandle, IntPtr.Zero, "ToolbarWindow32", "Overflow Notification Area");
      RefreshTrayArea(overflowNotificationAreaHandle);
    }
    RefreshTrayArea(notificationAreaHandle);
  }

  private static void RefreshTrayArea(IntPtr windowHandle) {
    const uint wmMousemove = 0x0200;
    RECT rect;
    GetClientRect(windowHandle, out rect);
    for (var x = 0; x < rect.right; x += 5)
      for (var y = 0; y < rect.bottom; y += 5)
        SendMessage(windowHandle, wmMousemove, 0, (y << 16) + x);
  }
}
"@

然后使用 [pInvoke]::RefreshTrayArea() 重置

Powershell solution, put this in your script

Add-Type -AssemblyName System.Windows.Forms
Add-Type @"
using System;
using System.Runtime.InteropServices;

public struct RECT {
  public int left;
  public int top;
  public int right;
  public int bottom;
}

public class pInvoke {
  [DllImport("user32.dll")]
  public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

  [DllImport("user32.dll")]
  public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);

  [DllImport("user32.dll")]
  public static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect);

  [DllImport("user32.dll")]
  public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, int wParam, int lParam);
  
  public static void RefreshTrayArea() {
    IntPtr systemTrayContainerHandle = FindWindow("Shell_TrayWnd", null);
    IntPtr systemTrayHandle = FindWindowEx(systemTrayContainerHandle, IntPtr.Zero, "TrayNotifyWnd", null);
    IntPtr sysPagerHandle = FindWindowEx(systemTrayHandle, IntPtr.Zero, "SysPager", null);
    IntPtr notificationAreaHandle = FindWindowEx(sysPagerHandle, IntPtr.Zero, "ToolbarWindow32", "Notification Area");
    if (notificationAreaHandle == IntPtr.Zero) {
      notificationAreaHandle = FindWindowEx(sysPagerHandle, IntPtr.Zero, "ToolbarWindow32", "User Promoted Notification Area");
      IntPtr notifyIconOverflowWindowHandle = FindWindow("NotifyIconOverflowWindow", null);
      IntPtr overflowNotificationAreaHandle = FindWindowEx(notifyIconOverflowWindowHandle, IntPtr.Zero, "ToolbarWindow32", "Overflow Notification Area");
      RefreshTrayArea(overflowNotificationAreaHandle);
    }
    RefreshTrayArea(notificationAreaHandle);
  }

  private static void RefreshTrayArea(IntPtr windowHandle) {
    const uint wmMousemove = 0x0200;
    RECT rect;
    GetClientRect(windowHandle, out rect);
    for (var x = 0; x < rect.right; x += 5)
      for (var y = 0; y < rect.bottom; y += 5)
        SendMessage(windowHandle, wmMousemove, 0, (y << 16) + x);
  }
}
"@

Then use [pInvoke]::RefreshTrayArea() to reset

风轻花落早 2024-07-11 17:53:31

查看此博客条目:刷新任务栏通知区域。 我正在使用此代码刷新系统托盘以摆脱孤立的图标,它工作得很好。
该博客文章内容非常丰富,并且很好地解释了作者为发现解决方案而执行的步骤。

#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);
}

Take a look at this blog entry: REFRESHING THE TASKBAR NOTIFICATION AREA. I am using this code to refresh the system tray to get rid of orphaned icons and it works perfectly.
The blog entry is very informative and gives a great explanation of the steps the author performed to discover his solution.

#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);
}
枉心 2024-07-11 17:53:31

去年,我在我的 Codeaholic 博客上发表了一篇题为 [Delphi] 更新 SysTray

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

I covered this issue last year on my Codeaholic weblog in an article entitled [Delphi] Updating SysTray.

My solution is a Delphi ActiveX/COM DLL. The download link still works (though for how much longer I don't know as my PLUG membership has lapsed.)

2024-07-11 17:53:31

据我所知,这是不可能的 Gustavo - 每个应用程序都应将其通知图标放入系统托盘中,并确保其保持正确的状态。

有时您会注意到,当 explorer.exe 崩溃时,某些图标不会重新出现 - 这并不是因为它们的进程崩溃了,而是因为当 explorer.exe 的新实例启动时,它们的应用程序没有将通知图标放入系统托盘中向上。 再次强调,是应用程序负责。

很抱歉没有给您带来更好的消息!

As far as I know that isn't possible Gustavo - it's up to each application to put its notifyicon in the systray, and ensure it's kept in the right state.

You'll notice sometimes when explorer.exe crashes that certain icons don't reappear - this isn't because their process has crashed, simply that their application hasn't put the notifyicon in the systray when the new instance of explorer.exe started up. Once again, it's the application that's responsible.

Sorry not to have better news for you!

始终不够 2024-07-11 17:53:31

在您的代码中包含以下代码以刷新系统托盘。

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);

Include following code with yours to refresh System Tray.

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);
明月夜 2024-07-11 17:53:31

对于任何使用路易斯答案的人来说,有两个重要细节(来自刷新任务栏通知区域) 在 Windows 7 或 Windows 8 上:

首先,正如答案所反映的那样,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:刷新通知区域

Two important details for anyone using Louis's answer (from REFRESHING THE TASKBAR NOTIFICATION AREA) on Windows 7 or Windows 8:

First, as the answer was reflected to show, the window titled "Notification Area" in XP is now titled "User Promoted Notification Area" in Windows 7 (actually probably Vista) and up.

Second, this code does not clear icons that are currently hidden. These are contained in a separate window. Use the original code to refresh visible icons, and the following to refresh hidden icons.

//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);

For anyone who just needs a utility to run to accomplish this, rather than code, I built a simple exe with this update: Refresh Notification Area

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文