3

I've written a Windows Service that periodically executes a SSIS package that moves documents from Server A to Server B.

The problem is that in order to do so, I need to use an infinite loop, which starts when the Service starts.

Naturally, I placed this loop into the OnStart() method. Unfortunately, the service never signals that it has started, since it never reaches the end of this method...

Here is the relevant code:

protected override void OnStart(string[] args)
{
    Application app = new Application();
    Package pkg = app.LoadFromDtsServer(@"MSDB\PullDoc", "Server", null);
    while (true)
    {
        DTSExecResult pkgResults = pkg.Execute();//Execute the package.
        EventLog.WriteEntry(pkgResults.ToString());
        Thread.Sleep(1000 * 60 * 5);//Sleep at least five minutes.
    }
}

I would imagine this is a common problem, given that most Services should be running indefinitely.

Any ideas on how to get this service to return that it has started?

Thanks!

nosirrahcd
  • 1,467
  • 3
  • 18
  • 36

4 Answers4

4

You should use a System.Threading.Timer instead of an infinite loop.

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • Related question: when I use the timer, it will call my callback method every 5 minutes, even if the method hasn't finished executing yet. Is there a simple way to make it not count until the end of the method? – nosirrahcd Jan 05 '11 at 20:23
  • @user: Set the timer to only run once, then start it again at the end of the method. – SLaks Jan 05 '11 at 20:24
3

Your service should do its work on a different thread. The OnStart, OnStop etc methods are there to process commands to your service from the Windows Service Control Manager (SCM), and the SCM expects them to return promptly.

Using a System.Threading.Timer as suggested by @SLaks achieves this: the timer events will be executed on a thread from the .NET Thread Pool. Your OnStart method just Enables the Timer, while the OnStop method disables it (OnPause and OnResume can do likewise if you want).

Chris Dickson
  • 11,964
  • 1
  • 39
  • 60
2

You are not doing this correctly, you should never block a function from returning and you should use a new Thread. As it was suggested, you should use a Timer object. Here is a code snippet to show you how:

    private void OnElapsedTime(object source, ElapsedEventArgs e)
    {
         CopyAToB();
    }
    Timer timer = new Timer();
    protected override void OnStart(string[] args)
    {
        timer.Elapsed += new ElapsedEventHandler(OnElapsedTime);
        timer.Interval = 60000 * 5;
        timer.Enabled = true;
    }
    private void CopyAToB()
    {
        // do somethings
    }
Malachi
  • 3,205
  • 4
  • 29
  • 46
Durden81
  • 966
  • 9
  • 25
  • In a windows service you will want to use a System.Timers.Timer or System.Threading.Timer. See this post for a more detailed explanation http://stackoverflow.com/questions/246697/windows-service-and-timer – Mike Z Jan 06 '11 at 15:26
1

I would recommend that you use a System.Threading.Timer like suggested but here is an example of how I would implement the functionality.

In this example I have the function fire 4 times an hour and it will quickly validate if its still running from the previous call and if so skip it otherwise it will create a new thread and fire off the function.

Imports System.Threading

Public Class myService

  Private myThreadingTimer As System.Threading.Timer
  Private keepRunning As Boolean = False
  Private processing As Boolean = False

  Protected Overrides Sub OnStart(ByVal args() As String)
    Dim myTimerCallback As New TimerCallback(AddressOf OnTimedEvent)

    If YourCheckHere() Then
      keepRunning = True
      myThreadingTimer = New System.Threading.Timer(myTimerCallback, Nothing, 1000, 1000)
    Else
      'What you want to do here
    End If
  End Sub

  Protected Overrides Sub OnStop()
    keepRunning = False
  End Sub

  Private Sub OnTimedEvent(ByVal state As Object)
    If Date.Now.Minute = 14 And Date.Now.Second = 31 Or Date.Now.Minute = 29 And Date.Now.Second = 31 _
    Or Date.Now.Minute = 44 And Date.Now.Second = 31 Or Date.Now.Minute = 59 And Date.Now.Second = 31 _
    Then
      'Make Sure Its Supposed To Still Be Running
      If keepRunning Then
        'Make Sure The Process Is Not Already Running
        If Not processing Then
          'Process is not currently running lets start it
          Dim myThread As New Thread(New ThreadStart(AddressOf myProcess))
          myThread.Start()
       End If
      End If
    End If
  End Sub

  Public Sub myProcess()
    Try
      ' Set the processing flag so the function does not run again until complete
      processing = True

      'Do whatever logic you need here

    Catch ex As Exception
      'Since You Can Not Use A MessageBox Do Logging Or Whatever You Need Here

    Finally
      processing = False
    End Try
  End Sub

End Class
Mike Z
  • 96
  • 4