The following article is based on information that I gleaned from these two sources: Jayesh Jain’s article at: http://www.developerfusion.co.uk/show/3441/2/ and the MSDN article Introduction to Windows Service Applications at: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vbcon/html/vbconintroductiontontserviceapplications.asp
Originally called a NT service, the core function of a Windows service is to run an application in the background. There are few things that make them different from a Windows application. A Windows service can start before any user logs in to the system (if it has been setup to start at start up process). A Windows service can also be setup in such a way that it requires a user to start it manually.
Windows services are processes and run quite efficiently. Normally a Windows service will not have a user interface for the simple reason that it can be run even if no one is logged into the system. This is not a rule, just good design.
In Windows XP you can view a list of services currently running on your computer by opening Control Panel / Administrative Tools / Services, as shown here:
Before .NET, creating a Windows service was hard. Thanks to .NET, however, this is becoming very easy and we shall now learn how to create a Windows Service in VB.NET. The steps outlined here are for VB 2005 (Visual Studio 2005), but you might do this with Visual Studio 2003 by employing DesktopSerialIO.dll, instead of the VS2005 SerialPort component. Note, the source code for this project is included on the CD ROM that accompanies my book.
There are a few things that you should know before we dive in, however. Windows services are not available under Windows 95, 98 or ME -- you need to have Windows NT 4.0, Windows 200x and Windows XP to run services.
The advantage to use .NET is that the framework incorporates all of the classes to create, install and control a Windows Service. Open Visual Studio .NET and create a new Windows service project, which we shall call "GPSLocationService". Click OK. Our GPSLocationService service will do these things:
· Automatically detect a GPS receiver, and open the port associated with the receiver (note, the GPS receiver port will not be available to other applications if this service is running).
· Log Date, Time, and valid Latitude and Longitude to the EventLog.
Add a serial port control from the Toolbox. Add a reference to DecodeGPS.dll. Import this needed references as shown in the Source Code section. Add two Timer controls. You will need to add these to the Toolbox – the Timer control that is already there is not the one that you need; this is a bug! Instead, add a System.Timers.Timer and drop two on the design surface. The Windows.Forms.Timer that already is in the Toolbox will not work in a Service.
Hint: Use of one or more System.Timers.Timer timers is a common feature in a Windows Service. Services execute in the background and usually require monitoring of PC data or events at regular intervals. Timers are a naturally way to drive such processes.
Double click the design surface to open the code window. Type in the following declarations at the top of the Service1 Class:
Private WithEvents GPSDecoder As New DecodeGPS.DecodeGPS
Private GPSFound As Boolean
Private GPSPort As String
Private PortIndex As Integer
Private SerialPortNames() As String
Private StartUp As Boolean = True
Private FirstLog As Boolean = True
Go to the OnStart event subroutine. Type in the following code:
With SerialPort1
.StopBits = 1
.Parity = IO.Ports.Parity.None
.BaudRate = 4800
.RtsEnable = True
.DtrEnable = True
SerialPortNames = .GetPortNames()
End With
'prepare to locate a gps receiver
Timer1.Interval = 4000
Timer2.Interval = 300
Timer2.Enabled = False
Timer1.Enabled = True
Go to the code window
for the
Timer1_Elapsed
event subroutine
.
Type in the following code:
Dim MyLog As New EventLog() ' create a new event log
' Check if the the Event Log Exists
If Not MyLog.SourceExists("GPSLocationService") Then
MyLog.CreateEventSource("GPSLocationService", "GPS Log") ' Create Log
End If
MyLog.Source = "GPSLocationService"
' Write to the Log
If StartUp = True Then
StartUp = False
For I As Integer = 0 To SerialPortNames.Length - 1
' Write to the Log
MyLog.WriteEntry("GPS Log" & " " & _
" Serial port found at " & SerialPortNames(I) & vbCrLf, EventLogEntryType.Information)
Next
End If
If GPSFound = False Then
If PortIndex = 0 Then
MyLog.Source = "GPSLocationService"
' Write to the Log
MyLog.WriteEntry("GPS Log" & " " & _
CStr(TimeOfDay) & " GPS Loc Started (No GPS identified) ", EventLogEntryType.Information)
End If
If PortIndex < SerialPortNames.Length Then
With SerialPort1
Timer2.Enabled = False
If .IsOpen = True Then
.Close()
End If
.PortName = SerialPortNames(PortIndex)
PortIndex += 1
Try
.Open()
Catch ex As Exception
End Try
If .IsOpen = True Then Timer2.Enabled = True
End With
Else
MyLog.Source = "GPSLocationService"
' Write to the Log
MyLog.WriteEntry("GPS Log" & " " & _
CStr(TimeOfDay) & " A GPS Receiver was not detected! ", EventLogEntryType.Information)
Timer1.Enabled = False
End If
Else
MyLog.Source = "GPSLocationService"
' Write to the Log
With GPSDecoder
If .status = "A" Then
MyLog.WriteEntry("GPS Log" & " " & _
.DecimalLatitude.ToString & ", " & .DecimalLongitude.ToString & ", " _
& .LocalDate & ", " & .LocalTime, EventLogEntryType.Information)
Else
MyLog.WriteEntry("GPS Log" & " " & _
CStr(TimeOfDay) & " GPS Coordinates not valid! ", EventLogEntryType.Information)
End If
End With
End If
Add the following code to the Timer2_Elapsed event:
Dim Buffer As String
With SerialPort1
If .IsOpen = True AndAlso .BytesToRead > 0 Then
Buffer = .ReadExisting
Try
SyncLock (Me)
GPSDecoder.GPSStream(Buffer)
End SyncLock
Catch ex As Exception
End Try
End If
End With
Add this code to the GPSDecoder_GPSDecoded event subroutine:
GPSFound = True
GPSPort = SerialPort1.PortName
Type in the following
code in the
OnStop
event
subroutine:
Timer1.Enabled = False
Timer2.Enabled = False
If SerialPort1.IsOpen Then SerialPort1.Close()
Our application is now ready, but there are a few things that we need to do before we build it. This EXE is not a Windows application, and hence you can't just click and run it -- it needs to be installed as a service. Visual Studio has a facility where we can add an installer to our program and then use a utility program or Setup program to install the service.
What happens if the GPS receiver is disconnected, a common occurrence with USB devices, after the service has detected the port and begun running? There is no code to handle this problem. Many, perhaps most, Windows services have a common theme, and this example is no exception. Often, at regular intervals, they monitor ongoing operations and then do something useful. So, frequently you see some sort of timing mechanism (the Sytem.Timers.Timer control) to perform this regular monitor. We do that now, to receive the serial NMEA-0183 data stream. However, if data stops (for whatever reason), we do not detect that fact. What might be a useful way to handle this here? I’ll outline some steps:
· Set a Boolean class-level flag to False at the top of the GPS_Decoded event. This flag indicates that, for this cycle, GPS data have been decoded.
· Test this same flag in the Timer1_Elapsed event, inside the GPSFound = True block. If GPSFound = True, and our newly added flag is False, then the process is running correctly, and the GPS receive still is connected. We then set this new flag = True, so that the test is ready for the next Timer1_Elapsed test cycle.
· If GPSFound = True, and our newly added flag is True, then the GPS receiver no longer is connected (or otherwise has stopped outputting data). We then can execute code to perform some sort of remedial action, perhaps to log the fact and to start another timer that attempts to reestablish connection to a GPS receiver at regular intervals – or that notifies the system user via the System Tray or some sort of popup window (see Suggestions, below).
· |
· In Solution Explorer, double-click GPSLocationService1.vb. |
|
· Right-click the design surface, choose Properties, and then click Add Installer. |
· |
· In the Properties dialog box for ServiceInstaller1, change the ServiceName and DisplayName properties to GPSLocationService. · In Design view, click ServiceProcessInstaller1. · In the Properties dialog box, change the Account property to LocalSystem. The LocalService value and the NetworkService value are only available in Microsoft Windows XP and later operating systems.
|
Build the Service
Use the Build menu item and select Build GPSLocationService.
Use a compiled Setup project to install the Windows Service
After you complete the steps in the preceding section to configure the Windows Service project, you can do one of two things.
First, you might open a Command Prompt or use the Run window to execute InstlUtil.exe with our service as a command-line argument. InstlUtil.exe is installed with the .NET SDK. On my computer it is located in C:\windows\Microsoft.NET\Framework\v2.0.50727\. At the command prompt type:
C:\windows\Microsoft.NET\Framework\v2.0.50727\InstallUtil.exe c:\SVCTest\GPSLocationService\bin\Release\GPSLocationService.EXE (all one line)
When you execute this statement, our service will be installed. I find this first procedure to be best for two reasons. It is easy, but more importantly, it works. The next procedure is what is suggested by Microsoft – and it should be best… However, I simply haven’t been able to get it to work reliably. However, you may want to give it a try.
The second technique is to add a deployment project that packages the service application so that the service application can be installed. To do this, follow these steps:
1. |
Add a new project to your GPSLocationService project.
|
|||||||||
2. |
Tell the deployment project what the deployment project will package.
|
|||||||||
|
|
|||||||||
3. |
By default, Setup projects are not included in the build configuration. To build the solution, follow these steps:
|
|||||||||
4. |
To install the service, right-click GPSServiceSetup, and then click Install. You will see dialog:
|
|||||||||
|
|
|||||||||
5. |
In the GPSServiceSetup dialog box, click Next three times. Notice that a progress bar appears while the Setup program is installing the service. |
|||||||||
6. |
When the service is installed, click Close. |
Running a service and starting a service are two different things -- when you install the service you are running the service, but have yet have to start it.
To view and start the service, open Control Panel / Administrative Tools. Now click Services, locate GPSLocationService, right click on it and select Start to start it:
Our service is now started. Open the Event Viewer from Administrative Tools and click Application Log to see the logs created by the Service (GPSLocationService) every 4 seconds. If you don't see any logs click refresh (F5). You will have to keep refreshing to see the latest event logs:
Double-click the event to view the detail window:
Stopping the Service
This procedure is similar to installing the service but now we shall run the InstallUtil with the /U parameter which will uninstall the service:
C:\windows\Microsoft.NET\Framework\v2.0.50727\InstallUtil.exe /u c:\SVCTest\GPSLocationService\bin\Release\GPSLocationService.EXE (all one line)
(Specify the executable path on your computer.)
Take note of the message to confirm that the service was uninstalled properly and verify this using the Services window under Control Panel/Administrative Tools.
If you used the GPSServiceSetup to create a Setup MSI, then you may use that Setup to uninstall the service.
Suggestions