|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Visual Basic Subclassing Routines Shell_NotifyIcon: Windows Systray NOTIFYICONDATA Overview |
||
Posted: | Tuesday January 14, 2003 | |
Updated: | Monday December 26, 2011 | |
Applies to: | VB5, VB6 | |
Developed with: | VB6, Windows XP | |
OS restrictions: | None | |
Author: | VBnet - Randy Birch | |
Related: |
Shell_NotifyIcon: Windows Systray NOTIFYICONDATA Overview
Shell_NotifyIcon: Add Icon to Windows System Tray
Shell_NotifyIcon: Display Systray Balloon Tips |
|
Prerequisites | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
VB5 of VB6 to support AddressOf. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
The
Shell's systray was introduced with Windows 95 and exists in all versions
of Windows. And as enhancements to the Windows Shell have progressed, so
too has
the definition of the NOTIFYICONDATA structure used to add, modify and
delete icons and data placed into the systray, as well as to display,
modify and react to balloon tips in Windows 2000, Windows XP and Windows
.NET server. In its earliest form - known as version 3 as Shell 3.0 corresponded to the release of Internet Explorer 3, the NOTIFYICONDATA structure contained just seven members - cbSize, hWnd, uID, uFlags, uCallbackMessage, hIcon, and szTip. In Windows Shell versions prior to version 5, szTip was limited to 64 characters.
With the release of Shell 5 in Windows 2000, szTip was doubled to 128 characters and seven new structure parameters were added. Since two of the new structure members are defined as a C Union, which has no counterpart in VB, from our point of view these two structure members - uTimeout and uVersion - need to be represented as a single Long and thus from the VB point of view structure contained six new members. Shell version 6 released with Windows XP added a further reserved member, itself another UDT defining a GUID. Although for all practical purposes this member is never populated in VB, it is nonetheless expected in the XP definition.
In order for an application to properly use the NOTIFYICONDATA structure on all Windows versions the application must inform the shell of the version of the structure it is using. As is usual for a dynamic structure for Microsoft's APIs, the structure contains a cbSize member that is used to specify the size of the UDT being passed. Based on this size the API can take advantage of additional functionality more recent Shell versions provide. If your application uses and specifies the size of the latest XP structure format, the application may not run with earlier versions of Shell32.dll which expect a smaller structure. The cbSize member must therefore always, as a minimum, be assigned a value that represents the correct size for the current target Shell version. Note that this does not necessarily correlate to an OS version as some version of Internet Explorer, as recent as IE5, upgraded the shell in order to provide the new version's functionality. Therefore it is insufficient to test merely for the OS version using GetVersion or GetVersionEx. The Shell_NotifyIcon API requires the cbSize value be properly specified whenever a NOTIFYICONDATA structure is passed. Failing to set cbSize at all will cause the call to fail with no icon appearing in the systray. Passing a structure designed for use in later Windows versions to a prior shell version will cause either nothing to occur or if subclassed for messages, cause your app to crash. These points - shell version and structure size - are the two most popular issues developers seem to have difficulty with in designing systray applications to run on systems other than their development machine. The complete structure translated into a VB UDT that can be made viable for all current Windows Shell versions is: Private Type GUID
Data1 As Long
Data2 As Integer
Data3 As Integer
Data4(7) As Byte
End Type
Private Type NOTIFYICONDATA
cbSize As Long
hWnd As Long
uID As Long
uFlags As Long
uCallbackMessage As Long
hIcon As Long
szTip As String * 128 'shell 5+
dwState As Long 'shell 5+
dwStateMask As Long 'shell 5+
szInfo As String * 256 'shell 5+
uTimeoutAndVersion As Long 'shell 5+
szInfoTitle As String * 64 'shell 5+
dwInfoFlags As Long 'shell 5+
guidItem As GUID 'shell 6+
End Type
The best-case scenario would see your application employ this NOTIFYICONDATA structure in order to utilize post-Shell 5 features on newer operating systems, yet enable the structure to degrade gracefully to ensure at least minimal functionality (icon, tool tip, respond to mouse events) on pre-Shell 5 systems.
In order to utilize the latest structure in an application yet maintain compatibility with all previous Shell versions that application may be installed on it is necessary to forego using VB's Len() statement directly in setting up the cbSize member, and instead pre-determine the different NOTIFYICONDATA structure's sizes for when members specific to the different shell versions are added. These size values are define in the application as constants and through subsequent determination of the shell version the appropriate structure size is assigned to a NOTIFYICONDATA_SIZE variable: Private Const NOTIFYICONDATA_V1_SIZE As Long = 88 'pre-5.0 structure size
Private Const NOTIFYICONDATA_V2_SIZE As Long = 488 'pre-6.0 structure size
Private Const NOTIFYICONDATA_V3_SIZE As Long = 504 '6.0+ structure size
Private NOTIFYICONDATA_SIZE As Long
GetFileVersionInfoSize, GetFileVersionInfo and VerQueryValue executed against shell32.dll will return the shell's major version, so by creating a wrapper function - IsShellVersion - we have everything necessary to determine the shell version and set NOTIFYICONDATA_SIZE to the correct structure size value. This method of determining the proper structure size has been added to all VBnet's systray, systray animation and balloon tip demos listed above.
Private Declare Function GetFileVersionInfoSize Lib "version.dll" _
Alias "GetFileVersionInfoSizeA" _
(ByVal lptstrFilename As String, _
lpdwHandle As Long) As Long
Private Declare Function GetFileVersionInfo Lib "version.dll" _
Alias "GetFileVersionInfoA" _
(ByVal lptstrFilename As String, _
ByVal dwHandle As Long, _
ByVal dwLen As Long, _
lpData As Any) As Long
Private Declare Function VerQueryValue Lib "version.dll" _
Alias "VerQueryValueA" _
(pBlock As Any, _
ByVal lpSubBlock As String, _
lpBuffer As Any, _
nVerSize As Long) As Long
Private Sub SetShellVersion()
Select Case True
Case IsShellVersion(6)
NOTIFYICONDATA_SIZE = NOTIFYICONDATA_V3_SIZE '6.0+ structure size
Case IsShellVersion(5)
NOTIFYICONDATA_SIZE = NOTIFYICONDATA_V2_SIZE 'pre-6.0 structure size
Case Else
NOTIFYICONDATA_SIZE = NOTIFYICONDATA_V1_SIZE 'pre-5.0 structure size
End Select
End Sub
Private Function IsShellVersion(ByVal version As Integer) As Boolean
'returns True if the Shell version
'(shell32.dll) is equal or later than
'the value passed as 'version'
Dim nBufferSize As Long
Dim nUnused As Long
Dim lpBuffer As Long
Dim nVerMajor As Integer
Dim bBuffer() As Byte
Const sDLLFile As String = "shell32.dll"
nBufferSize = GetFileVersionInfoSize(sDLLFile, nUnused)
If nBufferSize > 0 Then
ReDim bBuffer(nBufferSize - 1) As Byte
Call GetFileVersionInfo(sDLLFile, 0&, nBufferSize, bBuffer(0))
If VerQueryValue(bBuffer(0), "\", lpBuffer, nUnused) = 1 Then
CopyMemory nVerMajor, ByVal lpBuffer + 10, 2 '*** see below for explanation
IsShellVersion = nVerMajor >= version
End If 'VerQueryValue
End If 'nBufferSize
End Function
To use this code, its simply a matter checking if NOTIFYICONDATA_SIZE is 0 before using in a call to Shell_NotifyIcon and if it is, calling SetShellVersion first...
Public Function ShellTrayAdd(sTipMsg As String) As Long
If NOTIFYICONDATA_SIZE = 0 Then SetShellVersion
With nid
'size of the NID type
.cbSize = NOTIFYICONDATA_SIZE
'... other structure members
*** The VerQueryValue API returns data formatted to the VS_FIXEDFILEINFO structure format: Private Type VS_FIXEDFILEINFO
dwSignature As Long
dwStrucVersion As Long 'e.g. 0x00000042 = "0.42"
dwFileVersionMS As Long 'e.g. 0x00030075 = "3.75"
dwFileVersionLS As Long 'e.g. 0x00000031 = "0.31"
dwProductVersionMS As Long 'e.g. 0x00030010 = "3.10"
dwProductVersionLS As Long 'e.g. 0x00000031 = "0.31"
dwFileFlagsMask As Long 'e.g. 0x3F for version "0.42"
dwFileFlags As Long 'e.g. VFF_DEBUG Or VFF_PRERELEASE
dwFileOS As Long 'e.g. VOS_DOS_WINDOWS16
dwFileType As Long 'e.g. VFT_DRIVER
dwFileSubtype As Long 'e.g. VFT2_DRV_KEYBOARD
dwFileDateMS As Long
dwFileDateLS As Long
End Type
As each member of the structure is defined as Long, the type has a 4-byte alignment for its members. Thus, when the data is returned in lpBuffer from VerQueryValue, using a CopyMemory call against lpBuffer, where pTo was the VS_FIXEDFILEINFO structure and Length was equal to Len(VS_FIXEDFILEINFO), the values corresponding to the structure above would be obtained. However, in our case we know specifically where in the structure the piece of data we want resides ... it is the dwFileVersionMS value. Even more specifically, we only want the two most-significant bytes of that Long. To obtain that data, instead of defining a VS_FIXEDFILEINFO structure the code simply moves to the appropriate spot in the data. Assuming a variable X defined As Long, we can obtain the VS_FIXEDFILEINFO member data directly from lpBuffer by using:
On an XP system that called VerQueryValue twice, once using the VS_FIXEDFILEINFO structure, and a second time using the CopyMemory code highlighted in red above, the return values would be:
393216 (fi.dwProductVersionMS value)
If fi.dwProductVersionMS was passed to a function to extract the two most-significant bytes (HiWord), the result would be an integer with the value of 6. But this can be directly obtained from lpBuffer by 'skipping over' the two least-significant bytes in dwFileVersionMS to obtain the two most-significant bytes directly. To do this, the offset used in the CopyMemory call is 10 (8 byte DWORD offset + 2 byte Integer offset). Like the HiWord value of fi.dwProductVersionMS, this also returns a 6 on XP, and will return the appropriate value for your Shell32.dll version. The NOTIFYICONDATA Structure Members
RemarksIf you set the NIF_INFO flag in the uFlags member, the standard ToolTip is replaced by a balloon ToolTip. For more discussion of balloon ToolTips, see the ToolTip Controls chapter. No more than one balloon ToolTip at a time is displayed for the taskbar. If an application attempts to display a ToolTip when one is already being displayed, the ToolTip will not appear until the existing balloon ToolTip has been visible for at least the system minimum timeout value. For example, a balloon ToolTip with uTimeout set to 30 seconds has been visible for seven seconds when another application attempts to display a balloon ToolTip. If the system minimum timeout is ten seconds, the first ToolTip displays for an additional three seconds before being replaced by the second ToolTip. However, you must initialize the structure with its size. You can run your application on pre-5.0 versions of Shell32.dll by defining the appropriate version number (see Shell and Common Controls Versions). However, this may cause problems if your application also needs to run on systems with more recent versions. Your can keep your application compatible with all Shell32.dll versions while still using the current header files by setting the size of the NOTIFYICONDATA structure appropriately. Before initializing the structure, use the DllGetVersion function to determine which Shell32.dll version is installed on the system. |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|
|||||
|
|||||
|
|||||
Copyright ©1996-2011 VBnet and Randy Birch. All Rights Reserved. |