Using Windows Services to monitor folders and unzip files - VB.NET






4.22/5 (16 votes)
Mar 25, 2005
3 min read

149428

1275
How to use VB.NET to build a Windows Service that monitor folders looking for files in ZIP format (and unzip them).
Introduction
A friend, developing an application in VB 6.0 needed a solution with the following requirements:
- Have to run all the time.
- There is no need to any user to be logged on.
- Must run in Win 2K, XP and may be in 2003 server.
The answer was: Windows Services.
What does the application do?
The application that generated this solution uploads mdb access files zipped into a file to decrease the size of the data base file, 'cause some of his clients use 56k modem connections.
When developing his application in ASP.NET, he needed some way to monitor directory for this special zipped files. As the files arrive, the solution must unzip the files and overwrite the file content in the same directories.
The easiest way to do this, we thought, was with VB 6. We started to look for some place to find information of how doing this in VB 6, but no success. Every article, tutorial and code I found used VB.NET, C# or any other .NET language.
We were convinced that VB.NET had to be the choice for developing the Windows service. Were fascinated with how easy is to build a Windows service with VB.NET. With VB 6, we tried to build a normal executable and run it against a series of tools (as many articles describe) but none of the tools did the job.
With VB.NET we only inherited from the System.ServiceProcess.ServiceBase
class and completed the overloaded methods OnStart
and OnStop
. And the job was done... er... almost done.
In the beginning, I was using the D# IDE which has no facilities to build a Windows service, I had to manually type the definitions for the methods and so. I tried to set the Me.timer1.SynchronizingObject
property to me
(The W.Service
) class and this lead me into a terrible error of crashing the stack. With some try-and-error debug, I discovered this and the Windows service started to run.
The Installer
The installer does nothing but to install the Windows service. We detected that the Installer
class has no way to set up the description of the Windows service. Thanks to the Andy Hopper's Adding a description to a .NET Windows service article, we could set it up with a little code. In the Install
overloaded method we did:
'Open the HKEY_LOCAL_MACHINE\SYSTEM key
rgkSystem = Microsoft.Win32.Registry.LocalMachine.OpenSubKey("System")
'Open CurrentControlSet
rgkCurrentControlSet = rgkSystem.OpenSubKey("CurrentControlSet")
'Go to the services key
rgkServices = rgkCurrentControlSet.OpenSubKey("Services")
'Open the key for your service, and allow writing
rgkService = rgkServices.OpenSubKey(si.ServiceName, True)
'Add your service's description as a REG_SZ value named "Description"
rgkService.SetValue("Description", m_Description)
'(Optional) Add some custom information your service will use...
rgkConfig = rgkService.CreateSubKey("Parameters")
rgkConfig.Close()
rgkService.Close()
rgkCurrentControlSet.Close()
rgkSystem.Close()
The approach
We used a timer object to monitor the directories listed in a .ini file in every three seconds.
<System.Diagnostics.DebuggerStepThrough()> _
Private Sub InitializeComponents()
Me.timer1 = New System.Timers.Timer()
Me.timer1.Interval = 3000
Me.timer1.Enabled = True
Me.ServiceName = "CheckWebDataBase"
End Sub
In the OnStart
and OnStop
, we only had to enable or disable the timer.
Protected Overrides Sub OnStart(ByVal args() As String)
' TODO: Add start code here (if required)
' to start your service.
Me.timer1.Enabled = True
End Sub
Protected Overrides Sub OnStop()
' TODO: Add tear-down code here (if required)
' to stop your service.
Me.timer1.Enabled = False
End Sub
The timer elapsed event is listed below:
Private Sub Timer1Elapsed(ByVal sender As System.Object, _
ByVal e As System.Timers.ElapsedEventArgs) _
Handles timer1.Elapsed
Dim qt As Integer, x As Integer
Dim path As String = "c:\windows"
Dim folder As String, date_ As String, ZIP As String
Dim INIFile As String = path & "\Customer.ini"
qt = CType(CheckWebDataBaseService.Ini.ReadIni(INIFile, _
"General", "Number"), System.Int32)
For x = 1 To qt
folder = CheckWebDataBaseService.Ini.ReadIni(INIFile, "Folders", x)
'Zip File name ZIP = folder & "DataBase.zip"
date_ = CStr(FileDateTime(ZIP))
If CheckWebDataBaseService.Ini.ReadIni(INIFile, "Dates", x) <> date_ Then
Me.ExtractArchive(ZIP, folder)
CheckWebDataBaseService.Ini.WriteIni(INIFile, "Dates", x, date_)
End If
Next
End Sub
We used the ini approach for the usability, 'cause it's easy to add new customers. Note that the name of the zipped file is hard-coded as "DataBase.zip" See the contents for Customer.ini:
[General]
Number=2
[Folders]
1=c:\dev\test\
2=c:\dev\test2\
[Dates]
1=6/1/2005 10:40:19
2=6/1/2005 10:25:49
In Number, we have the numbers of customers. In Folders, we have the customers' folders. And in Dates, we have the last updated date of the file in the folder. To unzip the files, we have used the excellent library SharpZipLib from ICsharpCode (the same for the SharpDevelop or #D).
Public Sub ExtractArchive(ByVal zipFilename As String, ByVal ExtractDir As String)
Dim Redo As Integer = 1
Dim MyZipInputStream As ZipInputStream
Dim MyFileStream As FileStream
MyZipInputStream = New ZipInputStream(New FileStream(zipFilename, _
FileMode.Open, FileAccess.Read))
Dim MyZipEntry As ZipEntry = MyZipInputStream.GetNextEntry
Directory.CreateDirectory(ExtractDir)
While Not MyZipEntry Is Nothing
If (MyZipEntry.IsDirectory) Then
Directory.CreateDirectory(ExtractDir & "\" & MyZipEntry.Name)
Else
If Not Directory.Exists(ExtractDir & "\" & _
Path.GetDirectoryName(MyZipEntry.Name)) Then
Directory.CreateDirectory(ExtractDir & "\" & _
Path.GetDirectoryName(MyZipEntry.Name))
End If
MyFileStream = New FileStream(ExtractDir & "\" & _
MyZipEntry.Name, FileMode.OpenOrCreate, FileAccess.Write)
Dim count As Integer
Dim buffer(4096) As Byte
count = MyZipInputStream.Read(buffer, 0, 4096)
While count > 0
MyFileStream.Write(buffer, 0, count)
count = MyZipInputStream.Read(buffer, 0, 4096)
End While
MyFileStream.Close()
End If
Try
MyZipEntry = MyZipInputStream.GetNextEntry
Catch ex As Exception
MyZipEntry = Nothing
End Try
End While
If Not (MyZipInputStream Is Nothing) Then MyZipInputStream.Close()
If Not (MyFileStream Is Nothing) Then MyFileStream.Close()
End Sub
End Class
Screenshots
Ending
And that's all. We hope you find this code useful.
Future
We plan to substitute ini files for config files. If anyone do it for me, please keep me updated.
Thanks
To Nirondes for the idea to build that Windows service. I am sure we learned too much.