Редкая ошибка при создании значка в трее после выхода пользователя из системы / входа в систему

Я создаю значок на панели задач:

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

Результат этого теста: случаи, когда значок в трее не отображается, это в точности те случаи, когда MessageBox TaskbarRestart не отображается, т.е. когда событие TaskbarRestart никогда не доходит до цикла сообщений ... Это странно ...

Примечание: это происходит только после выхода / повторного входа пользователя.


person Basj    schedule 22.07.2017    source источник
comment
вы можете прописать специальное сообщение s_uTaskbarRestart = RegisterWindowMessage(L"TaskbarCreated"); и каждый раз, когда у вас будет s_uTaskbarRestart - добавлять значок на панель задач. сделайте это также на WM_CREATE. он может выйти из строя по 1.) причине - панель задач еще не создана   -  person RbMm    schedule 22.07.2017
comment
@RbMm Я попробовал это и добавил в вопрос результат теста. У тебя есть идея?   -  person Basj    schedule 22.07.2017
comment
TrayMessage вам нужно не всегда в default, а только когда message == uTaskbarRestart + на WM_CREATE. конечно возможен разрыв между TrayMessage (панель задач еще не создана в данный момент) и RegisterWindowMessage (панель задач уже создана, поэтому вы не получите сообщение). вам нужно изменить порядок - сначала вызов RegisterWindowMessage на WM_CREATE или лучше при запуске программы (это глобальные данные, не связанное с окном) и только после этого TrayMessage WM_CREATE   -  person RbMm    schedule 22.07.2017
comment
поэтому обычно в entrypoint (WinMain, etc) вызовите uTaskbarRestart = RegisterWindowMessage(L"TaskbarCreated"); (это должно быть сначала и один раз) и только потом, при WM_CREATE и message == uTaskbarRestart вызовите TrayMessage   -  person RbMm    schedule 22.07.2017
comment
@RbMm о да TrayMessage не всегда в default:, но только если message == uTaskbarRestart + WM_CREATE, вы правы, это была неправильная копия / вставка. Редактировал вопрос. Попробую ваше новое предложение в комментариях!   -  person Basj    schedule 23.07.2017
comment
@RbMm Я пробовал, но до сих пор безуспешно. См. Здесь: сообщение RegisterWindowMessage должно выполняться во время WM_CREATE: msdn.microsoft.com / ru-ru / library /   -  person Basj    schedule 23.07.2017
comment
@RbMm Не повезло. Я пробовал использовать uTaskbarRestart = RegisterWindowMessage(L"TaskbarCreated"); в WinMain перед основным циклом сообщений. В 75% случаев это работает, но иногда: 1. выход из сеанса 2. журнал сеанса 3. запускается проводник 4. мои приложения запускаются скрытыми (без значка) (я вижу это в диспетчере процессов!) 5. нет окна сообщений это похоже на то, что в моем приложении никогда не запускается TaskbarCreated :) (хотя в 75% случаев окно сообщения отображается!)   -  person Basj    schedule 23.07.2017
comment
he RegisterWindowMessage should be done during WM_CREATE: - не конечно. это плохой пример. s_uTaskbarRestart - это глобальные данные по смыслу (не связанные с вашим окном, посмотрите даже в примере, что он определен как static) должен быть инициализирован только один раз. лучшее место - запуск программы   -  person RbMm    schedule 23.07.2017
comment
Позвольте нам продолжить это обсуждение в чате.   -  person Basj    schedule 23.07.2017
comment
очень странно, что в некоторых случаях вы не получаете uTaskbarRestart сообщение. Я думаю, что он отправляется во все окна верхнего уровня, однако я не отлаживаю подробно этот случай - поэтому не могу просто сказать, почему это происходит   -  person RbMm    schedule 23.07.2017


Ответы (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, является переход в режим сна на некоторое время и повторная попытка.

Пол Бейкер объяснил, почему код ошибки может отличаться, перефразируя его из 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. В противном случае он отправляет сообщение WM_COPYDATA в окно уведомления в трее, используя SendMessageTimeout с таймаутом всего 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;
person Basj    schedule 22.07.2017