VB6 Systray icon remains after closing my app

I have a few functions in place to send my app to the systray and make use of display balloons.
My problem (if you can call it that) is that when I close the app THE ICON REMAINS IN THE SYSTRAY. When I move my mouse over the icon it disappears. This behaviour doesn't exist on any other app in the systray, so I wanted to know how I automatically remove this icon.

I have added a call to the 'DeleteIconFromTray' function on every event related to closing the main from. I'll post my current code in the next comment.

Note - in this particular app I always have an icon in the tray as I have the property 'ShowInTaskbar' = False and there it no title bar or border on the forms used.

' Type passed to Shell_NotifyIcon
Private Type NOTIFYICONDATA
Size As Long
Handle As Long
ID As Long
Flags As Long
CallBackMessage As Long
Icon As Long
Tip As String * 128
dwState As Long
dwStateMask As Long
szInfo As String * 256
uTimeoutAndVersion As Long
szInfoTitle As String * 64
dwInfoFlags As Long
guidItem As GUID
End Type

1. Why do you have to call DeleteIconFromTray immediately after calling AddIconToTray?
2. Why do you have to call DeleteIconFromTray in your Form_MouseMove event?
3. Shouldn't you have to use App.ThreadID instead of the fixed APP_SYSTRAY_ID?

The right way to do it is to add the icon to the tray (AddIconToTray) when the form loads.
Now, when the form unloads that's when you call the DeleteIconFromTray function.

DO NOT TRY TO REMOVE THE ICON FROM THE TRAY WHILE THE APP IS RUNNING
WHICH IS WHAT YOU'RE DOING IN THE "MouseMove" EVENT.

'---------------------------------------------------------------------------------------
Private Sub Form_Load()
'// add the icon to the system tray area
Call AddIconToTray("My Tool Tip")
End Sub
'---------------------------------------------------------------------------------------
Private Sub Form_Unload(Cancel As Integer)
'// remove the icon from the system tray
DeleteIconFromTray
End Sub
'---------------------------------------------------------------------------------------
Private Sub DeleteIconFromTray()

Private Type NOTIFYICONDATA
Size As Long
Handle As Long
ID As Long
Flags As Long
CallBackMessage As Long
Icon As Long
Tip As String * 128
dwState As Long
dwStateMask As Long
szInfo As String * 256
uTimeoutAndVersion As Long
szInfoTitle As String * 64
dwInfoFlags As Long
guidItem As GUID
End Type

should it be only

Private Type NOTIFYICONDATA
Size As Long
Handle As Long
ID As Long
Flags As Long
CallBackMessage As Long
Icon As Long
Tip As String * 64
End Type

See - that's the weird thing. I have the same code in another project and the icon does disappear whenever I close the app. I'll look for any discrepencies later today, otherwise I'll just google for some different sys tray code and try that.

>> 've found on projects that have more than one form don't actually end as a process shown in tasklist unless i use the End command. <<

This is probably the problem why your icon does not disappear immediately after you exit you app.
If this is the case, then use the following code in the Unload event of the main form to make sure all
loaded forms are, in fact, destroyed when your app exits.

The MouseMove event will be automatically taken care of once the "AttachForm" method has been
called, which pass the form object to the class so that the class can use its "hwnd" property thru-out
the program execution.

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
dwState As Long
dwStateMask As Long
szInfo As String * 256
uTimeoutAndVersion As Long
szInfoTitle As String * 64
dwInfoFlags As Long
guidItem As GUID
End Type

If Err Then
RaiseEvent OnError(Err.Description, "clsTrayIcon.ShowBalloon()")
Err.Clear
Else
ShowBalloon = True
End If

End Function

Public Function ShowPopupMenu(oForm As Form, _
mnuMainPopupMenu As Menu, _
x As Single, _
y As Single, _
Optional mnuDefaultPopupMenuItem As Menu = Nothing) As Long

'***********************************************************
' This method should be called from the client form's *
' MouseMove event if we don't use the AttachForm method. *
'***********************************************************

On Local Error Resume Next
Dim lMessage As Long

lMessage = x / Screen.TwipsPerPixelX

Select Case lMessage
Case WM_LBUTTONDBLCLK
oForm.WindowState = vbNormal
oForm.Show
Case WM_RBUTTONUP
If Not mnuMainPopupMenu Is Nothing Then
If mnuDefaultPopupMenuItem Is Nothing Then
oForm.PopupMenu mnuMainPopupMenu, , x, y
Else
oForm.PopupMenu mnuMainPopupMenu, , x, y, mnuDefaultPopupMenuItem
End If
End If
End Select

ShowPopupMenu = Err.Number

If Err Then
RaiseEvent OnError(Err.Description, "clsTrayIcon.ShowPopupMenu()")
Err.Clear
End If

'*****************************************************************
' This event receives the callbacks from the System Tray icon. *
' This should automatically take care of the mouse event on the *
' client form if we use the AttachForm method. *
'*****************************************************************
On Error Resume Next

Case WM_RBUTTONUP '517 display popup menu
If Not m_oPopupMenu Is Nothing Then
If Not m_oPopupMenuDefault Is Nothing Then
m_oForm.PopupMenu m_oPopupMenu, , , , m_oPopupMenuDefault
End If
End If

Case NIN_BALLOONSHOW
' Sent when the balloon is shown (balloons are queued)
Debug.Print "NIN_BALLOONSHOW"

Case NIN_BALLOONHIDE
' Sent when the balloon disappears—for example, when the icon is deleted.
' This message is not sent if the balloon is dismissed because of a timeout or a mouse click.
Debug.Print "NIN_BALLOONHIDE"

Case NIN_BALLOONTIMEOUT
' Sent when the balloon is dismissed because of a timeout
Debug.Print "NIN_BALLOONTIMEOUT"

Case NIN_BALLOONUSERCLICK
' Sent when the balloon is dismissed because of a mouse click
RaiseEvent OnBalloonClick(Trim(TrayIcon.szInfoTitle), Trim(TrayIcon.szInfo))

End Select

If Err Then
RaiseEvent OnError(Err.Description, "clsTrayIcon.m_oForm_MouseMove")
End If

Private Sub oTray_OnBalloonClick(ByVal sBalloonTitle As String, ByVal sBalloonMessage As String)
' Display the main window when the user clicks
' on a popup balloon from the system tray.
Me.WindowState = vbNormal
Me.Show
End Sub
'================================[ FORM CODE ]===================================

Great job jkaios - I've got a soltuion going now.
Out of the code I posted in the first comment - how much can I delete?

I made this change:
If UCase(Command) = "/HIDE" Then App.TaskVisible = False 'for Windows Startup

also another tip (although not used in this case):
instead of Dim WithEvents oTray As clsTrayIcon
and then Set oTray = New clsTrayIcon
you can just use Dim oTray As New clsTrayIcon (btw I called it clsSysTray)

I working through a few other probs that I'll post here later if I don't solve them (in regards to your code)

OK - I've worked through a lot of your code understanding how it works so that I can customize stuff for my needs - there's at least one thing that I need a bit of help with regarding the AttachForm function.

Using this function and deleteing my old code meant that mnuPopup wouldn't appear when r/clicking my tray icon. I found out that i could change it and add the arguments:
Call oTray.AttachForm(Me, mnuPopup, mnpTray)
however this makes "Restore From Tray" appear in bold (since it's default) - and I DON"T want this. So I removed the last argument but now I don't get the popup menu at all. Can I just use oTray.AttachForm(Me, mnuPopup) on its own somehow?

Case WM_RBUTTONUP '517 display popup menu
If Not m_oPopupMenu Is Nothing Then
If Not m_oPopupMenuDefault Is Nothing Then
m_oForm.PopupMenu m_oPopupMenu, , , , m_oPopupMenuDefault
Else '<<== ADD THIS LINE ==>>
m_oForm.PopupMenu m_oPopupMenu '<<== ADD THIS LINE ==>>
End If
End If

you wrote this:
>>also another tip (although not used in this case):
>>instead of Dim WithEvents oTray As clsTrayIcon
>>and then Set oTray = New clsTrayIcon
>>you can just use Dim oTray As New clsTrayIcon (btw I called it clsSysTray)

but that is not entirely true

if you dim an object as New, vb will (internally) EVERY time you access an property or method, check if the object exists.

Featured Post

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Get people started with the process of using Access VBA to control Excel using automation, Microsoft Access can control other applications. An example is the ability to programmatically talk to Excel. Using automation, an Access application can laun…

Viewers will learn how to properly install Eclipse with the necessary JDK, and will take a look at an introductory Java program.
Download Eclipse installation zip file:
Extract files from zip file:
Download and install JDK 8:
Open Eclipse and …