# VBForums CodeBank > CodeBank - Visual Basic 6 and earlier >  VB6 System tray icon / systray

## Ellis Dee

The solution attached to this post allows for basic system tray functionality. You can create your systray icon (and change it on the fly) from a form icon or by loading/drawing an image on a 32x32 pixel picturebox. There is easy support for a popup menu, as well as showing standard balloon tips. That's all there is to basic systray icons.

If that's all you need, the attached demo project is for you. No dlls or ocxs; it's all pure native VB6, meaning fewer headaches for the developer. The logic is contained in a class, and is exposed to the developer in a form.

On to the demo:

----------


## Ellis Dee

*Overview*

As we can see from basDemo.bas, firing up the system tray couldn't be simpler:
vb Code:
Option Explicit
 Private Sub Main()
    If App.PrevInstance Then Exit Sub
    Load frmSysTray
End Sub
You pretty much never want two instances of a tray app running, so we use Previnstance to cancel multiple instances. If you decide to abandon the Sub Main approach and just make frmSysTray your startup form, add the following line of code to the very beginning of the Form_Load event:

If App.PrevInstance Then Unload Me

*Events*

The systray class fires messages directly to frmSysTray, so when you want to capture feedback from the tray icon, add event handlers to the form as if you were working with any regular control. Select "SysTray" from the left code dropdown -- where it typically starts with "(General)" -- and then choose any of the events you like from the code dropdown on the right. Supported events are:

BalloonClicked()
BalloonHide()
BalloonTimeout()
DoubleClick()
LeftClick()
RightClick()

The RightClick() event is where you typically open a popup menu, which is exactly what the demo does. Define your tray icon's popup menu in frmSysTray and handle the events from it there as well, just as with any regular menu. 

Note that LeftClick() can supercede DoubleClick(), so basically figure you can support one or the other. 

*Methods / Properties*

In addition to the tray icon talking to your app, your app will want to talk to the tray icon. From within frmSysTray, you can access the SysTray class using the following methods/properties:

SysTray.Init
Call this once in frmSysTray's Form_Load event to connect the class to the form. Optionally send a tooltip to display if the user hovers their mouse over the tray icon.

SysTray.MouseMove
This is an internal method you don't need to (and shouldn't) mess with. It is how the class captures messages from Windows, which in turn get interpreted and sent back to the form for you to use.

SysTray.ShowBalloonTip
Works the same as a msgbox. Send it a message, icon and title, plus optionally send the number of milliseconds it should stay on the screen before closing itself. Default is 30 seconds.

SysTray.SetIcon
Send this method a form and the tray icon will use that form's icon.

SysTray.DrawIcon
Send this method a picturebox and the tray icon will convert the picture into an icon. Optionally send a color to be used for transparency. This lets you draw the icon yourself using Line() commands if you need fine control, like say an activity indicator. Or you can use LoadPicture to work with image files. Note that the picturebox is 32x32 and the tray icon is typically only 16x16, so your images will likely get squished.

SysTray.TooltipText 
Simple property to set the tooltip text if you need to change it during runtime.

As written, the SysTray object is only available from within frmSysTray. If you want to access the tray in a different form or from a module or class, change the declaration at the top of frmSysTray to public:

Public WithEvents SysTray As clsSysTray

Now you can access it directly from anywhere in your project by first specifying the form. eg: 

frmSysTray.SysTray.ShowBalloonTip "Hello, world!"

----------


## Ellis Dee

For more information on working with the system tray:

Using the System Tray by Paul Kimmel (CodeGuru.com)

Animated system tray by Wokawidget

MSN Messenger Style Systray Notification Messages by Wokawidget

Showing Balloon Tips by Steve McMahon (vbAccelerator.com)

Here is a sample application using the code from this thread.

----------


## LaVolpe

If you want some tips on Vista/Win7 compatibility, here are a few.  I've been doing some research on this topic recently.

1. Vista offers users option to display a popup window instead of a tooltip. In Vista, hover over your Sound icon and the new "tip" should show.  In order to know that the window should be displayed, new msgs were added: NIN_POPUPOPEN and NIN_POPUPCLOSE.  Additionally, Vista can send the mouse coordinates back with the message.
:: The above requires at least two modifications
a. There is now a NOTIFYICON_VERSION_4 = 4 constant vs just version 3
b: Subclassing is required now, because one can no longer use VB's MouseMove events to trap the messages.  With v4, wParam contains both icon id and message, lParam contains the X,Y coords

2. Vista offers a couple of other balloon options too. One can select a 32x32 icon or 16x16 icon for the balloon.  In fact, the balloon icon can now be any icon you want and is no longer limited to those in WinXP and earlier.  Some new constants for Vista
NIIF_LARGEICON = &H20
NIIF_RESPECT_QUIET_TIME = &H80
NIF_REALTIME = &H40    
NIF_SHOWTIP = &H80     

3. The NOTIFYICONDATA structure grew by 4 bytes in Vista.  The balloon timeout is no longer honored/used

Some comments on your project, if you don't mind.

1. There are more messages that can be received
WM_CONTEXTMENU, all buttonclicks for the MiddleButton and XButton, NIN_KEYSELECT & NIN_SELECT

2. NIIF_USER is not defined in you BalloonIconEnum. If this option is chosen (XP and higher), the app icon is used.

3. FYI: You can offer a HideBalloon method if desired. Simply show a balloon with a null message and it will kill an existing balloon for the same icon.

4. Though not commonly used, it could be easy for you to offer multiple icons (i.e., 2 icons for same app displayed at same time) if you chose to do so.

----------


## Condomx

if i want to change the icon in the system tray where should i go and what should i edit?

----------


## Ellis Dee

> if i want to change the icon in the system tray where should i go and what should i edit?


Set the form icon in the form frmSysTray, and then from inside the form call SysTray.SetIcon Me.



> Some comments on your project, if you don't mind.
> 
> 1. There are more messages that can be received
> WM_CONTEXTMENU, all buttonclicks for the MiddleButton and XButton, NIN_KEYSELECT & NIN_SELECT
> 
> 2. NIIF_USER is not defined in you BalloonIconEnum. If this option is chosen (XP and higher), the app icon is used.
> 
> 3. FYI: You can offer a HideBalloon method if desired. Simply show a balloon with a null message and it will kill an existing balloon for the same icon.
> 
> 4. Though not commonly used, it could be easy for you to offer multiple icons (i.e., 2 icons for same app displayed at same time) if you chose to do so.


By all means. I just discovered this technique a couple days ago, so I'm far from an expert or anything.

1. What is ContextMenu, and how does it differ from RightClick? KeySelect? How do you use the keyboard with the system tray? I don't really understand any of the constants from this point; a little help?

3. Sweet, I looked around for how to do that. Clever. It won't disappear until it's been open at least 10 seconds, but still better than nothing.

4. What's the thinking behind having two icons display at the same time? I don't follow.

----------


## LaVolpe

> 1. What is ContextMenu, and how does it differ from RightClick? KeySelect? How do you use the keyboard with the system tray? I don't really understand any of the constants from this point; a little help?


With WinXP+, WM_ContextMenu was added  & simply means rightclick. You still get the right down/up, but get WM_ContextMenu also.

KeySelect is odd.  Here is how you can test it in XP.
:: Click that tray icon/circle that shows/hides tray icons. Leave mouse over the circle/icon
:: Use right arrow and move over the various icons. Pressing Space or Enter will trigger a KeySelect.  Honestly, why would someone navigate the system tray that way?




> 3. Sweet, I looked around for how to do that. Clever. It won't disappear until it's been open at least 10 seconds, but still better than nothing.


With Vista, it is immediate.  Maybe setting both the balloon title & message may make it immediate in XP and below? Curiosity.. will test this myself later.




> 4. What's the thinking behind having two icons display at the same time? I don't follow.


Not many apps do this.  Avast anti-virus app does. One is used primarily for database notifications/actions while the other is used for app settings. Avast was smart enough to allow user to combine both icons to a single icon.  Personally, I don't want an app adding multiple icons to my tray, but IMO should be a user's choice.  I think in your project, just creating another systray form would do the trick.  In my classes, I use the .UID member of the tray structure to identify each tray icon created.

Here are 3 MSDN reference I used for most of my research:
The NOTIFYICONDATA structure/description
The Shell_Notify API call and added remarks
Tray Message meanings

----------


## Ellis Dee

> With Vista, it is immediate.  Maybe setting both the balloon title & message may make it immediate in XP and below? Curiosity.. will test this myself later.


On second thought, I don't actually know that it isn't immediately cleared on XP if you send a blank balloon. I was reacting to the one-balloon-at-a-time functionality of the system tray, where if you stack up a bunch of messages they will only appear one at a time, waiting the minimum time (10 seconds) before closing each. If you send a blank balloon it may well simply close the currently open one. I'll test it and update the project with a new method if it does.

I really appreciate the feedback, especially those MSDN links. I wanted to create a good codebank thread for _simple_ system tray functionality. The ones I found were all super-advanced (animated? Messenger?) and not practical for those who just want a little icon to show up and be clickable. heh. This thread should serve that need well, as a nice jumping off point for all things systray.

----------


## Ellis Dee

> :: Use right arrow and move over the various icons. Pressing Space or Enter will trigger a KeySelect.  Honestly, why would someone navigate the system tray that way?


Accessibility for the disabled. Microsoft was probably required by law to add that functionality.

----------


## Edgemeal

Is it possible to show the balloon without that exit button in the top-right corner? 

In one of my apps the balloon only pops up to notify the user, clicking on the balloon doesn't do anything except close the balloon and set focus back on the form that was in use.

The problem is setting focus to a form after receiving the balloon "exit" click message, setfocus calls are ignored. If the user clicks the balloon setfocus works fine. What gives?

*EDIT:*
I tried this but when the balloon exit button is clicked the forms titlebar just flashes.


```
        Case BalloonClick, BalloonExit
            If Form1.WindowState <> 1 Then SetForegroundWindow Form1.hwnd
```

----------


## Ellis Dee

Not sure offhand, but my gut reaction is the standard tried-and-true work-around: timers. Kludgy I know, but for a temporary solution, consider using a timer with an interval of around 100. Have the BalloonExit event enable the timer, and have the timer do the setfocus and then turn itself off. Whatever it is about the event procedure that ignores the setfocus call will get purged as that event is allowed to end naturally. The timer then does its own independent thing, meaning it should be able to setfocus without issue.

Hopefully that will work well enough to tide you over until a real solution can be found.

----------


## Ellis Dee

> Is it possible to show the balloon without that exit button in the top-right corner?


After playing around with it for a bit for an app I'm writing, yes, just don't send a title. When you don't send a title the icon is ignored too and the only thing displayed is the message text.

On an unrelated note, the timeout setting seems to be ignored and it just stays open forever. I've added a second timer to frmSysTray that sends a blank message, immediately clearing the balloon tip. I then added three lines to the end of clsSysTray.ShowBalloonTip that allows leaving the balloontip open for any amount of time you like up to a minute:

```
frmSysTray.tmrHide.Enabled = False
frmSysTray.tmrHide.Interval = plngTimeout
frmSysTray.tmrHide.Enabled = True
```

Expect an updated attachment in a week or two after I've worked out all the kinks to everything.

----------


## Edgemeal

> After playing around with it for a bit for an app I'm writing, yes, just don't send a title. When you don't send a title the icon is ignored too and the only thing displayed is the message text.


I tried that with my balloon code and it does get rid of the exit button and the balloon auto closes after about 5-10 seconds if I do something, otherwise it seems to just stay open forever like you say which is actually how I'd want it to work anyway.  I never even tried no title, good catch!  :Thumb:

----------


## vbeeeeer

mods go ahead and delete

----------


## Condomx

this is a nice thing from Ellis Dee.,kep up da gud work man.CHEER!

----------


## jeffrey4u

i tried practicing it at home.
when i right-click on the icon at the taskbar after running the project, nothing happen. the only thing that keeps working is the tooltip n nothing more.
am using VISTA version of windows and i don't know if the windows has got something to do with it.
any help?

----------


## Edgemeal

> when i right-click on the icon at the taskbar after running the project, nothing happen. the only thing that keeps working is the tooltip n nothing more.
> am using VISTA version of windows and i don't know if the windows has got something to do with it.
> any help?


Don't have Vista here to test but one possible problem I see is the code expects the form to be set to pixel scalemode, if not then you need to convert X for the SysTray.MouseMove calls, maybe something like,..



```
Dim msg As Single
msg = ScaleX(X, ScaleMode, vbPixels)
SysTray.MouseMove Button, msg, Me
```

----------


## Ellis Dee

Upthread *LaVolpe* says that subclassing is required in Vista. Subclassing makes me wet myself, so I'm afraid I won't be much help with Vista support. If I can manage to get my hands on a Vista machine I'll try to take a look.

----------


## Edgemeal

I think LaVolpe is saying subclass is needed if you want to use a popup window instead of a tooltip in Vista. ?

Might want to try using a pic box for the tray handle and mouse_move events instead of the form, years ago I ran into unexpected results using the form for the tray, been using a pic box ever since and haven't had a problem/complaint.

----------


## jeffrey4u

ok am waiting .
try it on a VISTA mchine n help me solve mine.
thz

----------


## Ellis Dee

> Might want to try using a pic box for the tray handle and mouse_move events instead of the form, years ago I ran into unexpected results using the form for the tray, been using a pic box ever since and haven't had a problem/complaint.


*jeffrey*, to try this, replace the Init() function in clsSysTray with this:

```
Public Function Init(ppic As PictureBox, pstrTooltip As String) As Boolean
    Const NIF_MESSAGE = &H1
    Const WM_MOUSEMOVE = &H200
    Const NIM_ADD = &H0
    
    With mtypIcon
        .cbSize = Len(mtypIcon)
        .hwnd = ppic.hwnd
        .hIcon = ppic.Parent.Icon
        .uID = vbNull
        .uFlags = NIF_MESSAGE
        .uCallbackMessage = WM_MOUSEMOVE
    End With
    Shell_NotifyIcon NIM_ADD, mtypIcon
    SetIcon pfrm
    Me.TooltipText = pstrTooltip
End Function
```

Then change Form_Load() in frmSysTray to this:

```
Private Sub Form_Load()
    Set SysTray = New clsSysTray
    Me.WindowState = vbMinimized
    DoEvents
    Me.Hide
    SysTray.Init Me.pic, "System Tray demo"
End Sub
```

----------


## Edgemeal

Ellis, you'll get an error since you are no longer passing the form to the Init func., maybe like this?...



```
Public Sub SetIcon(hIcon As Long)
    mtypIcon.hIcon = hIcon
    RefreshIcon
End Sub

Public Function Init(ppic As PictureBox, pstrTooltip As String) As Boolean
    Const NIF_MESSAGE = &H1
    Const WM_MOUSEMOVE = &H200
    Const NIM_ADD = &H0
    
    With mtypIcon
        .cbSize = Len(mtypIcon)
        .hwnd = ppic.hwnd
        .hIcon = ppic.Parent.Icon
        .uID = vbNull
        .uFlags = NIF_MESSAGE
        .uCallbackMessage = WM_MOUSEMOVE
    End With
    Shell_NotifyIcon NIM_ADD, mtypIcon
    SetIcon ppic.Parent.Icon 'pfrm
    Me.TooltipText = pstrTooltip
End Function
```

----------


## Ellis Dee

Good catch, thanks. I should know better than to post code freehand without actually trying it in VB.

I would have probably left the SetIcon() function alone and just sent ppic.Parent to it, but either way works.

----------


## sinac

Hi, How to change font and font-size balloon?

----------


## Ellis Dee

> Hi, How to change font and font-size balloon?


No clue, sorry.

----------


## escalador

Hello, the right-click menu does not disappear after clicking on desktop or somewhere else, it's there a solution to that ? Thanks

----------


## Ellis Dee

Ah, yeah, happily that's an easy fix. In the form code, add a single API call to the Right_Click() event, plus of course the API declaration.



```
Option Explicit

Private Declare Function SetForegroundWindow Lib "user32" (ByVal hwnd As Long) As Long
[...]
Private Sub SysTray_RightClick()
    SetForegroundWindow Me.pic.hwnd ' Auto close menu if user clicks away
    PopupMenu Me.mnuSysTray
End Sub
```

----------


## escalador

That was really fast reply. Thanks alot !

----------


## public

Oh dear why ya're messing around with a picturebox?  :Alien Frog: 


What about the VB 6 - Ressource-Editor to include icon's to the exe?
If the menu item 'Tools\Ressource editor' is not visible in ya VB6-IDE - you may need to enable this via 'Add-ins\Add-in-Manager'
<_Moderator note: Removed links to EXE files.>_
Okay well incase you are using 'Visual Basic 6 Portable' here is how to add the Ressource editor
Get these files from somewhere (sorry binaries are not allowed here)
c:\Program Files\Visual Basic 6 Portable\commtb32.dll
c:\Program Files\Visual Basic 6 Portable\wizards\rc.exe
c:\Program Files\Visual Basic 6 Portable\wizards\rcdll.dll
c:\Program Files\Visual Basic 6 Portable\wizards\resedit.dll 
(c:\Program Files\Visual Basic 6 Portable\wizards\rsedtdeu.dll)
c:\Program Files\Visual Basic 6 Portable\wizards\rsedtenu.dll
regsvr32.exe resedit.dll
(regsvr32.exe rsedtdeu.dll)
regsvr32.exe rsedtenu.dll

Well extent the *clsSysTray.cls* by this:


```
Public Sub SetIconFromRes(ResID As Variant)
    Me.Icon = LoadResPicture(ResID, vbResIcon)
End Sub


Public Property Get Icon() As Long
  Icon = mtypIcon.hIcon
End Property

Public Property Let Icon(ByVal vNewValue As Long)
   mtypIcon.hIcon = vNewValue
   RefreshIcon
End Property
```

Now here are some inspiration for the use

vb Code:
SysTray.Icon = LoadPicture("WRENCH.ICO")
    SysTray.SetIconFromRes 101
    SysTray.SetIconFromRes "Pause"


and maybe now uncomment/delete these Function/subs 
DrawIcon, BitmapToIcon, BitmapToIconTransparent, GetColMask

and also

' Bitmap to Icon
Private Type * and
Private Declare Function *

... and yes on more thing
*Attention:*
When using clsSysTray.cls with an own form,
Set the *form properties* Scale\*ScaleMode = 3 - Pixel* !


If there is something else selected (like for ex. 1-Twips) the code ...


vb Code:
Public Sub MouseMove(Button As Integer, ByVal X As Long, pfrm As Form)
   Select Case X
        Case WM_LBUTTONDBLCLK: RaiseEvent DoubleClick
        Case WM_LBUTTONUP: RaiseEvent LeftClick
        Case WM_RBUTTONUP: RaiseEvent RightClick
...
...will not match the the constant 'WM_LBUTTONDBLCLK' due to other scaling 
...and so stuff like the rightclick menu will not work!

... and you can delete 'Set frmSysTray = Nothing' in

vb Code:
Private Sub Class_Terminate()
...
    Set frmSysTray = Nothing
End Sub
it is unnecessary.

----------


## jg.sa

G'Day Guys




> That was really fast reply. Thanks alot !


I hope this happens to me as well  :Big Grin: 

I know this is now a last decade post, but I have been working my way thru. the example & it works really well  :Cool: 

Now I would like to 'Restore' the form so I can have some 'settings' etc. for the app.

Can anyone give me a link to a sample that can show me how to restore the form & hide the tray icon ???

TIA

----------


## Bobbles

There may very well be more (and better) ways to skin this cat.
But the attached appears to do what you asked.
There is only one change in the class (making the terminate Public)
There is one change in the IDE to add the minimize button to the form
The other changes are in the Form's code.
Rob

----------


## Bobbles

PS I notice the form when arising from the sys tray, tends to be lower in the pecking order (you have to minimize other running apps, to see this form).
If you find that annoying, you could try Method 3 on this web page -
http://www.vbforums.com/showthread.p...-top-of-others

----------


## jg.sa

G'Day Bobbles

Thanks for the info & .zip all working really well  :Smilie: 

If anyone else trips over this thread I have found from feedback that you really need to be using Global Hotkey to get the app. into the tray & out of the tray, this is much easier than a 'Restore' from the tray & option in the app. to go to the tray !!!

Just my 2c worth !!!

----------

