您可以向 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
。
您不应该使用FindWindowEx
的caption
参数来查找窗口,因为这些是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 资源管理器发送信号以使其刷新系统托盘图标吗?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Swift 缩放 HTML 内容以使其适合手机宽度?