Solved

Re: Problem with Windows Service program (still!)

Posted on 2006-11-21
16
334 Views
Last Modified: 2011-10-03
I spoke too soon! I have been developing and testing the timer with a Console App and this works perfectly with the same "worker" code ( I should explain that the PollDistributionHouses class is in a separate assembly, so the self same code is used for the Windows Service version). However _pdh.Process() is only run once in the Service version.
It looks as if the timer isn't firing om the interval in the Service version.

This is the Console App version :-

static void Main()
{
        AutoResetEvent manualEvent     = new AutoResetEvent(false);
        PollDistributionHouses _pdh = new PollDistributionHouses(autoEvent) ;

// Create the delegate that invokes methods for the timer.
        TimerCallback timerDelegate =
               new TimerCallback(_pdh.Process);
     
// Create a timer that signals the delegate to invoke
// Process after .2 second, and every 15 seconds
// thereafter.

        Timer stateTimer = new Timer(timerDelegate, autoEvent, 200, 15000);
       
         autoEvent.WaitOne() ;
}

/// "Worker class"
public class PollDistributionHouses
{
     private BusinessLogic _BusinessLogic ;
     private int _iCompanyIndex=0 ;
     private int _iNow  ;
     private int _iLastTime = 0 ;
     private int _iMinutes = 0;
     private     AutoResetEvent autoEvent ;

     
     public PollDistributionHouses(AutoResetEvent me)
     {
                     autoEvent = me ;
          _BusinessLogic = new BusinessLogic() ;
          Companies _Companies = new Companies(_BusinessLogic.Data) ;
          foreach (DataRow _CompaniesRow in _BusinessLogic.Data.Companies.DefaultView.Table.Rows)
          {
               try
               {
               }
               catch
               {
               }
               _iCompanyIndex++ ;
          }
     }    


public void Process(object s)
{
     autoEvent.Reset() ;

     _iNow = BusinessLogic.NowMinutes() ;
               
     for (_iCompanyIndex=0; _iCompanyIndex < _BusinessLogic.Data.Companies.DefaultView.Table.Rows.Count ;_iCompanyIndex++ )
     {
          if (_BusinessLogic.Data.Companies[_iCompanyIndex].Uri.Length > 0)
          {
               _iLastTime = int.Parse(_BusinessLogic.Data.Companies[_iCompanyIndex].LastTime) ;
               _iMinutes = int.Parse(_BusinessLogic.Data.Companies[_iCompanyIndex].Minutes) ;
               if (_iNow < _iLastTime) _iLastTime -= (60 * 24) ;
               if (_iNow - _iLastTime >= _iMinutes)
               {
               this.Go(_BusinessLogic, _iCompanyIndex) ;                                   _iLastTime = _iNow ;
               _BusinessLogic.Data.Companies[_iCompanyIndex].LastTime = _iLastTime.ToString() ;
               }
          }
     }
}

and this is the Windows Service Version :-

using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.ServiceProcess;
using DataAccess;
using ApplicationLogic;
using System.Configuration;
using System.Threading;


namespace MDSWindowsService
{
     public class MDSWindowsService : System.ServiceProcess.ServiceBase
     {
          /// <summary>
          /// Required designer variable.
          /// </summary>
          private System.ComponentModel.Container components = null;
          private AutoResetEvent autoEvent ;
          private PollDistributionHouses _pdh ;

          public MDSWindowsService()
          {
               // This call is required by the Windows.Forms Component Designer.
               InitializeComponent();

               // TODO: Add any initialization after the InitComponent call
          }

          // The main entry point for the process
          static void Main()
          {
               System.ServiceProcess.ServiceBase[] ServicesToRun;
     
               // 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 Service1(), new MySecondUserService()};
               //
               ServicesToRun = new System.ServiceProcess.ServiceBase[] { new MDSWindowsService() };

               System.ServiceProcess.ServiceBase.Run(ServicesToRun);
          }

          /// <summary>
          /// Required method for Designer support - do not modify
          /// the contents of this method with the code editor.
          /// </summary>
          private void InitializeComponent()
          {
               components = new System.ComponentModel.Container();
               this.ServiceName = "MDSWindowsService";
          }

          /// <summary>
          /// Clean up any resources being used.
          /// </summary>
          protected override void Dispose( bool disposing )
          {
               if( disposing )
               {
                    if (components != null)
                    {
                         components.Dispose();
                    }
               }
               base.Dispose( disposing );
          }

          /// <summary>
          /// Set things in motion so your service can do its work.
          /// </summary>
          protected override void OnStart(string[] args)
          {
               // TODO: Add code here to start your service.

               autoEvent = new AutoResetEvent(false);
               _pdh = new PollDistributionHouses(autoEvent) ;

               // Create the delegate that invokes methods for the timer.
               TimerCallback timerDelegate =
                    new TimerCallback(_pdh.Process);
     
               // Create a timer that signals the delegate to invoke
               // Process after one second, and every 1000 seconds
               // thereafter.

               Timer stateTimer = new Timer(timerDelegate, autoEvent, 200, 15000);

          }
 
          /// <summary>
          /// Stop this service.
          /// </summary>
          protected override void OnStop()
          {
               // TODO: Add code here to perform any tear-down necessary to stop your service.
               autoEvent.WaitOne() ;
          }
     }
}

I know that I have used AutoResetEvent instead of ManualResetEvent (copied from MSDN), but was happy with it 'cos it worked in the Console App version.

I must be missing something simple ???
0
Comment
Question by:jonskin
  • 6
  • 3
  • 3
  • +3
16 Comments
 
LVL 4

Expert Comment

by:meenasree
ID: 17994429
In the method process autoEvent.set() should be called this will solve the problem
0
 

Author Comment

by:jonskin
ID: 17995176
Thanks for the reply but, unfortunately, that made no difference to the Windows Service operation : it still fired just the once. Furthermore, in the Console App version the change actually kills the timer, so that it only fires once then exits. When I then removed autoEvent.Set() from Process the Console App. went back to correct execution as before.

Actually, I am a bit puzzled by your suggested solution since the examples in MSDN show that autoEvent.Set() is actually used to kill the timer :-

http://msdn2.microsoft.com/en-us/library/system.threading.timer(VS.80).aspx

0
 
LVL 1

Expert Comment

by:LeoVermaak
ID: 17995198
Hi

You are quite correct:

timer.Enabled = true;

I would create the timer in the server contructor:

timer = new Timer();
timer.Interval = Convert.ToDouble(ConfigurationSettings.AppSettings["TimerInterval"].ToString());
timer.Elapsed += new ElapsedEventHandler(OnTimer);
DebugLvl = ConfigurationSettings.AppSettings["DebugLevel"].ToString().ToCharArray(0,6);


and OnStart:

timer.Enabled = true;

I would also to some logging:
EventLog.WriteEntry("Service started:" + this.ReturnLoggingValue(6).ToString());

Hope this help
0
 

Author Comment

by:jonskin
ID: 17996446
"Hope this help"

Sadly ... not a lot, except that you confirm that autoEvent.Set() is not the answer.

Which method do you mean by "server constructor" ?  Also, you appear to be suggesting
a different sort of timer (is this one in Threading?) -  where is the TimerCallback delegate?

I will have a try at some event logging, though.  That might make facilitate debugging.




0
 
LVL 1

Expert Comment

by:LeoVermaak
ID: 17997249
Hi again

I was bite in a rush when I posted my previous reply.

The first thing I would do is change your timer and use the "using System.Timers;".  The reason is that it is a server based timer with a smaller footprint.

You can also manipulate the timer externally if you declare the timer protected or public object of the service class and you pass the "components" variable to your working class.

I always set the oTimer.enabled = false; when the timer event fires and then do the required processing.

The server constructer - I mean service constructer:
public MDSWindowsService()
          {
               // This call is required by the Windows.Forms Component Designer.
               InitializeComponent();

               // TODO: Add any initialization after the InitComponent call
          }

With regards to eventLogging - I can not live without event logging :)
I use a "layer base" event logging methodology e.g.
DebugLevel = "xxx" - DebugLevel = "999"
If the service object is my top object - the value of the first x will determine what logging I do on that object.
The service object call the worker object  - the value of the 2nd x will determine what logging I do on that object.
The worker object call Business objects - the value of the 3rd x will determine what logging I do on that object.

You can create you own eventLogger which you keep on calling in your assemble and specify the value you assign to the method/exec/exception/property call.

Hope this help
0
 

Author Comment

by:jonskin
ID: 17998394
It may well be that the System.Timer is your preference and I will certainly take a look at how to use it. However, I have spent quite a bit of time following the original advice I received (Q_22061348) and would really like to know why that method is not working. I closed Q_22061348 because I thought it was working ( it did with the Console App.) and had to start the question anew here.
I will also look into your advice on event logging - thnx.
0
 
LVL 1

Expert Comment

by:LeoVermaak
ID: 17999380
The problem you have with the console application working and not the service is the service is multi threaded and the execution you assigning when timeout event occurs is 'out side' the service instance.

The console application is a single threaded application by definition and application execution and thus instance reference thread is 'hults' and kept while waiting for the timeout object 'process' the timeout.

The expert can write you an essay on this matter but that is basically the short and sweet of it.

Good luck
0
Threat Intelligence Starter Resources

Integrating threat intelligence can be challenging, and not all companies are ready. These resources can help you build awareness and prepare for defense.

 
LVL 23

Expert Comment

by:Christopher Kile
ID: 18022856
Your timer is, in fact, constructed in the correct location (the OnStart handler for the service).  There is no need to pass autoEvent as the state object to the timer, but then the way it's referenced in the delegate seems to me to be non-harmful.  I'm a bit time-bound today, but early tomorrow I'll try to create this service myself and see what the cause of the problem might be.

BTW, I do know that System.Threading.Timer sometimes craps out with certain OS versions after 1 to 100 iterations.  Go to support.microsoft.com and search for System.Threading.Timer and see if your OS might be affected (check your service pack level, it's important); if you might be impacted, a new service pack might fix you.
0
 
LVL 22

Expert Comment

by:cookre
ID: 18023553
Windows timer - based on message pump.  Not suitable for services - even if typed Own/Interactive

Thread timer - based on callback.  As a managed object, if you don't keep a reference to it, it goes away.

System timer - based on OS raised event.
0
 

Author Comment

by:jonskin
ID: 18025920
Thanks, cookre, would you like to expand on that comment?
0
 
LVL 23

Expert Comment

by:Christopher Kile
ID: 18030344
I've been unable to duplicate your bug.  My version (using a test object) merrily runs along.

Try explicitly defining Timer as System.Threading.Timer in both the declaration and the New invocation.

Also, try attaching to the service process using the Visual Studio debugger, set a breakpoint on Process(), and trace through it on the first invocation.  Set your start delay (200 in your example) to 15000 to give you 15 seconds to attach to the process and set the breakpoint (set it larger if you are new to trying this).  They way it works:

Load your service project into VS.NET
Start your service
go to the Debug menu and select Processes
find your service in the list of Processes
click the Attach button
Make sure ONLY Common Language Runtime is checked, then hit OK
Hit close
Set your breakpoint on the source line that begins the Process() routine (explicitly include the source in your project, if necessary)
Wait until the breakpoint is hit, then step through.

I believe this will give you some clue as to what is happening.  Also, PLEASE invoke autoEvent.Set() at the end of Process().

BTW, did you check what version/service pack of your OS that you're using?

NOTES:  System.Timer is not recommended by Microsoft for use in Windows Services - System.Threading.Timer is.
0
 
LVL 23

Expert Comment

by:Christopher Kile
ID: 18037956
I stand corrected.  This source certainly has more experience than I do at this sort of thing, and I'd tend to follow his recommendations.  The code you have SHOULD work, but try this link if you've tried everything else and you're still not getting the functionality you require.

http://www.codeguru.com/csharp/.net/cpp_managed/windowsservices/article.php/c6919__1
0
 

Author Comment

by:jonskin
ID: 18067785
0
 
LVL 1

Accepted Solution

by:
Computer101 earned 0 total points
ID: 18320646
PAQed with points refunded (100)

Computer101
EE Admin
0
 

Author Comment

by:jonskin
ID: 18322430
This is what worked - eventually! :-


namespace MDSWindowsService
{
      public class MDSWindowsService : System.ServiceProcess.ServiceBase
      {
            /// <summary>
            /// Required designer variable.
            /// </summary>
            protected Timer timer;
            private System.ComponentModel.Container components = null;
            private PollDistributionHouses _pdh;

            public MDSWindowsService()
            {
                  // This call is required by the Windows.Forms Component Designer.
                  InitializeComponent();

                  // TODO: Add any initialization after the InitComponent call
                  timer = new Timer();
                  timer.Interval = double.Parse(ConfigurationSettings.AppSettings["TimerInterval"].Trim());
                  timer.Elapsed += new ElapsedEventHandler(OnTimer);
            }

            // The main entry point for the process
            static void Main()
            {
                  System.ServiceProcess.ServiceBase[] ServicesToRun;
     
                  // 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 Service1(), new MySecondUserService()};
                  //
                  ServicesToRun = new System.ServiceProcess.ServiceBase[] { new MDSWindowsService() };

                  System.ServiceProcess.ServiceBase.Run(ServicesToRun);
            }

            /// <summary>
            /// Required method for Designer support - do not modify
            /// the contents of this method with the code editor.
            /// </summary>
            private void InitializeComponent()
            {
                  components = new System.ComponentModel.Container();
                  this.ServiceName = "MDSWindowsService";
            }

            protected void OnTimer(Object source, ElapsedEventArgs e)
            {
                  string usrDir = null;      //Directory where files will be
                  
                  TimeSpan diffTSpan = new TimeSpan(1,0,0,0);
                  
                  timer.Stop();
                  string strResult = _pdh.Process() ;
                  if (strResult.Length > 0)
                  {
                        strResult = "MDSWindowsService Failed on DHID "+strResult ;
                        BusinessLogic.NewEventLog (strResult, 27000) ;
                  }
                  timer.Start();
            }


            /// <summary>
            /// Clean up any resources being used.
            /// </summary>
            protected override void Dispose( bool disposing )
            {
                  if( disposing )
                  {
                        if (components != null)
                        {
                              components.Dispose();
                        }
                  }
                  base.Dispose( disposing );
            }

            /// <summary>
            /// Set things in motion so your service can do its work.
            /// </summary>
            protected override void OnStart(string[] args)
            {
                  // TODO: Add code here to start your service.

                  _pdh = new PollDistributionHouses() ;
                  OnContinue();
                  BusinessLogic.NewEventLog ("MDSWindowsService Started", 22001) ;
                  //EventLog.WriteEntry("MDSWindowsService Started");
            }
 
            /// <summary>
            /// Stop this service.
            /// </summary>
            protected override void OnStop()
            {
                  // TODO: Add code here to perform any tear-down necessary to stop your service.
                  OnPause();
                  _pdh = null ;
                  BusinessLogic.NewEventLog ("MDSWindowsService Started", 22002) ;
                  //EventLog.WriteEntry("MDSWindowsService Stopped");
            }


            protected override void OnPause()
            {
                  timer.Enabled = false;
            }

            protected override void OnContinue()
            {
                  timer.Enabled = true;
            }


      }
}
0

Featured Post

What Should I Do With This Threat Intelligence?

Are you wondering if you actually need threat intelligence? The answer is yes. We explain the basics for creating useful threat intelligence.

Join & Write a Comment

Does the idea of dealing with bits scare or confuse you? Does it seem like a waste of time in an age where we all have terabytes of storage? If so, you're missing out on one of the core tools in every professional programmer's toolbox. Learn how to …
A short article about a problem I had getting the GPS LocationListener working.
An introduction to basic programming syntax in Java by creating a simple program. Viewers can follow the tutorial as they create their first class in Java. Definitions and explanations about each element are given to help prepare viewers for future …
In this fourth video of the Xpdf series, we discuss and demonstrate the PDFinfo utility, which retrieves the contents of a PDF's Info Dictionary, as well as some other information, including the page count. We show how to isolate the page count in a…

707 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

15 Experts available now in Live!

Get 1:1 Help Now