–This page is mostly just here for archival purposes. It’s way outdated–
In Visual Basic.NET, click “Project”, “Add User Control…” then put all this stuff in it. Then under the Toolbox in the form Designer, you should have a new thing under My User Controls. NICE!
Imports System
Imports System.Drawing
Imports System.Windows.Forms
Imports System.Runtime.InteropServices
Imports System.ComponentModel
Imports System.Reflection
Public Class NotifyIconEx
Inherits System.ComponentModel.Component
#Region "Notify Icon Target Window"
Private Class NotifyIconTarget
Inherits System.Windows.Forms.Form
Public Sub NotifyIconTarget()
Me.Text = "Hidden NotifyIconTarget Window"
End Sub
Protected Overrides Sub DefWndProc(ByRef msg As System.Windows.Forms.Message)
If (msg.Msg = &H400) Then '// WM_USER
Dim msgId As Integer = msg.LParam.ToInt32
Dim id As Integer = msg.WParam.ToInt32
Select Case msgId
Case &H201 ' WM_LBUTTONDOWN
Case &H202 ' WM_LBUTTONUP
'If (ClickNotify) Then
RaiseEvent ClickNotify(Me, id)
'End If
Case &H203 ' WM_LBUTTONDBLCLK
'If (DoubleClickNotify <> null) Then
RaiseEvent DoubleClickNotify(Me, id)
'End If
Case &H205 ' WM_RBUTTONUP
'If (RightClickNotify <> null) Then
RaiseEvent RightClickNotify(Me, id)
'End If
Case &H200 ' WM_MOUSEMOVE
Case &H402 ' NIN_BALLOONSHOW
' this should happen when the balloon is closed using the x
' - we never seem to get this message!
Case &H403 ' NIN_BALLOONHIDE
' we seem to get this next message whether the balloon times
' out or whether it is closed using the x
Case &H404 ' NIN_BALLOONTIMEOUT
Case &H405 ' NIN_BALLOONUSERCLICK
'If (ClickBalloonNotify <> Nothing) Then
RaiseEvent ClickBalloonNotify(Me, id)
'End If
End Select
ElseIf (msg.Msg = &HC086) Then ' WM_TASKBAR_CREATED
'If (TaskbarCreated <> Nothing) Then
RaiseEvent TaskbarCreated(Me, System.EventArgs.Empty)
'End If
Else
MyBase.DefWndProc(msg)
End If
End Sub
Public Delegate Sub NotifyIconHandler(ByVal sender As Object, ByVal id As Long)
Public Event ClickNotify As NotifyIconHandler
Public Event DoubleClickNotify As NotifyIconHandler
Public Event RightClickNotify As NotifyIconHandler
Public Event ClickBalloonNotify As NotifyIconHandler
Public Event TaskbarCreated As EventHandler
End Class
#End Region
#Region "Platform Invoke"
<StructLayout(LayoutKind.Sequential)> Private Structure NotifyIconData
Public cbSize As Integer ' DWORD
Public hWnd As System.IntPtr ' HWND
Public uID As Integer ' UINT
Public uFlags As NotifyFlags ' UINT
Public uCallbackMessage As Integer ' UINT
Public hIcon As System.IntPtr ' HICON
<MarshalAs(UnmanagedType.ByValTStr, sizeconst:=128)> Public szTip As String
Public dwState As NotifyState ' DWORD
Public dwStateMask As NotifyState ' DWORD
<MarshalAs(UnmanagedType.ByValTStr, sizeconst:=256)> Public szInfo As String
Public uTimeoutOrVersion As Integer ' UINT
<MarshalAs(UnmanagedType.ByValTStr, sizeconst:=64)> Public szInfoTitle As String
Public dwInfoFlags As NotifyInfoFlags ' DWORD
End Structure
<StructLayout(LayoutKind.Sequential)> Private Structure POINT
Public x As Integer
Public y As Integer
End Structure
Private Overloads Declare Function Shell_NotifyIcon Lib "shell32.dll" (ByVal cmd As NotifyCommand, ByRef data As NotifyIconData) As Boolean
Private Overloads Declare Function TrackPopupMenuEx Lib "user32.dll" (ByVal hMenu As System.IntPtr, ByVal uFlags As Long, ByVal x As System.Int32, ByVal y As System.Int32, ByVal hwnd As System.IntPtr, ByVal ignore As System.IntPtr) As System.Int32
Private Overloads Declare Function GetCursorPos Lib "user32.dll" (ByRef point As POINT) As System.Int32
Private Overloads Declare Function SetForegroundWindow Lib "user32.dll" (ByVal hWnd As System.IntPtr) As System.Int32
Private Overloads Declare Function PostMessage Lib "user32.dll" Alias "PostMessageA" (ByVal hwnd As System.IntPtr, ByVal wMsg As Integer, ByVal wParam As System.IntPtr, ByVal lParam As System.IntPtr) As Integer
#End Region
Public Enum NotifyInfoFlags As Integer
[Error] = &H3
Info = &H1
None = &H0
Warning = &H2
End Enum
Private Enum NotifyCommand As Integer
Add = &H0
Delete = &H2
Modify = &H1
End Enum
Private Enum NotifyFlags As Integer
Message = &H1
Icon = &H2
Tip = &H4
Info = &H10
State = &H8
End Enum
Private Enum NotifyState As Integer
Hidden = &H1
End Enum
Private m_id As Long = 0 ' each icon in the notification area has an id
Private m_handle As IntPtr ' save the handle so that we can remove icon
Private Shared m_messageSink As NotifyIconTarget = New NotifyIconTarget
Private Shared m_nextId As Long = 1
Private m_text As String = ""
Private m_icon As Icon = Nothing
Private m_contextMenu As ContextMenu = Nothing
Private m_visible As Boolean = False
Private m_doubleClick As Boolean = False ' fix for extra mouse up message we want to discard
Public Event Click As EventHandler
Public Event DoubleClick As EventHandler
Public Event BalloonClick As EventHandler
#Region "Properties"
Public Property Text() As String
Set(ByVal Value As String)
If (m_text <> Value) Then
m_text = Value
CreateOrUpdate()
End If
End Set
Get
Return m_text
End Get
End Property
Public Property Icon() As Icon
Set(ByVal Value As Icon)
m_icon = Value
CreateOrUpdate()
End Set
Get
Return m_icon
End Get
End Property
Public Property ContextMenu() As ContextMenu
Set(ByVal Value As ContextMenu)
m_contextMenu = Value
End Set
Get
Return m_contextMenu
End Get
End Property
Public Property Visible() As Boolean
Set(ByVal Value As Boolean)
If (m_visible <> Value) Then
m_visible = Value
CreateOrUpdate()
End If
End Set
Get
Return m_visible
End Get
End Property
#End Region
Public Sub NotifyIconEx()
End Sub
' this method adds the notification icon if it has not been added and if we
' have enough data to do so
Private Sub CreateOrUpdate()
If (Me.DesignMode) Then
Return
End If
If (m_id = 0) Then
If (Not m_icon Is Nothing) Then
' create icon using available properties
Create(m_nextId)
m_nextId += 1
End If
Else
' update notify icon
Update()
End If
End Sub
Private Sub Create(ByVal id As Long)
Dim Data As NotifyIconData = New NotifyIconData
Data.cbSize = Marshal.SizeOf(Data)
m_handle = m_messageSink.Handle
Data.hWnd = m_handle
m_id = id
Data.uID = m_id
Data.uCallbackMessage = &H400
Data.uFlags = NotifyFlags.Icon Or NotifyFlags.Message Or NotifyFlags.Tip
Data.hIcon = m_icon.Handle ' this should always be valid
Data.szTip = m_text
If (m_visible = False) Then
Data.dwState = NotifyState.Hidden
End If
Data.dwStateMask = NotifyState.Hidden
Dim ret As Boolean = Shell_NotifyIcon(NotifyCommand.Add, Data)
' add handlers
AddHandler m_messageSink.ClickNotify, AddressOf OnClick
AddHandler m_messageSink.DoubleClickNotify, AddressOf OnDoubleClick
AddHandler m_messageSink.RightClickNotify, AddressOf OnRightClick
AddHandler m_messageSink.ClickBalloonNotify, AddressOf OnClickBalloon
AddHandler m_messageSink.TaskbarCreated, AddressOf OnTaskbarCreated
End Sub
' update an existing icon
Private Sub Update()
Dim data As NotifyIconData = New NotifyIconData
data.cbSize = Marshal.SizeOf(data)
data.hWnd = m_messageSink.Handle
data.uID = m_id
data.hIcon = m_icon.Handle ' this should always be valid
data.szTip = m_text
data.uFlags = NotifyFlags.Icon Or NotifyFlags.Tip Or NotifyFlags.State
data.dwStateMask = NotifyState.Hidden
If (m_visible = False) Then
data.dwState = NotifyState.Hidden
End If
Dim ret As Boolean = Shell_NotifyIcon(NotifyCommand.Modify, data)
End Sub
Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
Remove()
MyBase.Dispose(disposing)
End Sub
Public Sub Remove()
If (m_id <> 0) Then
' remove the notify icon
Dim data As NotifyIconData = New NotifyIconData
data.cbSize = Marshal.SizeOf(data)
data.hWnd = m_handle
data.uID = m_id
Shell_NotifyIcon(NotifyCommand.Delete, data)
m_id = 0
End If
End Sub
Public Sub ShowBalloon(ByVal title As String, ByVal text As String, ByVal type As NotifyInfoFlags, ByVal timeoutInMilliSeconds As Integer)
If (timeoutInMilliSeconds < 0) Then
Throw New ArgumentException("The parameter must be positive", "timeoutInMilliseconds")
End If
Dim data As NotifyIconData = New NotifyIconData
data.cbSize = Marshal.SizeOf(data)
data.hWnd = m_messageSink.Handle
data.uID = m_id
data.uFlags = NotifyFlags.Info
data.uTimeoutOrVersion = timeoutInMilliSeconds ' this value does not seem to work - any ideas?
data.szInfoTitle = title
data.szInfo = text
data.dwInfoFlags = type
Dim ret As Boolean = Shell_NotifyIcon(NotifyCommand.Modify, data)
End Sub
#Region "Message Handlers"
Private Sub OnClick(ByVal sender As Object, ByVal id As Long)
If (id = m_id) Then
If (m_doubleClick = False) Then
RaiseEvent Click(Me, EventArgs.Empty)
m_doubleClick = False
End If
End If
End Sub
Private Sub OnRightClick(ByVal sender As Object, ByVal id As Long)
If (id = m_id) Then
' show context menu
If (Not m_contextMenu Is Nothing) Then
Dim point As Point = New Point
GetCursorPos(point)
SetForegroundWindow(m_messageSink.Handle) ' this ensures that if we show the menu and then click on another window the menu will close
' call non public member of ContextMenu
m_contextMenu.GetType().InvokeMember("OnPopup", BindingFlags.NonPublic Or BindingFlags.InvokeMethod Or BindingFlags.Instance, Nothing, m_contextMenu, New Object() {System.EventArgs.Empty})
TrackPopupMenuEx(m_contextMenu.Handle, 64, point.x, point.y, m_messageSink.Handle, IntPtr.Zero)
PostMessage(m_messageSink.Handle, 0, IntPtr.Zero, IntPtr.Zero)
End If
End If
End Sub
Private Sub OnDoubleClick(ByVal sender As Object, ByVal id As Long)
If (id = m_id) Then
m_doubleClick = True
'If (Not DoubleClick Is Nothing) Then
RaiseEvent DoubleClick(Me, EventArgs.Empty)
'End If
End If
End Sub
Private Sub OnClickBalloon(ByVal sender As Object, ByVal id As Long)
If (id = m_id) Then
'If (BalloonClick <> Nothing) Then
RaiseEvent BalloonClick(Me, EventArgs.Empty)
'End If
End If
End Sub
Private Sub OnTaskbarCreated(ByVal sender As Object, ByVal e As EventArgs)
If (m_id <> 0) Then
Create(m_id) ' keep the id the same
End If
End Sub
#End Region
End Class