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


Shell_NotifyIcon: Windows Systray NOTIFYICONDATA Overview

Shell_NotifyIcon: Add Icon to Windows System Tray
Shell_NotifyIcon: Respond to Systray Icon/Menu Interaction
Shell_NotifyIcon: Respond to Systray Icon/Menu Interaction in a MDI App

Shell_NotifyIcon: Animate the System Tray Icon

Shell_NotifyIcon: Display Systray Balloon Tips
Shell_NotifyIcon: Respond to Systray Balloon Tip Clicks
Shell_NotifyIcon: Use SetTimer to Define Balloon Tip Life

VB5 of VB6 to support AddressOf.

7900 bytesThe 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.

typedef struct _NOTIFYICONDATA { 
    DWORD cbSize; 
    HWND hWnd; 
    UINT uID; 
    UINT uFlags; 
    UINT uCallbackMessage; 
    HICON hIcon; 
    TCHAR szTip[64];

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.

typedef struct _NOTIFYICONDATA { 
    DWORD cbSize; 
    HWND hWnd; 
    UINT uID; 
    UINT uFlags; 
    UINT uCallbackMessage; 
    HICON hIcon; 
    #if (_WIN32_IE < 0x0500)
        TCHAR szTip[64];
        TCHAR szTip[128];
    #if (_WIN32_IE >= 0x0500)
        DWORD dwState; 
        DWORD dwStateMask; 
        TCHAR szInfo[256]; 
        union {
            UINT  uTimeout; 
            UINT  uVersion; 
        TCHAR szInfoTitle[64]; 
        DWORD dwInfoFlags; 
    #if (_WIN32_IE >= 0x600)
        GUID guidItem;

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

   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

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
   '... other structure members

*** The VerQueryValue API returns data formatted to the VS_FIXEDFILEINFO structure format: 

   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:

VS_FIXEDFILEINFO member    Byte Alignment    CopyMemory code

1  dwSignature    0    CopyMemory X, ByVal lpBuffer, 4
2  dwStrucVersion    4    CopyMemory X, ByVal lpBuffer + 4, 4
3  dwFileVersionMS    8    CopyMemory X, ByVal lpBuffer + 8, 4
4  dwFileVersionLS    12    CopyMemory X, ByVal lpBuffer + 12, 4
    ... and so on             

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)
393216     (X value using offset +8)

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
Size of this structure, in bytes.
Handle to the window that receives notification messages associated with an icon in the taskbar status area. The Shell uses hWnd and uID to identify which icon to operate on when Shell_NotifyIcon is invoked.
Application-defined identifier of the taskbar icon. The Shell uses hWnd and uID to identify which icon to operate on when Shell_NotifyIcon is invoked. You can have multiple icons associated with a single hWnd by assigning each a different uID.
Flags that indicate which of the other members contain valid data. This member can be a combination of the following:
Flag Description
NIF_ICON The hIcon member is valid.
NIF_MESSAGE The uCallbackMessage member is valid.
NIF_TIP The szTip member is valid.
NIF_STATE The dwState and dwStateMask members are valid.
NIF_INFO Use a balloon ToolTip instead of a standard ToolTip. The szInfo, uTimeout, szInfoTitle, and dwInfoFlags members are valid.
NIF_GUID Reserved.
Application-defined message identifier. The system uses this identifier to send notifications to the window identified in hWnd. These notifications are sent when a mouse event occurs in the bounding rectangle of the icon, or when the icon is selected or activated with the keyboard. The wParam parameter of the message contains the identifier of the taskbar icon in which the event occurred. 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. See the Taskbar guide chapter for further discussion.
Handle to the icon to be added, modified, or deleted.
Pointer to a null-terminated string with the text for a standard ToolTip. It can have a maximum of 64 characters including the terminating NULL.

For Version 5.0 and later, szTip can have a maximum of 128 characters, including the terminating NULL.

Version 5.0. State of the icon. There are two flags that can be set independently.
Flag Description
NIS_HIDDEN The icon is hidden.
NIS_SHAREDICON The icon is shared.
Version 5.0. A value that specifies which bits of the state member are retrieved or modified. For example, setting this member to NIS_HIDDEN causes only the item's hidden state to be retrieved.
Version 5.0. Pointer to a null-terminated string with the text for a balloon ToolTip. It can have a maximum of 255 characters. To remove the ToolTip, set the NIF_INFO flag in uFlags and set szInfo to an empty string.
Union with uVersion. The timeout value, in milliseconds, for a balloon ToolTip. The system enforces minimum and maximum timeout values. uTimeout values that are too large are set to the maximum value and values that are too small default to the minimum value. The system minimum and maximum timeout values are currently set at 10 seconds and 30 seconds, respectively. See the remarks for further discussion of uTimeout.
Version 5.0. Union with uTimeout. Specifies whether the Shell notify icon interface should use Windows 95 or Windows 2000 behavior. For more information on the differences in these two behaviors, see Shell_NotifyIcon. This member is only employed when using Shell_NotifyIcon to send an NIM_VERSION message.
Value Description
0 Use the Windows 95 behavior. Use this value for applications designed for Windows versions prior to Windows 2000.
NOTIFYICON_VERSION Use the Windows 2000 behavior. Use this value for applications designed for Windows 2000 and later.
Version 5.0. Pointer to a null-terminated string containing a title for a balloon ToolTip. This title appears in boldface above the text. It can have a maximum of 63 characters.
Version 5.0. Flags that can be set to add an icon to a balloon ToolTip. It is placed to the left of the title. If the szTitleInfo member is zero-length, the icon is not shown. These flags are mutually exclusive.
Flag Description
NIIF_ERROR An error icon.
NIIF_INFO An information icon.
NIIF_NONE No icon.
NIIF_WARNING A warning icon.
NIIF_ICON_MASK Version 6.0. Reserved.
NIIF_NOSOUND Version 6.0. Do not play the associated sound. Applies only to balloon ToolTips.
Version 6.0. Reserved.


If 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.


PayPal Link
Make payments with PayPal - it's fast, free and secure!


Copyright 1996-2011 VBnet and Randy Birch. All Rights Reserved.
Terms of Use  |  Your Privacy


Hit Counter