NOTIFYICONDATA 结构中使用的 NOTIFYICON_VERSION 和 NOTIFYICON_VERSION_4 之间的区别?
Difference between NOTIFYICON_VERSION and NOTIFYICON_VERSION_4 used in NOTIFYICONDATA structure?
从 Windows 添加系统托盘图标时,我们可以通过 NOTIFYICONDATA
结构将 API 的两个版本传递给 Shell_NotifyIcon()
。两者之间存在细微差别 API,MSDN 上并未列出这些差别。我花了一些功夫才找出其中的一些差异,现在我将分享这些差异。 Improvements/additions 随时欢迎回答。
PS: 这个问题纯粹是为了分享我在过去几天尝试 windows DPI 缩放所学到的知识。
uVersion
NOTIFYICONDATA
结构的成员可以有 3 个可能的值,表示用于创建任务栏图标的 API 的版本。
- 0 将此值用于为 Windows 2000 之前的 Windows 版本设计的应用程序。
- NOTIFYICON_VERSION 使用 Windows 2000 行为。将此值用于为 Windows 2000 及更高版本设计的应用程序。
- NOTIFYICON_VERSION_4 使用当前行为。将此值用于为 Windows Vista 及更高版本设计的应用程序。
当涉及到托盘图标的消息处理程序时,wParam
和 uParam
的区别如下图所示。
请注意,在 NOTIFYICON_VERSION_4
中,wParam 给出了各种事件的 X 和 Y 坐标,但在 NOTIFYICON_VERSION
中没有提供获取坐标的规定。这引起了一个有趣的行为(这是我试图解决的一个 BUG 的原因)。如果您使用 NOTIFYICON_VERSION
,然后调用托盘图标的上下文菜单,那么鼠标光标,无论您在调用菜单时位于何处,都会被放置在托盘图标的正中央。即使您使用键盘 (WINDOWS+B) 调用图标的上下文菜单,鼠标光标仍会移动到图标.
在您查看我试图在 Pico torrent 应用程序中解决的这个特定 BUG 之前,您可能不会特别感兴趣。
场景是这样的。
- OS : Windows 10
- 应用程序不 per-monitor DPI 感知,但系统级 DPI 感知。
- 当用户登录时,桌面缩放设置有一个初始值,比如 150%。
- Pico 种子是 运行。
- DPI 缩放值更改为 125%
- 调用了 Pico torrent 的上下文菜单
上下文菜单不会显示在其应有的位置,并且会移位一点,显示出偏差。
查看以下图片以了解正在发生的事情。
问题是虽然 MSDN 说 GET_X_LPARAM(wParam)
和 GET_Y_LPARAM(wParam)
应该在托盘图标的处理程序中给出正确的值,但在存在DPI 缩放比例(即在不注销和登录的情况下更改 DPI 缩放比例)。另一方面,API GetCursorPos()
return 是鼠标光标坐标的正确值。请注意 NOTIFYICON_VERSION_4
和 GetCursorPos()
将不起作用,因为上下文菜单可以使用键盘调用,鼠标光标可以在屏幕上的任何位置。
那么,当以上述方式完成 DPI 缩放时,您如何结合刚刚学到的所有知识来正确显示托盘图标的上下文菜单,而不让您的应用程序 per-monitor DPI 感知(对于 per-monitor DPI 感知应用程序 GET_X_LPARAM(wParam)
,并且 GET_Y_LPARAM(wParam)
总是 return 正确值)?
使用 NOTIFYICON_VERSION
而不是 NOTIFYICON_VERSION_4
,这将在调用上下文菜单时将鼠标光标定位在托盘图标上,然后使用 GetCursorPos()
获取鼠标光标的位置。使用 TrackPopupMenu()
和坐标显示上下文菜单。
PS: 在上面的示例中,DPI 缩放值从 150% 更改为 125%。当您的托盘图标区域位于屏幕右下角时,当 DPI 从较大值缩放到较小值时,上下文菜单偏差会更加明显。这是因为当 DPI 缩放完成后,windows 放大 UI 不 per-monitor 感知的元素,使用 DPI 虚拟化,然后事情移动 right-wards,并且 down-wards.例如。如果在应用程序中windows 矩形是(0,0,100,100)(屏幕坐标),那么在放大到150% 之后,它可能变成(0,0,150,150)。现在对于托盘图标的菜单,如果你指定坐标超出屏幕 bottom-right,那么 OS 仍然会显示在屏幕内部的右下角位置,这确保了菜单是正确显示。例如。如果一个屏幕是1920x1080,并且给菜单TrackPopupMenu()
(10000,10000),菜单仍然会显示在1920x1080的屏幕矩形内。因此,增加 DPI 缩放将不会进一步移动上下文菜单,如果它已经到达 right-bottom 最位置。
@sahil-singh 你是对的,我同意其他人的观点,你的应用程序应该是 DPI 感知的,但这不是重点。
我有一个类似的问题,我的应用程序(仍然)不支持 DPI,并且 GET_X_LPARAM(wParam) 将 return non-virtual 坐标。将此值传递给 TrackPopupMenu() 后,我在屏幕上的位置错误。
最好的方法是使用 GetMessagePos() 而不是 wParam。在这种情况下,Windows 将为您提供带有虚拟坐标的新 DWORD,然后使用 GET_X_LPARAM/GET_Y_LPARAM 获取可传递给 TrackPopupMenu() 的值。
这似乎(2 年后的 2019 年)是 documented on MSDN:
NOTIFYICONDATAA structure
uCallbackMessage
Type: UINT
When the uVersion member is either 0 or NOTIFYICON_VERSION, the wParam parameter of the message contains the identifier of the taskbar icon in which the event occurred. This identifier can be 32 bits in length. The lParam parameter holds the mouse or keyboard message associated with the event. For example, when the pointer moves over a taskbar icon, lParam is set to WM_MOUSEMOVE.
When the uVersion member is NOTIFYICON_VERSION_4, applications continue to receive notification events in the form of application-defined messages through the uCallbackMessage member, but the interpretation of the lParam and wParam parameters of that message is changed as follows:
- LOWORD(lParam) contains notification events, such as NIN_BALLOONSHOW, NIN_POPUPOPEN, or WM_CONTEXTMENU.
- HIWORD(lParam) contains the icon ID. Icon IDs are restricted to a length of 16 bits.
- GET_X_LPARAM(wParam) returns the X anchor coordinate for notification events NIN_POPUPOPEN, NIN_SELECT, NIN_KEYSELECT, and all mouse messages between WM_MOUSEFIRST and WM_MOUSELAST. If any of those messages are generated by the keyboard, wParam is set to the upper-left corner of the target icon. For all other messages, wParam is undefined.
- GET_Y_LPARAM(wParam) returns the Y anchor coordinate for notification events and messages as defined for the X anchor.
从 Windows 添加系统托盘图标时,我们可以通过 NOTIFYICONDATA
结构将 API 的两个版本传递给 Shell_NotifyIcon()
。两者之间存在细微差别 API,MSDN 上并未列出这些差别。我花了一些功夫才找出其中的一些差异,现在我将分享这些差异。 Improvements/additions 随时欢迎回答。
PS: 这个问题纯粹是为了分享我在过去几天尝试 windows DPI 缩放所学到的知识。
uVersion
NOTIFYICONDATA
结构的成员可以有 3 个可能的值,表示用于创建任务栏图标的 API 的版本。
- 0 将此值用于为 Windows 2000 之前的 Windows 版本设计的应用程序。
- NOTIFYICON_VERSION 使用 Windows 2000 行为。将此值用于为 Windows 2000 及更高版本设计的应用程序。
- NOTIFYICON_VERSION_4 使用当前行为。将此值用于为 Windows Vista 及更高版本设计的应用程序。
当涉及到托盘图标的消息处理程序时,wParam
和 uParam
的区别如下图所示。
请注意,在 NOTIFYICON_VERSION_4
中,wParam 给出了各种事件的 X 和 Y 坐标,但在 NOTIFYICON_VERSION
中没有提供获取坐标的规定。这引起了一个有趣的行为(这是我试图解决的一个 BUG 的原因)。如果您使用 NOTIFYICON_VERSION
,然后调用托盘图标的上下文菜单,那么鼠标光标,无论您在调用菜单时位于何处,都会被放置在托盘图标的正中央。即使您使用键盘 (WINDOWS+B) 调用图标的上下文菜单,鼠标光标仍会移动到图标.
在您查看我试图在 Pico torrent 应用程序中解决的这个特定 BUG 之前,您可能不会特别感兴趣。
场景是这样的。
- OS : Windows 10
- 应用程序不 per-monitor DPI 感知,但系统级 DPI 感知。
- 当用户登录时,桌面缩放设置有一个初始值,比如 150%。
- Pico 种子是 运行。
- DPI 缩放值更改为 125%
- 调用了 Pico torrent 的上下文菜单 上下文菜单不会显示在其应有的位置,并且会移位一点,显示出偏差。
查看以下图片以了解正在发生的事情。
问题是虽然 MSDN 说 GET_X_LPARAM(wParam)
和 GET_Y_LPARAM(wParam)
应该在托盘图标的处理程序中给出正确的值,但在存在DPI 缩放比例(即在不注销和登录的情况下更改 DPI 缩放比例)。另一方面,API GetCursorPos()
return 是鼠标光标坐标的正确值。请注意 NOTIFYICON_VERSION_4
和 GetCursorPos()
将不起作用,因为上下文菜单可以使用键盘调用,鼠标光标可以在屏幕上的任何位置。
那么,当以上述方式完成 DPI 缩放时,您如何结合刚刚学到的所有知识来正确显示托盘图标的上下文菜单,而不让您的应用程序 per-monitor DPI 感知(对于 per-monitor DPI 感知应用程序 GET_X_LPARAM(wParam)
,并且 GET_Y_LPARAM(wParam)
总是 return 正确值)?
使用 NOTIFYICON_VERSION
而不是 NOTIFYICON_VERSION_4
,这将在调用上下文菜单时将鼠标光标定位在托盘图标上,然后使用 GetCursorPos()
获取鼠标光标的位置。使用 TrackPopupMenu()
和坐标显示上下文菜单。
PS: 在上面的示例中,DPI 缩放值从 150% 更改为 125%。当您的托盘图标区域位于屏幕右下角时,当 DPI 从较大值缩放到较小值时,上下文菜单偏差会更加明显。这是因为当 DPI 缩放完成后,windows 放大 UI 不 per-monitor 感知的元素,使用 DPI 虚拟化,然后事情移动 right-wards,并且 down-wards.例如。如果在应用程序中windows 矩形是(0,0,100,100)(屏幕坐标),那么在放大到150% 之后,它可能变成(0,0,150,150)。现在对于托盘图标的菜单,如果你指定坐标超出屏幕 bottom-right,那么 OS 仍然会显示在屏幕内部的右下角位置,这确保了菜单是正确显示。例如。如果一个屏幕是1920x1080,并且给菜单TrackPopupMenu()
(10000,10000),菜单仍然会显示在1920x1080的屏幕矩形内。因此,增加 DPI 缩放将不会进一步移动上下文菜单,如果它已经到达 right-bottom 最位置。
@sahil-singh 你是对的,我同意其他人的观点,你的应用程序应该是 DPI 感知的,但这不是重点。
我有一个类似的问题,我的应用程序(仍然)不支持 DPI,并且 GET_X_LPARAM(wParam) 将 return non-virtual 坐标。将此值传递给 TrackPopupMenu() 后,我在屏幕上的位置错误。
最好的方法是使用 GetMessagePos() 而不是 wParam。在这种情况下,Windows 将为您提供带有虚拟坐标的新 DWORD,然后使用 GET_X_LPARAM/GET_Y_LPARAM 获取可传递给 TrackPopupMenu() 的值。
这似乎(2 年后的 2019 年)是 documented on MSDN:
NOTIFYICONDATAA structure
uCallbackMessage
Type: UINT
When the uVersion member is either 0 or NOTIFYICON_VERSION, the wParam parameter of the message contains the identifier of the taskbar icon in which the event occurred. This identifier can be 32 bits in length. The lParam parameter holds the mouse or keyboard message associated with the event. For example, when the pointer moves over a taskbar icon, lParam is set to WM_MOUSEMOVE.
When the uVersion member is NOTIFYICON_VERSION_4, applications continue to receive notification events in the form of application-defined messages through the uCallbackMessage member, but the interpretation of the lParam and wParam parameters of that message is changed as follows:
- LOWORD(lParam) contains notification events, such as NIN_BALLOONSHOW, NIN_POPUPOPEN, or WM_CONTEXTMENU.
- HIWORD(lParam) contains the icon ID. Icon IDs are restricted to a length of 16 bits.
- GET_X_LPARAM(wParam) returns the X anchor coordinate for notification events NIN_POPUPOPEN, NIN_SELECT, NIN_KEYSELECT, and all mouse messages between WM_MOUSEFIRST and WM_MOUSELAST. If any of those messages are generated by the keyboard, wParam is set to the upper-left corner of the target icon. For all other messages, wParam is undefined.
- GET_Y_LPARAM(wParam) returns the Y anchor coordinate for notification events and messages as defined for the X anchor.