2

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

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 次启动一次(我的应用程序会在每次会话启动时自动启动 TaskSchedular 任务)

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

可能是什么原因?

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

本次测试的结果:托盘图标无法显示的情况正是MessageBoxTaskbarRestart没有显示的情况,即TaskbarRestart事件从不进入消息循环的情况......这很奇怪......

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

4

1 回答 1

1

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

1.

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

2.

处理 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/59235b293cbf5dfahttp://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;
于 2017-07-22T23:17:17.870 回答