用户注销/登录后,创建托盘图标时出现罕见错误

Rare error on Tray icon creation, after a user log out / log in

本文关键字:错误 托盘图标 登录 创建 用户注销      更新时间:2023-10-16

>我创建一个系统托盘图标:

BOOL TrayMessage(HWND hWnd, DWORD dwMessage)
{
NOTIFYICONDATA nid;
nid.cbSize = sizeof(nid);
nid.hWnd = hWnd;
nid.uID = 1;
nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
nid.hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_MYAPP));
lstrcpy(nid.szTip, L"MyApp");
nid.uCallbackMessage = WM_NOTIFYICON;
return Shell_NotifyIcon(dwMessage, &nid);
}

当应用启动/创建窗口时:

case WM_CREATE:
if (!TrayMessage(hWnd, NIM_ADD))
MessageBox(hMainWnd, L"Tray error.", 0, 0);

此错误消息框:

  • 当我正常启动.exe时永远不会发生。

  • 仅在用户注销/用户重新登录后发生,平均每 5次启动一次(我的应用程序在每次会话启动时自动启动

    ,并执行任务计划任务(

当然,当错误发生时,图标不会显示在任务栏中。

可能是什么原因?

系统
  1. 托盘系统尚未准备就绪(用户注销/再次登录后很短(?

  2. 任务栏本身还没有准备好?

  3. 我应该将创作移动到WM_CREATE之外的地方吗?


编辑:在@RbMm的评论之后,我尝试了这个:

case WM_CREATE:
TrayMessage(hWnd, NIM_ADD);
// I removed MessageBox(...) from here
uTaskbarRestart = RegisterWindowMessage(TEXT("TaskbarCreated"));
... 
break;
default:
if (message == uTaskbarRestart)
{
TrayMessage(hWnd, NIM_ADD);
MessageBox(hMainWnd, L"TaskbarRestart", 0, 0);
}

此测试的结果:无法显示托盘图标的情况正是未显示消息框TaskbarRestart的情况,即当任务栏重启事件永远不会进入消息循环时...这很奇怪...

注意:这仅在用户注销/重新登录后发生。

MSDN Shell_NotifyIcon的当前版本不再显示它(真可惜!(,但幸运的是,这里有一个存档版本,提供了两个有趣的信息:

1.

如果成功,则返回 TRUE,否则返回 FALSE。[...] 您可以调用 GetLastError 以获取有关失败案例的更具体信息。失败的最常见原因是任务栏窗口不存在或无响应。在这种情况下,GetLastError 返回E_FILE_NOT_FOUND。

阿拉伯数字。

处理Shell_NotifyIcon故障Shell_NotifyIcon 在 Windows
启动期间调用时通常会失败(例如,如果您的应用程序列在 HKLM\Software\Microsoft\Windows\CurrentVersion\Run 中(。这似乎是因为系统正忙于启动应用程序。该故障在低规格计算机或安装了某些品牌防病毒软件的计算机上更为常见,这些计算机在启动时似乎非常密集。

遗憾的是,您不能依赖 GetLastError 返回的错误代码。当Shell_NotifyIcon返回 false 时,GetLastError 返回的一些常见错误包括:

ERROR_FILE_NOT_FOUND (2)
ERROR_TIMEOUT (1460)
ERROR_SUCCESS (0)

对 Shell_NotifyIcon 返回的任何错误的最适当响应是休眠一段时间,然后重试。

Paul Baker解释了为什么错误代码可能不同,转述自 http://groups.google.com/group/microsoft.public.platformsdk.shell/msg/59235b293cbf5dfa 和 http://groups.google.com/group/microsoft.public.platformsdk.shell/msg/73973287f15c03fc:

Shell_NotifyIcon实际上最初调用 SetLastError(0(。之后,基本上它使用FindWindow来查找托盘通知窗口。如果此操作失败,它通常会返回ERROR_FILE_NOT_FOUND。否则,它会使用 SendMessageTimeout 将WM_COPYDATA消息发送到托盘通知窗口,超时时间仅为 4 秒。如果该消息返回零,则Shell_NotifyIcon将失败,GetLastError 返回零。

溶液:

case WM_CREATE:
...
if (!TrayMessage(hWnd, NIM_ADD)) 
SetTimer(hWnd, IDT_TIMER1, 4000, (TIMERPROC) NULL);
break;
case WM_TIMER:
TrayMessage(hWnd, NIM_ADD);
KillTimer(IDT_TIMER1);
break;