Team LiB
Previous Section Next Section

13.8 Create a Windows Service That Uses a System Tray Icon

Problem

You want to create a Windows service that uses some type of user interface, such as a system tray icon.

Solution

If possible, do not implement any user interface in a Windows service. As a last resort, enable desktop interaction using the Computer Management console.

Discussion

Microsoft guidelines discourage user interfaces with any Windows service application. Adding a user interface can lead to security risks and can prevent the service from running at all if the required permission isn't granted. In fact, Windows services should be able to run with absolutely no user intervention—there might not even be a user logged on to the computer.

In cases where you do need a user interface to configure some aspects of the Windows service operation (or to view some data that it has processed), it's recommended that you create a separate application. For example, your Windows service can be configured to write data to a database or an event log, and you can create a separate program that can read this information and present it to the user. A more advanced design is to use .NET Remoting to allow a user interface tool to talk to a Windows service. In this way, you can use a program that can configure a Windows service in real time if required. (This design is similar to the way the SQL Server works with its system tray utility.) For more information on this approach, refer to the .NET Remoting recipes in Chapter 17.

However, if you decide to provide some basic interface through a Windows service, it is possible. For example, you might want to add a system tray icon that indicates the status of the current operation. You can easily add a NotifyIcon control to your Windows service project and configure it in your service code. Following is a rudimentary example that modifies the icon text to show the processing state. To use this example as written, you must import the System.ServiceProcess and System.Threading namespaces.

Public Class IconService
    Inherits System.ServiceProcess.ServiceBase
   
    <MTAThread()> _
    Shared Sub Main()
        Dim ServicesToRun() As ServiceBase
        ServicesToRun = New ServiceBase(){New IconService()}
        System.ServiceProcess.ServiceBase.Run(ServicesToRun)
    End Sub
   
    Private Components As System.ComponentModel.IContainer
    Friend ServiceIcon As System.Windows.Forms.NotifyIcon
   
    Private Sub InitializeComponent ()
        Me.Components = New System.ComponentModel.Container()
        Dim resources As New System.Resources.ResourceManager( _
          GetType(IconService))
   
        ' Configure the system tray icon.
        ' The bitmap is retrieved from a resource file.
        ' This code is generated automatically by Visual Studio .NET.
        Me.ServiceIcon = New System.Windows.Forms.NotifyIcon(Me.Components)
        Me.ServiceIcon.Icon = CType( _
          resources.GetObject("ServiceIcon.Icon"), System.Drawing.Icon)
        Me.ServiceIcon.Text = ""
        Me.ServiceIcon.Visible = True
   
        Me.ServiceName = "IconService"
   
        ' (Some of the automatically generated designer code is omitted.)
    End Sub
   
    Private ServiceThread As Thread
    Private StopThread As Boolean = False
   
    Protected Overrides Sub OnStart(ByVal args() As String)
        ServiceIcon.Text = "Starting ..."
        ServiceThread = New Thread(AddressOf DoWork)
        ServiceThread.Start()
    End Sub
   
    Protected Overrides Sub OnStop()
        ServiceIcon.Text = "Stopping ..."
   
        ' Try to signal the thread to end nicely,
        ' (and wait up to 20 seconds).
        StopThread = True
        ServiceThread.Join(TimeSpan.FromSeconds(20))
   
        ' If the thread is still running, abort it.
        If (ServiceThread.ThreadState And _
          ThreadState.Running) = ThreadState.Running Then
            ServiceThread.Abort()
        End If
    End Sub
   
    Private Sub DoWork()
        Do Until StopThread
            ServiceIcon.Text = "Processing"
            Thread.Sleep(TimeSpan.FromSeconds(10))
        Loop
        ServiceIcon.Text = "Not Processing"
    End Sub
   
End Class

You could extend this design by adding a context menu with event handlers for menu items. Remember, though, that this design isn't recommended because it will limit the scenarios in which you can use this service.

If you install this example Windows service, by default it will not work. When you attempt to start the service, an exception may be thrown or it may start but not display the system tray icon. The solution is to manually enable desktop interaction privileges using the Computer Management console. Find the service in the list, right-click it, and select Properties. Then, in the Log On tab, select the Allow Service To Interact With Desktop check box, as shown in Figure 13-8.

Click To expand
Figure 13-8: Allowing a service to interact with the desktop.

Team LiB
Previous Section Next Section
Converted from CHM to HTML with chm2web Pro 2.85 (unicode)