NOTIFYICON_VERSION和NOTIFYICON_VERSION_4在NOTIFYICONDATA结构中使用的区别?

时间:2017-01-14 11:11:45

标签: windows winapi system-tray trayicon shell32.dll

在Windows中添加系统托盘图标时,我们可以通过Shell_NotifyIcon()结构将两个版本的API传递给NOTIFYICONDATA。这两个API之间存在细微差别,并且这些API未在MSDN上的任何位置列出。我花了一些力气来弄清楚我现在要分享的一些差异。总是欢迎改进/补充答案。

PS:这个问题纯粹是为了分享我过去几天学习Windows DPI扩展所学到的知识。

3 个答案:

答案 0 :(得分:1)

@ sahil-singh你是对的,我同意其他人的意见,你的应用程序应该是DPI意识到的,但是这不是一个重点。

我有一个类似的问题,我的应用程序(仍然)不支持DPI,GET_X_LPARAM(wParam)将返回非虚拟坐标。将此值传递给TrackPopupMenu()后,我在屏幕上的位置错误。

最好的方法是使用GetMessagePos()而不是wParam。在这种情况下,Windows将为您提供带有虚拟坐标的新DWORD,然后使用GET_X_LPARAM / GET_Y_LPARAM获取可以传递给TrackPopupMenu()的值。

答案 1 :(得分:0)

(2019年2年后)似乎是documented on MSDN:

  

NOTIFYICONDATAA结构

     

uCallbackMessage

     

类型: UINT

     

uVersion 成员为0或NOTIFYICON_VERSION时,消息的 wParam 参数包含事件发生在其中的任务栏图标的标识符。该标识符的长度可以为32位。 lParam 参数保存与事件关联的鼠标或键盘消息。例如,当指针移到任务栏图标上方时, lParam 设置为WM_MOUSEMOVE

     

uVersion 成员为NOTIFYICON_VERSION_4时,应用程序将继续通过 uCallbackMessage 成员以应用程序定义的消息形式接收通知事件,但对 lParam 和 wParam 参数更改如下:

     
      
  • LOWORD(lParam)包含通知事件,例如NIN_BALLOONSHOW,NIN_POPUPOPEN或WM_CONTEXTMENU。
  •   
  • HIWORD(lParam)包含图标ID。图标ID的长度限制为16位。
  •   
  • GET_X_LPARAM(wParam)返回通知事件NIN_POPUPOPEN,NIN_SELECT,NIN_KEYSELECT以及WM_MOUSEFIRST和WM_MOUSELAST之间的所有鼠标消息的X锚点坐标。如果这些消息中的任何一个由键盘生成,则wParam设置为目标图标的左上角。对于所有其他消息,wParam是未定义的。
  •   
  • GET_Y_LPARAM(wParam)返回针对X锚定义的通知事件和消息的Y锚坐标。
  •   

答案 2 :(得分:-1)

uVersion结构的

NOTIFYICONDATA成员可以有3个可能的值,表示用于创建任务栏图标的API版本。

  • 0 将此值用于为Windows 2000之前的Windows版本设计的应用程序。
  • NOTIFYICON_VERSION 使用Windows 2000行为。将此值用于为Windows 2000及更高版本设计的应用程序。
  • NOTIFYICON_VERSION_4 使用当前行为。将此值用于为Windows Vista及更高版本设计的应用程序。

对于托盘图标的消息处理程序,wParamuParam具有差异,如下图所示。

NOTIFYICON_VERSION vs NOTIFYICON_VERSION_4

请注意,在NOTIFYICON_VERSION_4中,wParam会给出各种事件的X和Y坐标,但没有规定在NOTIFYICON_VERSION中获取坐标。这引起了一个有趣的行为(这是我试图解决的BUG的原因)。如果您使用NOTIFYICON_VERSION,然后调用托盘图标的上下文菜单,则在您调用菜单时,鼠标光标位于托盘图标的中心位置。即使您使用键盘( WINDOWS + B )来调用图标的上下文菜单,鼠标光标仍会移动到图标。

在您查看我在Pico torrent应用程序中尝试解决的特定BUG之前,您可能不会特别感兴趣。

以下是该方案。

  • 操作系统:Windows 10
  • 应用程序不是每个监视器DPI感知,但是系统级DPI可识别。
  • 桌面缩放设置的初始值,例如150%,用户登录时。
  • Pico torrent正在运行。
  • DPI缩放值更改为,例如125%
  • 调用Pico torrent的上下文菜单 上下文菜单不会显示在适当的位置,并且会稍微移位,显示偏差。

请参阅以下图片以了解正在发生的事情。

DPI scaling level : 150 % DPI scaling level : 125 %

问题在于虽然MSDN表示GET_X_LPARAM(wParam)GET_Y_LPARAM(wParam)应该在托盘图标的处理程序中给出正确的值,但它没有,在存在DPI缩放的情况下(即,在不进行注销和登录的情况下更改DPI缩放)。另一方面,API GetCursorPos()返回鼠标光标坐标的正确值。请注意,NOTIFYICON_VERSION_4GetCursorPos()不起作用,因为可以使用键盘调用上下文菜单,鼠标光标可以位于屏幕上的任何位置。

那么,如何以上述方式完成DPI扩展时,如何将刚刚学到的所有知识结合起来正确显示托盘图标的上下文菜单,而不会使每个监视器的DPI感知应用程序(对于每个监视器的DPI感知应用程序{ {1}},GET_X_LPARAM(wParam)始终返回正确的值)?

使用GET_Y_LPARAM(wParam)代替NOTIFYICON_VERSION,这将在调用上下文菜单时将鼠标光标定位在托盘图标上,然后使用NOTIFYICON_VERSION_4获取鼠标光标的位置。使用带有坐标的GetCursorPos()显示上下文菜单。

PS:在上面的示例中,DPI缩放值从150%更改为125%。当托盘图标区域位于屏幕右下方时,当从较大值到较小值完成DPI缩放时,上下文菜单偏差更明显。这是因为当完成DPI缩放,并且窗口使用DPI虚拟化放大了不是每个监视器可识别的UI元素时,事情会向右移动,向下移动。例如。如果在应用程序中窗口矩形为(0,0,100,100)(屏幕坐标),则在放大到150%后,它可能变为(0,0,150,150)。现在对于托盘图标的菜单,如果指定位于屏幕右下方的坐标,则操作系统仍将显示位于屏幕内部的右下角位置,这可确保菜单正确显示。例如。如果屏幕为1920x1080,并且菜单给出TrackPopupMenu()(10000,10000),菜单仍将显示在1920x1080屏幕矩形内。因此,如果上下文菜单已经到达最右下角位置,则不会进一步移动上下文菜单。