您能否向 Windows 资源管理器发送信号以使其刷新系统托盘图标?
-
09-06-2019 - |
题
这个问题困扰了我很长一段时间,真的很烦人。
每次我在重新启动/重新启动后登录时,资源管理器都需要一些时间才能显示。我采取了等待所有服务启动然后登录的步骤,但这没有任何区别。结果总是一样的:即使应用程序已启动,某些图标也不会显示。
我对使一个应用程序“粘贴”图标的代码进行了一些研究,但是是否存在可以执行的 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()
仍然没有演出。
还有其他建议吗?
解决方案
看看这篇博客文章: 刷新任务栏通知区域. 。我正在使用此代码刷新系统托盘以摆脱孤立的图标,它工作得很好。该博客文章内容非常丰富,并且很好地解释了作者为发现解决方案而执行的步骤。
#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);
}
其他提示
对于任何使用路易斯答案的人来说,有两个重要的细节(来自 刷新任务栏通知区域) 在 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: 刷新通知区域
在您的代码中包含以下代码以刷新系统托盘。
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);
据我所知,这是不可能的 Gustavo - 每个应用程序都应将其通知图标放入系统托盘中,并确保其保持正确的状态。
有时您会注意到,当 explorer.exe 崩溃时,某些图标不会重新出现 - 这并不是因为它们的进程崩溃了,而是因为当 explorer.exe 的新实例启动时,它们的应用程序没有将通知图标放入系统托盘中向上。再次强调,是应用程序负责。
很抱歉没有给您带来更好的消息!
我去年在我的博客上讨论过这个问题 代码狂 博客中的一篇文章标题为 [Delphi] 更新系统托盘.
我的解决方案是 Delphi ActiveX/COM DLL。下载链接仍然有效(尽管我不知道我的 插头 会员资格已失效。)
我使用以下 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");
}
@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;
}
经过多次尝试,我发现有以下三个问题是你必须要知道的:
- 隐藏托盘窗口的父级是
NotifyIconOverflowWindow
, , 以外Shell_TrayWnd
. - 你不应该使用
caption
的参数FindWindowEx
找到一个窗口,因为这些是 Windows 操作系统的许多语言版本,显然它们并不总是相同的标题。 - 使用
spy++
Visual Studio 来查找或确定您想要的内容。
所以,我更改了@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);
}
}
}