65.9K
CodeProject is changing. Read more.
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.22/5 (16 votes)

Mar 25, 2005

3 min read

viewsIcon

149428

downloadIcon

1275

How to use VB.NET to build a Windows Service that monitor folders looking for files in ZIP format (and unzip them).

Sample Image - screenshot3.jpg

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

Before

After

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.