Solved

What is the perfect way to run a service application?

Posted on 2008-09-29
18
436 Views
Last Modified: 2012-05-05
Hi,

I'm about to wirte a service application with a timer. So I look aa EE and found examples, but wich one is the proper way.

Br
The 1st one:

procedure TCollectMail.TimerOnTimer(Sender: TObject);

var

  FF: Text;

begin

//  winExec('d:\mail\xa.cmd',SW_HIDE);

 

  AssignFile(FF, 'c:\CollectMail.log');

  if FileExists(log) then begin

    Append(FF);

  end else begin

    Rewrite(FF);

  end;

  Writeln(FF, FormatDateTime('dd.mm.yyyy hh:nn', Now));

  CloseFile(FF);

end;

 

 

procedure TCollectMail.ServiceExecute(Sender: TService);

begin

//  winExec('d:\mail\xa.cmd',SW_SHOWNORMAL);

  Timer.Enabled := True;

  while not Terminated do begin

    ServiceThread.ProcessRequests(False);

  end;

  Timer.Enabled := False;

end;

 

The 2nd one:

procedure TSampleService1.TimerOnTimer(Sender: TObject);

var

  txt : TextFile;

begin

  AssignFile(txt, 'C:\temp\Serv.txt');

  {$I-}

  Append(txt);

  {$I+}

  if IOResult=0 then

  begin

    Writeln(txt, FormatDateTime('yyyy-mm-dd', Date),' ',

                 FormatDateTime('hh:mm:ss', Time), ' .');

    Flush(txt);

    CloseFile(txt);

  end;

  Beep;

end;

 

procedure TSampleService1.ServiceStart(Sender: TService;

  var Started: Boolean);

var

  txt : TextFile;

begin

  Timer:=TTimer.Create(nil);

  Timer.Interval:=10000;

  Timer.OnTimer:=TimerOnTimer;

  AssignFile(txt, 'c:\temp\Serv.txt');

  {$I-}

  Reset(txt);

  {$I+}

  if IOResult <> 0 then

  begin

    Rewrite(txt);

    Writeln(txt, FormatDateTime('yyyy-mm-dd', Date),' ',

                 FormatDateTime('hh:mm:ss', Time), ' Service Started');

    Flush(txt);

  end;

end;

Open in new window

0
Comment
Question by:QC20N
  • 9
  • 7
18 Comments
 

Author Comment

by:QC20N
ID: 22594873
Damn, forgot to tell you that in those 2 exampels it dosen't matter what the OnTimer does. It just the OnExecute and the OnStart I'm interested in.

br.
0
 
LVL 28

Expert Comment

by:ciuly
ID: 22594874
if I were you, I would just drop a timer on the service and use it as I would from a classic windows applicaiton, dropping the timer on the form ;)
with differences:
- in service start: I start the timer
- in service  stop and pause I stop the timer
- and I would use a
reset/rewrite(f);
try
  read/write from/to file
finally
  closefile(f);
end;
0
 

Author Comment

by:QC20N
ID: 22594933
Ok. You are saying that I should do a OnStart procedure instaed of a OnExecute. Why? Why not the OnExecute procedure?

And as I said what the exampels do dosen't matter. I'm only interesting on how start the OnTimer and I can see there is 2 ways the OnExecute or the OnStart.

What should be the proper one?

br
0
 
LVL 28

Expert Comment

by:ciuly
ID: 22594981
there is no point in me explaining this stuff from 0, so here is a pretty good tutorial on windows services with delphi: http://www.tolderlund.eu/delphi/service/service.htm
an piece of the article on your specific problem is found in the attached code section
There are basically two places where you can put your service code:

In the OnExecute method or the OnStart event.
 

OnExecute method:

Put your code in the TService.OnExecute method. Here you can also create a thread with your code if you want.

Quote from the help under OnExecute:

"Occurs when the thread associated with the service starts up."

"If you are not spawning a new thread to handle individual service requests in an OnStart event handler, this is where you implement the service. When the OnExecute event handler finishes, the service thread terminates. Most OnExecute event handlers contain a loop that calls the service threads ProcessRequests method so that other service requests are not locked out."
 

OnStart event:

Create a thread (TThread) that contains your code and start the thread in the TService.OnStart event.

Quote from the help under OnStart:

"OnStartup occurs when the service first starts up, before the OnExecute event."

"This event should be used to initialize the service. For example, if each service request is handled in a separate thread (a good idea if handling the request takes much time) the thread for a request is spawned in an OnStart event handler."
 

Which method you use is a personal matter, both work fine.

Below is an example of both methods.
 
 

Using OnExecute method
 

procedure TCompanySqlDatabaseSpecialSomething.ServiceExecute(

  Sender: TService);

const

  SecBetweenRuns = 10;

var

  Count: Integer;

begin

  Count := 0;

  while not Terminated do

  begin

    Inc(Count);

    if Count >= SecBetweenRuns then

    begin

      Count := 0;
 

      { place your service code here }

      { this is where the action happens }

      SomeProcedureInAnotherUnit;
 

    end;

    Sleep(1000);

    ServiceThread.ProcessRequests(False);

  end;

end;
 

We loop around in the while-do loop until the service should be terminated, either when the machine is shutting down or the service is stopped from the service applet.

In this example the procedure "SomeProcedureInAnotherUnit" is called every 10 seconds.

Note that we do not use Sleep(10000) in order to wait 10 seconds.

If we did that our service would not be able to responds quickly to commands sent from the SCM (Service Control Manager).

Instead we sleep only for 1 second at a time and use a counter to count how many seconds how gone since the last call to SomeProcedureInAnotherUnit.

You can use the OnStart event if you want to perform some initialization here instead of doing it in the OnExecute event and that allows you to set the Started variable to False if you find that some needed settings is missing and don't want the service to start.
 

Using the OnExecute method this way has it's advantages and drawbacks.

Advantage:

The code is simple. You do not need to create a secondary thread.

Pausing and resuming the service is handled automatically without extra code.

Drawbacks:

The SomeProcedureInAnotherUnit must take only a very short time to finish, it should take no more than a few seconds at the most.
 

The OnExecute method works well if the code takes only short time to finish on each run.

If the code takes a long time to run, you should consider starting a secondary thread in the OnStart event instead.
 
 

Using OnStart event
 

First you need to define your secondary thread class where you put all your code to do what ever it is you want your service to do.

Create the thread as you usually make thread classes. One way is to select the menu item File, New, Other, "Thread Object".

If you do not have any experience with threads you need to get a working knowledge about threads before you continue with the service application.

Tutorials on thread programming in Delphi:

Original, now removed:

http://www.pergolesi.demon.co.uk/prog/threads/ToC.html

Can now be found here:

http://www.eonclash.com/Tutorials/Multithreading/MartinHarvey1.1/ToC.html

Open in new window

0
 

Author Comment

by:QC20N
ID: 22595170
Yes, that site I have seen, but the Timer has it's own OnTimer event. So it dosen't matter what kind of procedure I choose because the Ontimer do all the stuff, right?
0
 
LVL 28

Expert Comment

by:ciuly
ID: 22595189
more or less. if you go with the onexecute event, then you must place that loop you posted with while not terminated so that the service doesn't terminate until it si told to.
then in onstart you place timer.enabled=true and in onstop/pause/shutdown you place timer.enabled=false;
OR
you do it in onexecute like this:
timer on
try
  loop here
finally// just in case tehre is an exception while running.
  timer off
end;
0
 

Author Comment

by:QC20N
ID: 22595207
So either way, both are usefull?
0
 
LVL 28

Expert Comment

by:ciuly
ID: 22595316
of course. it all depends how you like it. if I were you, I wouldn't even use a timer, I would use a thread. all these are personal choices based on what needs to be done or how you are used to doing things.
in this particular case of developing services, there is no perfect way. tehre are 2 ways and both work and they can be combined as well, and it will still work fne. you just have to choose which way you like it better :)
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.

 

Author Comment

by:QC20N
ID: 22595353
Well, I need to a timer, cause should check an sql db on a certain time. Every 15 min. I need to check my sql db.
0
 
LVL 28

Expert Comment

by:ciuly
ID: 22595397
you are thinking this wrong. you do not need a timer. you want a timer. it's different.
ask yourself: and what stops you of doing that in a thread? nothing. see below code:

procedure thread.execute;
var i:integerl
begin
  while not terminated do
  begin
    i:=0;
    while (not terminated) and (i<60*15) do// this sleeps for 15 minutes
    begin
      inc(i);
      sleep(1000);// sleep 1 second
    end;
    try
      check DB;
    except
      log error to file or something
    end;
  end;
end;

so you see, it is never the need of something it is jsut the way you are used to doing things. same goes for this particular question: you can do it with onexecute even or without it. it really doens't matter. there is no perfect way.

sure, in your case, since it is a DB thing, I would suggest using a thread especially if the operation on the DB is slow (mnore than a few seconds. it is not advisable that a thread blocks for more than a few seconds.
0
 

Author Comment

by:QC20N
ID: 22595672
But what Thorlund is telling I should not use a OnExecute method if the if the code takes to long. I don't know at this point how long my code it going to be. But you are saying, in my case, you suggest I use a thread. What I understand about a thread, is that you can more processes running at the same time. Is that wise in my case? I thread one is not finished and a second one is running. What will happen if my ADOQuery hasen't closed the access to the DB before the next one need to use the DB?
0
 
LVL 28

Expert Comment

by:ciuly
ID: 22595776
>> But what Thorlund is telling I should not use a OnExecute method if the if the code takes to long

that's what I said too.

and you missunderstood this again: you create only one thread. in the service create event (make sure you create it suspended) and destroy it in the service destroy event (make sure to resume it and call waitfor)
you then start the thread in the onstart, stop it in the onstop and onshutdown and suspend it in the onpause event.
0
 

Author Comment

by:QC20N
ID: 22595924
True.

Maybe I have misunderstod something again, but why not use a komponent (Timer) that do the stuff I want?

And in your exampel, should I not have something like "prossesmessage" or something so the service dosen't use must CPU?
0
 
LVL 28

Accepted Solution

by:
ciuly earned 50 total points
ID: 22596002
because the timer runs in the service thread. it doens't matter that you code executes fro 5 minutes in the timer event or in the oinexecute event: from the trhead point of view, it's the same thread.

if your code executes fast, below 2 seconds, always, then use a timer, otherwise use a thread.  I am getting sick of explaining the same thing over and over again.

and you still don't understand, do you. you have 3 ways:
- one only with onexecute event
- one without onexecute event (but with onstart/stop/etc)
- and one which mixes the above 2 and should probably not be used if you don't understand them
so in this light of the things, why are you asking me if the processmessage is needed? in one way it is needed, in the other it is not. I explained it, that article explained it, what more do you want? should I get ziolko in to say the excat same thing again? would that make it better?

go, write the code and see it work. this is programming, not philosophy.
0
 

Author Closing Comment

by:QC20N
ID: 31501125
Ciuly is not a very good serviceminded person. He may be an expert in his field, but serviceminded he is not.
0
 

Author Comment

by:QC20N
ID: 22599465
I'm not frustrating. I think Ciuly is. Since he's angry, that I'm not fully understand what he is writing.

I will look into the other thing you mention. Al thoug that I do not know what you mean. :) by "Open Questions that you have abandoned."
0

Featured Post

Highfive Gives IT Their Time Back

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

A lot of questions regard threads in Delphi.   One of the more specific questions is how to show progress of the thread.   Updating a progressbar from inside a thread is a mistake. A solution to this would be to send a synchronized message to the…
Introduction The parallel port is a very commonly known port, it was widely used to connect a printer to the PC, if you look at the back of your computer, for those who don't have newer computers, there will be a port with 25 pins and a small print…
In this tutorial you'll learn about bandwidth monitoring with flows and packet sniffing with our network monitoring solution PRTG Network Monitor (https://www.paessler.com/prtg). If you're interested in additional methods for monitoring bandwidt…
This video shows how to remove a single email address from the Outlook 2010 Auto Suggestion memory. NOTE: For Outlook 2016 and 2013 perform the exact same steps. Open a new email: Click the New email button in Outlook. Start typing the address: …

758 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

20 Experts available now in Live!

Get 1:1 Help Now