Service doesn't like my use of Zip.dll

Feb 28, 2009 at 6:08 PM
Hi folks!

I have a service which is developed in VB2008Pro which, of course, is .NET 3.5 based.  I have added both the Ionic.Zip and Ionic.Zlib libraries to my project references.  I have also added Ionic, Ionic.zip and Ionic.Zlib to the Imported Namespaces of the project.  As a safety measure, I also added "Imports Ionic.Zip.ZipFile" in the service code. 

This is the code of the service:

Imports

Ionic.Zip.ZipFile

 

Public

Class TestService

 

 

    Protected Overrides Sub OnStart(ByVal args() As String)

 

        Timer1.Enabled =

True

 

 

    End Sub

 

 

    Protected Overrides Sub OnStop()

 

        Timer1.Enabled =

False

 

 

    End Sub

 

 

    Private Sub Timer1_Elapsed(ByVal sender As System.Object, ByVal e As System.Timers.ElapsedEventArgs) Handles Timer1.Elapsed

 

 

        Call WriteToLog("Timer1 event triggered")

 

 

        Call ZipMyFolder()

 

 

    End Sub

 

 

    Private Sub ZipMyFolder()

 

 

        Call WriteToLog("In the ZipMyFolder routine")

 

 

        Try

 

 

            Using zip As ZipFile = New ZipFile

 

                zip.AddDirectory(

"c:\MyFolder", "")

 

                zip.Save(

"C:\MyZippedFolder.zip")

 

 

            End Using

 

 

        Catch ex1 As Exception

 

 

            Call WriteToLog(ex1.ToString())

 

 

        End Try

 

 

    End Sub

 

 

    Private Sub WriteToLog(ByVal strEntry As String)

 

 

        Dim fso As Scripting.FileSystemObject

 

 

        Dim ts As Scripting.TextStream

 

 

        Dim LogFile As Scripting.File

 

        fso =

New Scripting.FileSystemObject

 

 

        If Not fso.FileExists("C:\ZipLog.log") Then fso.CreateTextFile("C:\ZipLog.log")

 

        LogFile = fso.GetFile(

"C:\ZipLog.log")

 

        ts = LogFile.OpenAsTextStream(Scripting.IOMode.ForAppending)

        ts.Write(strEntry & vbCrLf)

        ts.Close()

        ts =

Nothing

 

        LogFile =

Nothing

 

        fso =

Nothing

 

 

    End Sub

 

End

Class

 


In the log, the only entry that ever shows up is "Timer event triggered".  In other words, as soon as it tries to enter the ZipMyFolder routine, it skips it for some reason that I don't understand.  However, if I comment out all lines of code from Try to End Try in the ZipMyFolder routine, then the log starts to show entries of "In the ZipMyFolder routine". 

I've tried everything that I can think of but the service simply does not seem to like the library.  What did I forget to do? 

Thanks!
Feb 28, 2009 at 6:15 PM

Let's try this once again.

Imports
Ionic.Zip.ZipFile

 

Public Class TestService

 

    Protected Overrides Sub OnStart(ByVal args() As String)

        Timer1.Enabled = True

    End Sub

 

    Protected Overrides Sub OnStop()

        Timer1.Enabled = False

    End Sub

 

    Private Sub Timer1_Elapsed(ByVal sender As System.Object, ByVal e As System.Timers.ElapsedEventArgs) Handles Timer1.Elapsed

        Call WriteToLog("Timer1 event triggered")

        Call ZipMyFolder()

    End Sub

 

    Private Sub ZipMyFolder()

        Call WriteToLog("In the ZipMyFolder routine")

 

        Try

            Using zip As ZipFile = New ZipFile

                zip.AddDirectory("c:\MyFolder", "")

                zip.Save("C:\MyZippedFolder.zip")

            End Using

        Catch ex1 As Exception

            Call WriteToLog(ex1.ToString())

        End Try

    End Sub

 

    Private Sub WriteToLog(ByVal strEntry As String)

        Dim fso As Scripting.FileSystemObject

        Dim ts As Scripting.TextStream

        Dim LogFile As Scripting.File

 

        fso = New Scripting.FileSystemObject

 

        If Not fso.FileExists("C:\ZipLog.log") Then _

            fso.CreateTextFile("C:\ZipLog.log")

 

        LogFile = fso.GetFile("C:\ZipLog.log")

        ts = LogFile.OpenAsTextStream(Scripting.IOMode.ForAppending)

        ts.Write(strEntry & vbCrLf)

        ts.Close()

        ts = Nothing

        LogFile = Nothing

        fso = Nothing

    End Sub

End Class

 

Coordinator
Mar 1, 2009 at 3:23 PM
Edited Mar 1, 2009 at 3:29 PM

Hey WindChaser, what do you mean by "Service" ?

Is it a Windows Service - that is administered through   services.msc  ?

ps: You do not need to "Imports Ionic.Zip.ZipFile" as a safety measure.  I have been where you are - trying *anything* to get an app to work - but importing the Ionic.Zip.ZipFile namespace is not going to help you!  There is no such namespace.

Mar 1, 2009 at 4:14 PM

 

I'm using the VB2008 methodology to create a standard service which is user-independent, that is:

- Create a new project in VB2008 of the Windows Service type
- Add logic at module level, no forms or interfaces allowed
- Add a service installer to the project
- Build the project.  By the way, everything compiles properly.
- Install the service using InstallUtil.exe

The service runs great and can be viewed, started and stopped for the Windows Control Panel\Administrative Tools\Services interface.  The only problem arises when I initiate a ZipFile instance.  The timer event just locks up and doesn't even return an exception.  Meanwhile, the service keeps running but without any further timer events.

Thanks for the code pointer.  I've taken out the "Imports Ionic.Zip.ZipFile".

Coordinator
Mar 1, 2009 at 4:41 PM
A puzzle. 

I just tried this on my machine and everything works just fine.  
The results you are seeing could be a consequence of some of the other settings you are using in the service, like the Logon account for the service, or the filesystem permissions on the logfile or the directory-to-zip, and so on.  That stuff, I don't see in your code, because I guess it is in a separate module from the one you attached.  I'm attaching my code here.


' VBService.vb
' 
' for WindChaser.
' This defines a Windows Service in VB.
' See article http://www.vbdotnetheaven.com/UploadFile/mahesh/Winservvb11172005233242PM/Winservvb.aspx
' for the basic idea.
'
' compile this with: 
' c:\.net3.5\vbc.exe /t:exe /debug+ /R:System.dll /R:Ionic.Zip.dll /out:VBService.exe VBService.vb
'
' install as a service with: 
' c:\.net2.0\installutil.exe VBService.exe
'
'
' =======================================================
' Sun, 01 Mar 2009 08:23
' 



Imports System
Imports Ionic.Zip


Public Class TestService 
    Inherits System.ServiceProcess.ServiceBase

    ' Prepending the service name with a dash means it shows up first in the 
    ' list on the services control panel applet.
    Private Shared MyServiceName as String = "- Wind Chaser Service"
    Private LogFileName as String = "C:\temp\WindChaser.ZipService.log"
    'Private FolderToZip as String = "c:\MyFolder"
    Private FolderToZip as String = "c:\dinoch\dev\dotnet\zip\test\fodder"
    'Private OutputZipFile as String = "C:\MyZippedFolder.zip"
    Private OutputZipFile as String = "c:\dinoch\dev\dotnet\zip\test\WindChaser.zip"
    
    Private WithEvents Timer1 as System.Timers.Timer 

    Private CycleCount as Int32

    Public Sub New()
        InitializeSelf()
    End Sub


    Public Shared Function NameOfService as String
        NameOfService = TestService.MyServiceName
    End Function

    Private Sub InitializeSelf()
        Me.ServiceName = TestService.MyServiceName

        ' The timer period is expressed in milliseconds.
        Me.Timer1 = new System.Timers.Timer(40 * 1000) 
	Me.CycleCount = 0
    End Sub


    ' The main entry point for the process
    Shared Sub Main()
	Dim ServicesToRun As System.ServiceProcess.ServiceBase()
	' More than one user Service may run within the same process. To add
	' another service to this process, change the following line to
	' create a second service object. For example,
	' ServicesToRun = New System.ServiceProcess.ServiceBase() {new WinService1(), new MySecondUserService()}
	ServicesToRun = New System.ServiceProcess.ServiceBase() { New TestService() }
	System.ServiceProcess.ServiceBase.Run(ServicesToRun)
    End Sub



    Protected Overrides Sub OnStart(ByVal args() As String)
        WriteToLog("=======================================================")
        WriteToLog("WindChaser's Directory Zip Service is starting....")
        Timer1.Enabled = True 
    End Sub 
 

    Protected Overrides Sub OnStop()
        Timer1.Enabled = False 
        WriteToLog("WindChaser's Directory Zip Service is stopping....")
        WriteToLog("=======================================================")
    End Sub 
 
    Private Sub Timer1_Elapsed(ByVal sender As System.Object, ByVal e As System.Timers.ElapsedEventArgs) Handles Timer1.Elapsed
        WriteToLog("Timer1 event triggered")
        ZipMyFolder()
    End Sub 
 
    Private Sub ZipMyFolder()
	Me.CycleCount = Me.CycleCount + 1
        WriteToLog(String.Format("Enter ZipMyFolder(cycle={0})", Me.CycleCount))
        Try 
            Using zip As ZipFile = New ZipFile
                Zip.AddFileFromString("ReadMe.txt", "", "This zip file was created at " & System.DateTime.Now.ToString("F"))
                zip.AddDirectory(Me.FolderToZip, "")
                WriteToLog(" Saving...")
                zip.Save(Me.OutputZipFile)
                WriteToLog(String.Format(" Wrote {0} files to the zip archive.", zip.Entries.Count ))
            End Using 
        Catch ex1 As Exception
            WriteToLog("Zip Exception: " & ex1.ToString())
        End Try 
        WriteToLog("Exit ZipMyFolder()")
    End Sub 


 
    Private Sub WriteToLog(ByVal strEntry As String)
        ' use AppendText to write to the log file. 
        ' See http://msdn.microsoft.com/en-us/library/3zc0w663.aspx 
        ' for a discussion.

        ' The System.IO.File.AppendText() method creates the file if it 
        ' does not exist. 
        ' See http://msdn.microsoft.com/en-us/library/system.io.file.appendtext.aspx

        Using w As System.IO.StreamWriter = System.IO.File.AppendText(Me.LogFileName)
            w.WriteLine("{1} {0} : {2}", DateTime.Now.ToLongTimeString(), DateTime.Now.ToLongDateString(), strEntry)
        End Using 
    End Sub 

End Class 

 




<System.ComponentModel.RunInstallerAttribute(True)> _
Public Class MyProjectInstaller
    Inherits System.Configuration.Install.Installer

    Private serviceInstaller As System.ServiceProcess.ServiceInstaller
    Private processInstaller As System.ServiceProcess.ServiceProcessInstaller    
    
    Public Sub New()
        ' Instantiate installers for process and services.
        processInstaller = New System.ServiceProcess.ServiceProcessInstaller()
        serviceInstaller = New System.ServiceProcess.ServiceInstaller()
        
        ' The services will run under the system account.
        processInstaller.Account = System.ServiceProcess.ServiceAccount.LocalSystem
        
        ' The services will be started manually.
        serviceInstaller.StartType = System.ServiceProcess.ServiceStartMode.Manual
        serviceInstaller.Description = "A service that periodically zips up a directory."
        
        ' ServiceName must equal those on ServiceBase derived classes. 
        serviceInstaller.ServiceName = TestService.NameOfService
        
        ' Add installers to collection. Order is not important.
        Installers.Add(serviceInstaller)
        Installers.Add(processInstaller)
    End Sub
End Class

Mar 1, 2009 at 8:19 PM
It still doesn't work.  But the behavior has changed in that the Timer1 event does not lock up.  However, given that you've committed everything to code, it makes it much easier to figure out why all this is occuring.

These are the exact steps that I've taken, without excluding any detail.  If it's not there, that means that I didn't do it and therefore may have forgotten it.
- Created a new project in VB2008 under .NET Framework 3.5.  Selected Windows Service as the template.  Renamed project to TestService.
- In the project properties, 
    - I set the startup object as Sub Main. 
    - In the references, I added System.Configuration.Install from the .Net tab, and browsed over to where I saved the Ionic libraries to include Ionic.Zip.dll.
- In the Solution Explorer, I changed Service1.vb to TestService.vb .
- In the TestService.vb code pane, I overwrote the existing TestService class with your TestService class.  This yields 2 errors regarding multiple definitions of Sub Main and 1 warning that Sub New should call the InitializeComponent method.
- In the TestService.Designer.vb pane:
     - I removed the Shared Sub Main of the template.
     - I added your Public Class MyProjectInstaller.
     - For good measure, I changed Me.ServiceName = "Service1" to Me.ServiceName = "TestService" in the InitializeComponent sub even though we don't actually refer to it.

- Changed the FolderToZip to "C:\Temp\Folder" and the OutputZipFile to "C:\Temp\WindChaser.zip"
- Built TestService.exe, no problem.
- Used InstallUtil.exe from the .Net 2.0 folder to install the service.  The transacted install is completed.
- I start the - Wind Chaser Service and wait a few minutes, then stop the service.  This is the content of the log:

1 mars 2009 16:07:55 : =======================================================
1 mars 2009 16:07:55 : WindChaser's Directory Zip Service is starting....
1 mars 2009 16:08:35 : Timer1 event triggered
1 mars 2009 16:09:15 : Timer1 event triggered
1 mars 2009 16:09:55 : Timer1 event triggered
1 mars 2009 16:10:35 : Timer1 event triggered
1 mars 2009 16:11:00 : WindChaser's Directory Zip Service is stopping....
1 mars 2009 16:11:00 : =======================================================


Note that I am using a fresh install of Visual Studio 2008 Pro and that I am using .Net 3.5, not the .Net 3.5 Service Pack.  The machine is Intel-based on WinXP Pro SP2.

Thanks for your help!







Coordinator
Mar 1, 2009 at 10:31 PM
WC, I will check this out a little later, to see if I can follow your steps myself.
I didn't actually use Visual Studio to write the code I gave to you.  I just wrote it in a text editor, starting from what you gave me.
I'll try in Visual Studio later, and let you know how I fare.
Coordinator
Mar 2, 2009 at 3:07 PM

OK, I investigated a little more.

I followed your steps - tried to do it exactly as you described.

I got a working .exe.  The installutil succeeded.  When I started the service , everything worked as expected.

Monday, March 02, 2009 7:33:45 AM : =======================================================
Monday, March 02, 2009 7:33:45 AM : WindChaser's Directory Zip Service is starting....
Monday, March 02, 2009 7:34:25 AM : Timer1 event triggered
Monday, March 02, 2009 7:34:25 AM : Enter ZipMyFolder(cycle=1)
Monday, March 02, 2009 7:34:25 AM :  Saving...
Monday, March 02, 2009 7:34:26 AM :  Wrote 108 files to the zip archive c:\dinoch\dev\dotnet\zip\test\WindChaser.zip.
Monday, March 02, 2009 7:34:26 AM : Exit ZipMyFolder()
Monday, March 02, 2009 7:35:05 AM : Timer1 event triggered
Monday, March 02, 2009 7:35:05 AM : Enter ZipMyFolder(cycle=2)
Monday, March 02, 2009 7:35:05 AM :  Saving...
Monday, March 02, 2009 7:35:06 AM :  Wrote 108 files to the zip archive c:\dinoch\dev\dotnet\zip\test\WindChaser.zip.
Monday, March 02, 2009 7:35:06 AM : Exit ZipMyFolder()

I tried this on Vista (my own development machine), and on WinXP SP2, and got the same results in both cases. It just works.

I can make some additional suggestions to you.

  1. Double-check that all directories exist, and that the LocalService has permissions on them.
  2. If you are savvy with Visual Studio, you may be able to attach to your TestService.exe and debug it, as it runs. This may give you some insight into why the thing is not behaving the way you expect.
  3. If you cannot do that, try this:  To determine if the problem is related to the DotNetZip library, temporarily remove all the Code between the Try...End Try in the ZipMyFolder() method. This reduces the method to a couple calls to WriteToLog(). See if log messages are generated when you run the resulting service. In the code I provided, if the calls into the DotNetZip library succeed, you should see a log message indicating that. If the code fails, in other words if there is an exception in the DotNetZip code, you should see a log message to that effect. One way or the other you should see a log message.  We saw nothing in the log from the ZipMyFolder() method. Nothing at all. Strange.
Mar 2, 2009 at 5:56 PM

In the timer event, I delete and recreate the folder, then copy the log file into into it to prove that the service has access to the folder and file.  As you suggested, I took
out all lines of code pertaining to the library.  Everything works fine and the log shows all activity.  I then put just

Using zip As ZipFile = New ZipFile
      WriteToLog("ZipFile instantiated.")
End Using


and then all I get in the log is "Timer1 event triggered".

I've copied the entire project onto HTTP://ExcelDent.ca/TestService.zip so that you may download it. 
If it works on your machine, then the issue is outside the project.









 

 


 

 

Coordinator
Mar 2, 2009 at 10:57 PM
WC, I downloaded your code, ran it here - everything worked fine.
Not sure what is going on, something mysterious, that's for sure.
It's really odd that you don't see exceptions either.   What if you induced an artificial exception in the ZipMyFolder() method?  For example, just introduce a "Throw New System.Exception("Woot!") into that method.
If you surround that Throw statement with a Try...Catch...End Try.... And then in the Catch clause, you write to the log file... do you see log writes? 

I'm thinking that maybe there is an exception being generated by the Zip library somehow, but the mystery to me is why you do not see the exception, why you cannot catch it.  
Mar 3, 2009 at 1:04 AM
I've tried it all.  The problem occurs at instantiation, so I can't catch it like that.  But the issue is obviously at machine-level.  I would have to try it out on another machine, which I don't have available at the moment.  As soon as I figure something out, I'll let the forum know for everyone's benefit.  Thanks!
Coordinator
Mar 3, 2009 at 4:30 AM
What a puzzle.
Does an app using DotNetZip work as a regular standalone application?  (outside the Service)

Have you tried GAC'ing the library?  (Gacutil -i Ionic.Zip.dll)  I think you need to be administrator to GAC the library.
Mar 3, 2009 at 7:26 AM
@WindChaser,
You can try to add the following statement to break into the debugger while your service is running.

System.Diagnostics.Debugger.Break();