uVersion
член структуры NOTIFYICONDATA
может иметь 3 возможных значения, представляющих версию API, используемую для создания значка на панели задач.
- Используйте это значение для приложений, предназначенных для версий Windows, до Windows 2000.
- NOTIFYICON_VERSION Используйте поведение Windows 2000. Используйте это значение для приложений, предназначенных для Windows 2000 и более поздних версий.
- NOTIFYICON_VERSION_4 Использовать текущее поведение. Используйте это значение для приложений, предназначенных для Windows Vista и более поздних версий.
Когда дело доходит до обработчика сообщений для иконе подноса, wParam
и uParam
имеют различия, как показано на следующем рисунке.

Обратите внимание, что в NOTIFYICON_VERSION_4
WPARAM дает X и Y координаты различных событий, но не предусмотрена для получения координат в NOTIFYICON_VERSION
. Это вызывает интересное поведение (что было причиной ошибки, которую я пытался решить). Если вы используете NOTIFYICON_VERSION
, а затем вызовите контекстное меню значка в трее, то курсор мыши, где бы он ни находился, когда вы вызываете меню, попадает прямо в центр значка в трее. Даже если вы используете клавиатуру (WINDOWS + B) для вызова контекстного меню значка, курсор мыши по-прежнему перемещается к значку.
Это не может вас заинтересовать, пока вы не посмотрите на этот конкретный BUG, который я пытаюсь решить в приложении Pico torrent.
Вот сценарий.
- ОС: Windows 10
- Приложение не за монитор DPI в курсе, но на уровне системы DPI осознанным.
- Существует начальное значение набора масштабирования рабочего стола, скажем 150%, когда пользователь входит в систему.
- Pico торрент работает.
- DPI значение масштабирования изменяется, скажет контекстное меню 125%
- Пико торрентов вызываются контекстное меню не будет отображаться на своем месте, и будет смещаться немного, показывая девиации.
Чтобы узнать, что происходит, см. Следующие изображения.

Проблема заключается в том, что, хотя MSDN говорит, что GET_X_LPARAM(wParam)
и GET_Y_LPARAM(wParam)
должны давать правильные значения в обработчике трей, но это не делает, в присутствии DPI масштабирования (т.е. для изменение масштабирования DPI без выписки и входа в систему). С другой стороны, API GetCursorPos()
возвращает правильное значение координат курсора мыши. Обратите внимание, что NOTIFYICON_VERSION_4
вместе с GetCursorPos()
не будет работать, так как контекстное меню может быть вызвано с помощью клавиатуры, при которой курсор мыши может находиться где угодно на экране (экранах).
Итак, как вы объедините все знания, которые только что узнали, чтобы правильно отображать контекстное меню значка в трее, когда масштабирование DPI выполняется в соответствии с вышеописанным способом, не делая ваше приложение на мониторе DPI в курсе (для приложений, поддерживающих DPI-мониторинг) GET_X_LPARAM(wParam)
и GET_Y_LPARAM(wParam)
всегда возвращают правильное значение)?
Используйте NOTIFYICON_VERSION
вместо NOTIFYICON_VERSION_4
, это будет позиционировать курсор мыши на значке в трее, когда вызывается контекстное меню, а затем используйте GetCursorPos()
, чтобы получить позицию курсора мыши. Отобразите контекстное меню с помощью TrackPopupMenu()
с координатами.
PS: В приведенном выше примере значение масштабирования DPI изменяется от 150% до 125%. Отклонение контекстного меню более выражено, когда масштабирование DPI выполняется от большего значения до меньшего значения, когда область значка вашего лотка находится в нижней правой части экрана. Это происходит из-за того, что при масштабировании DPI окна увеличивают элементы пользовательского интерфейса, которые не контролируются монитором, используя виртуализацию DPI, тогда все перемещается вправо и вниз. например. если в приложении прямоугольник окон (0,0,100,100) (координаты экрана), то после увеличения до 150% он может стать (0,0,150,150). Теперь в меню значка в трее, если вы укажете координаты, лежащие за нижним правом экрана, тогда ОС все равно будет отображаться в нижнем правом положении, которое находится внутри экрана и которое гарантирует правильное отображение меню. например. если экран имеет разрешение 1920x1080, а для меню - TrackPopupMenu()
(10000 10000), меню будет отображаться внутри прямоугольника экрана 1920x1080. Таким образом, увеличение масштабирования DPI больше не будет перемещать контекстное меню, если оно уже достигло самой правой позиции в правом нижнем углу.
Правильное решение проблемы - сделать программу DPI-aware. При необходимости динамически загружайте 'SetProcessDpiAwareness()' во время выполнения. – andlabs
Также обратите внимание, что при появлении всплывающего окна значка в лотке с помощью клавиатуры вы можете использовать ['Shell_NotifyIconGetRect()'] (https://msdn.microsoft.com/en-us/library/windows/desktop/dd378426 (v = vs.85) .aspx), чтобы получить текущую позицию экрана самого значка, вместо использования 'GetCursorPos()', чтобы получить текущую позицию экрана мыши. –
@andlabs Нет, это не так.Потому что, если вы сделаете процесс для каждого монитора DPI осведомленным, вам придется обрабатывать масштабирование каждого элемента пользовательского интерфейса самостоятельно. Многие разработчики по-прежнему хотят использовать виртуализацию DPI в некоторой степени. Более того, если ваше приложение использует двоичный файл от третьего лица, который создает элементы пользовательского интерфейса, вы не сможете обрабатывать масштабирование DPI в нем, так как у вас нет кода. Это была основная причина, побудившая Microsoft внедрить новый API в WIndows 10 Anniversary Update, SetThreadDpiAwarenessContext(). –