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

标签 c++ windows winapi system-tray systray

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

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 任务启动 session 时自动启动)

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

可能是什么原因?

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

测试结果:托盘图标无法显示的情况恰好 MessageBox TaskbarRestart 未显示的情况,即 TaskbarRestart 事件从不来到消息循环...这很奇怪...

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

最佳答案

当前版本的 MSDN Shell_NotifyIcon 不再显示它(太可惜了!),但幸运的是,有一个 archived version here这给出了两个有趣的信息:

1.

Returns TRUE if successful, or FALSE otherwise. [...] You can call GetLastError for more specific information about a failure case. The most common cause of failure is that the taskbar window doesn't exist or is unresponsive. GetLastError in that case returns E_FILE_NOT_FOUND.

2.

Handling Shell_NotifyIcon failure
Shell_NotifyIcon will often fail when called during Windows startup (for instance, if your application is listed in HKLM\Software\Microsoft\Windows\CurrentVersion\Run. This appears to be because the system is busy starting applications. The failure is more common on low-spec computers or computers with some brands of antivirus software installed, which seem to be very intensive at startup.

Unfortunately, you cannot rely on the error code returned by GetLastError. When Shell_NotifyIcon returns false, some of the common errors returned by GetLastError are:

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

The most appropriate response to any error returned by Shell_NotifyIcon is to sleep for a period of time and retry.

An explanation of why the error code may differ has been made by Paul Baker, paraphrased from http://groups.google.com/group/microsoft.public.platformsdk.shell/msg/59235b293cbf5dfa and http://groups.google.com/group/microsoft.public.platformsdk.shell/msg/73973287f15c03fc:

Shell_NotifyIcon actually calls SetLastError(0) initially. After that, basically it uses FindWindow to find the tray notification window. If this fails, it will typically return ERROR_FILE_NOT_FOUND. Otherwise it sends a WM_COPYDATA message to the tray notification window, using SendMessageTimeout with a timeout of only 4 seconds. If that message returns zero, then Shell_NotifyIcon will fail with GetLastError returning zero.

解决方法:

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;

关于c++ - 用户注销/登录后创建托盘图标时出现罕见错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45255294/

相关文章:

c++ - 获取 Windows API 标题栏图标

c++ - 将 HBITMAP 绘制到分层窗口上。怎么了?

c# - Windows.UI.ViewManagement.ApplicationView.TryUnsnap() 已过时

windows - PrintWindow 位图不同于 PrintScreen Key 位图

c++ - 如何检测 QLabel 中的文本何时更改?

c++ - 这里需要内存屏障 "*pEnd_ = v; __sync_synchronize ();++pEnd_;"吗?

c - 从c中的其他文件打开文件路径

C++ - 通过 ref 将对象传递给 CreateThread()

c++ - 使用 placement new[] 有什么问题?做

c++ - 在 Qt 开发人员中将 double 转换为 int