Still celebrating National IT Professionals Day with 3 months of free Premium Membership. Use Code ITDAY17

x
?
Solved

Re: Problem with Windows Service program (still!)

Posted on 2006-11-21
16
Medium Priority
?
343 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 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
The top UI technologies you need to be aware of

An important part of the job as a front-end developer is to stay up to date and in contact with new tools, trends and workflows. That’s why you cannot miss this upcoming webinar to explore the latest trends in UI technologies!

 

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
 
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
 
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

New benefit for Premium Members - Upgrade now!

Ready to get started with anonymous questions today? It's easy! Learn more.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

In this post we will learn how to make Android Gesture Tutorial and give different functionality whenever a user Touch or Scroll android screen.
We live in a world of interfaces like the one in the title picture. VBA also allows to use interfaces which offers a lot of possibilities. This article describes how to use interfaces in VBA and how to work around their bugs.
Viewers will learn how to properly install Eclipse with the necessary JDK, and will take a look at an introductory Java program. Download Eclipse installation zip file: Extract files from zip file: Download and install JDK 8: Open Eclipse and …
Six Sigma Control Plans

715 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