История одного Tray Balloon
Понадобилось мне как-то раз добавить в программу выскакивающее облачко. Программа была написана в Visual C++ 2008, и ничто не предвещало беды.
Программа замечательно компилилась, но... облака не выдавала. Проверил, запрещены ли они в реестре. Нет. Скачал с сайта Microsoft древний пример, скомпилировал. Тоже не работает. Поставил в VirtualBox свежую винду. Запустил свою программу: невнятно ругнулась (filemon, гугл и Dependency Walker подсказали, что на рантайм VC9), пришлось компилить статически. Запустилась, но облака все равно нет. Попытался статически скомпилить пример Microsoft, попутно поправив тяжкие баги, доставшиеся тому от VC6: не вышло, компилятор настоятельно требует /MD switch for _AFXDLL builds.
В инете появился
shuffle_c и попытался скомпилить пример. Его VC++ 2008 Express отказался, потому что ему не понравился MFC, и он скомпилил пример VC++ 2005. Облако появилось. Он скинул мне exeшник и облако появилось и у меня на виртуалке. Затем на хосте. Меня начали одолевать смутные сомнения. Я скинул ему и свой проект. Облако так же невозмутимо появилось сначала у него, затем у меня в виртуалке, затем у меня на хосте. Сомнений тревожащие всходы заколосились и разродились неожиданным озарением. Я попросил
shuffle_c посмотреть sizeof(NOTIFYICONDATA). 952! — был ответ. А у меня 956, отметил я и полез в ShellApi.h.
Несложная проверка показала, что 2005-й компилил эту структуру без HICON hBalloonIcon, используя в условиях компиляции _WIN32_IE. Тут стоит сделать лирическое отступление и сказать, что в MSDN, идущем в составе Visual Studio 2008, сказано, что поля, отвечающие за облака, доступны только в версии Shell32.dll 5.0 и выше, и поэтому для их использования надо определить #define _WIN32_IE 0x0500 или #define _WIN32_IE 0x0600. Как положено порядочному программисту, кушающему кашку и слушающемуся MSDN, я использовал второй дефайн.
Но NTDDI_VERSION чихать хотел на _WIN32_IE. Если NTDDI_VERSION не определен, а определен _WIN32_WINNT, он использует его (сдвигая вправо на 16 бит). А на несколько строчек выше, если не определен ни _WIN32_WINNT, ни _CHICAGO_ (это кодовое имя 95-й винды), то _WIN32_WINNT полагается равным 0x0600, т.е. коду Висты! Переводя на простой русский язык: если Visual C++ 2008 не знает, какая у вас ось, значит, у вас Виста (которая здесь же, в сырцах, именуется Longhorn!). Shell32.dll от моей родной Windows XP получал «вистовый» размер 956, офигевал, и облака не выдавал.
Все это — попытка кратко описать, какая именно магия стоит за короткой строчкой воркараунда
и сколько может уйти времени на то, чтобы эту магию создать, если кто-то забыл изменить пару строчек в MSDN, обновив заголовочные файлы.
Программа замечательно компилилась, но... облака не выдавала. Проверил, запрещены ли они в реестре. Нет. Скачал с сайта Microsoft древний пример, скомпилировал. Тоже не работает. Поставил в VirtualBox свежую винду. Запустил свою программу: невнятно ругнулась (filemon, гугл и Dependency Walker подсказали, что на рантайм VC9), пришлось компилить статически. Запустилась, но облака все равно нет. Попытался статически скомпилить пример Microsoft, попутно поправив тяжкие баги, доставшиеся тому от VC6: не вышло, компилятор настоятельно требует /MD switch for _AFXDLL builds.
В инете появился
typedef struct _NOTIFYICONDATAW {
DWORD cbSize;
HWND hWnd;
UINT uID;
UINT uFlags;
UINT uCallbackMessage;
HICON hIcon;
#if (NTDDI_VERSION < NTDDI_WIN2K)
WCHAR szTip[64];
#endif
#if (NTDDI_VERSION >= NTDDI_WIN2K)
WCHAR szTip[128];
DWORD dwState;
DWORD dwStateMask;
WCHAR szInfo[256];
union {
UINT uTimeout;
UINT uVersion; // used with NIM_SETVERSION, values 0, 3 and 4
} DUMMYUNIONNAME;
WCHAR szInfoTitle[64];
DWORD dwInfoFlags;
#endif
#if (NTDDI_VERSION >= NTDDI_WINXP)
GUID guidItem;
#endif
#if (NTDDI_VERSION >= NTDDI_LONGHORN)
HICON hBalloonIcon;
#endif
} NOTIFYICONDATAW, *PNOTIFYICONDATAW;
DWORD cbSize;
HWND hWnd;
UINT uID;
UINT uFlags;
UINT uCallbackMessage;
HICON hIcon;
#if (NTDDI_VERSION < NTDDI_WIN2K)
WCHAR szTip[64];
#endif
#if (NTDDI_VERSION >= NTDDI_WIN2K)
WCHAR szTip[128];
DWORD dwState;
DWORD dwStateMask;
WCHAR szInfo[256];
union {
UINT uTimeout;
UINT uVersion; // used with NIM_SETVERSION, values 0, 3 and 4
} DUMMYUNIONNAME;
WCHAR szInfoTitle[64];
DWORD dwInfoFlags;
#endif
#if (NTDDI_VERSION >= NTDDI_WINXP)
GUID guidItem;
#endif
#if (NTDDI_VERSION >= NTDDI_LONGHORN)
HICON hBalloonIcon;
#endif
} NOTIFYICONDATAW, *PNOTIFYICONDATAW;
Несложная проверка показала, что 2005-й компилил эту структуру без HICON hBalloonIcon, используя в условиях компиляции _WIN32_IE. Тут стоит сделать лирическое отступление и сказать, что в MSDN, идущем в составе Visual Studio 2008, сказано, что поля, отвечающие за облака, доступны только в версии Shell32.dll 5.0 и выше, и поэтому для их использования надо определить #define _WIN32_IE 0x0500 или #define _WIN32_IE 0x0600. Как положено порядочному программисту, кушающему кашку и слушающемуся MSDN, я использовал второй дефайн.
Но NTDDI_VERSION чихать хотел на _WIN32_IE. Если NTDDI_VERSION не определен, а определен _WIN32_WINNT, он использует его (сдвигая вправо на 16 бит). А на несколько строчек выше, если не определен ни _WIN32_WINNT, ни _CHICAGO_ (это кодовое имя 95-й винды), то _WIN32_WINNT полагается равным 0x0600, т.е. коду Висты! Переводя на простой русский язык: если Visual C++ 2008 не знает, какая у вас ось, значит, у вас Виста (которая здесь же, в сырцах, именуется Longhorn!). Shell32.dll от моей родной Windows XP получал «вистовый» размер 956, офигевал, и облака не выдавал.
Все это — попытка кратко описать, какая именно магия стоит за короткой строчкой воркараунда
#define _WIN32_WINNT _WIN32_WINNT_WINXP
и сколько может уйти времени на то, чтобы эту магию создать, если кто-то забыл изменить пару строчек в MSDN, обновив заголовочные файлы.