Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Windows Service: Do work at specified times (Delphi)

Just checking if there's any best practice when writing a Windows Service.

The Service (Single-thread) needs to work at specified time intervals, right now I can only think of:

  1. Use sleep(), then check the time in a loop?
  2. Use a TTimer?

Any advice?

like image 358
Atlas Avatar asked Jul 02 '09 09:07

Atlas


2 Answers

It doesn't really matter that your service is single-threaded, as a service will have its code always called in different thread contexts:

  • The Service Manager will start, stop, pause and resume the service execution, and request the current service state.

  • The service itself will have at least one thread doing the real work, which needs to react on the requests from the service manager, change the service execution state as requested, and return the requested information. A service needs to react to requests from the Service Manager in a reasonably short time, otherwise it will consider the service to be hung and kill it. That's why - if the service may have long-executing or blocking code - it may be better to have more than one service thread.

Whether to use Sleep() or timer messages does also depend on the availability of a message pump in the service threads. If you don't have a message pump you should use Sleep() or timer callbacks. If you have a message pump anyway, because you need to communicate with other processes or threads via Windows messages, or you need to do OLE stuff, then it may be easiest to use timer messages.

A few years ago I wrote a service for timed background execution of tasks, similar to the Windows at or Unix cron functionality. It doesn't use much of the VCL, only some base classes. The Run() method of the service looks like this:

procedure TScheduleService.Run;
var
  RunState: TServiceRunState;
begin
  while TRUE do begin
    RunState := GetServiceRunState;
    if (RunState = srsStopped) or (fEvent = nil) then
      break;
    if RunState = srsRunning then begin
      PeriodicWork;
      Sleep(500);
    end else
      fEvent.WaitFor(3000);
    Lock;
    if fServiceRunStateWanted <> srsNone then begin
      fServiceRunState := fServiceRunStateWanted;
      fServiceRunStateWanted := srsNone;
    end;
    Unlock;
  end;
end;

This uses Sleep() in a loop, but a solution using

while integer(GetMessage(Msg, HWND(0), 0, 0)) > 0 do begin
  TranslateMessage(Msg);
  DispatchMessage(Msg);
end;

would work just as well, and then Windows timer messages could be used.

like image 76
mghie Avatar answered Nov 15 '22 06:11

mghie


Does this need to be a service? Could you maybe setup a scheduled task in Windows?

like image 45
Jamie Avatar answered Nov 15 '22 07:11

Jamie