1

I want to make service application in Delphi that run and copy some files everyday at 02:00 PM. So i have used timer. but control not going to timer event and Service terminate within 15 second. I have wrote a code on Timer Event. How can i use timer with service? Please Help. Thanks in Advance.

My Code is Here:

unit untMain;

interface

uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.SvcMgr, Vcl.Dialogs, Vcl.ExtCtrls, DateUtils, Vcl.Forms,
untCommon;

type
TsrvBackupService = class(TService)
tmrCopy: TTimer;
procedure tmrCopyTimer(Sender: TObject);

private
strlstFiles : TStringList;
{ Private declarations }
public
{ Public declarations }
end;

var
srvBackupService: TsrvBackupService;

implementation

{$R *.DFM}

procedure ServiceController(CtrlCode: DWord); stdcall;
begin
srvBackupService.Controller(CtrlCode);
end;


procedure TsrvBackupService.tmrCopyTimer(Sender: TObject);
var
strCurTime   : string;
strBKPpath   : string;
strBKPTime   : string;
NowDay       : word;
NowMonth     : word;
NowYear      : word;
NowHour      : word;
NowMin       : word;
NowSec       : word;
NowMilli     : Word;
begin
  DecodeTime(now,NowHour,NowMin,NowSec,NowMilli);
  strCurTime := IntToStr(NowHour)+':'+IntToStr(NowMin);
  strBKPTime := '14:00'
  strBKPpath := ExtractFilePath(Application.ExeName);
  if strCurTime = strBKPTime then begin
     Try
           CopyFile(PChar('c:\datafile.doc'),PChar(strBKPpath + 'datafile.doc'),true);
     except
        on l_e: exception do begin
           MessageDlg(l_E.Message,mtError,[mbOk],0);
        end;
     end;
  end;
end;

end.
Ganpat
  • 768
  • 3
  • 15
  • 30

3 Answers3

6

Instead of a timer, use a simple thread launched in OnStart event.

A tutorial is here:

http://www.tolderlund.eu/delphi/service/service.htm

TTimer is better suited for GUI applications. They need a message pump (see here):

TTimer requires a running message queue in order to receive the WM_TIMER message that allows the OS to pass the message to the HWND, or to trigger the specified callback

mjn
  • 36,362
  • 28
  • 176
  • 378
  • 3
    Most people do not understand how to use the OnExecute event correctly, so they end up deadlocking the service unexpectedly and/or causing SCM errors about the service being unresponsive, and wonder why. Leave the OnExecute event unassigned and let TService managethe SCM interactions for you. The advice of using the OnStart event to create a worker thread is the best approach. – Remy Lebeau Sep 15 '12 at 18:21
1

As others have explained, you cannot simply use a TTimer component inside a Windows Service Application, as it relies on a message pump which does not come by default in a Service. I see four main options:

  1. Implement a message pump to be able to use the TTimer,
  2. Use a Thread to continuously check for the date/time
  3. Just like #2, use the Service's OnExecute event to check for date/time
  4. Utilize Windows' Scheduled Tasks

I would recommend #2 above, and here's why.

#1 might be a bit much for your scenario, I'm sure you don't want to go that far.

#3 might be easier, but the service's thread needs a little special treatment which I'm also sure you don't need to care about.

#4 might be the ideal solution, but I won't try to change your decision on a service.

Creating a thread is the way to go because it's rather simple and expandable. All my service applications work on a multi-threaded basis, and nothing ever goes inside the actual service's thread, other than handling the actual service.

I was working on a sample for you, but I over-complicated it and it would be a lot of pollution to include it here. I hope at least I got you moving in the right direction.

Community
  • 1
  • 1
Jerry Dodge
  • 26,858
  • 31
  • 155
  • 327
  • Option 1 is really very easy. Option 2, you don't really mean "continuously check" do you? Or are you proposing Sleep and polling? Still a pretty vile option. There are timers in Windows that don't need message pumps. Option 4 is the real answer! – David Heffernan Sep 17 '12 at 12:39
  • @Gani Refer to the links I posted above, you can find code there. – Jerry Dodge Sep 19 '12 at 12:52
  • @JerryDodge i can't understand content of that link because i don't know about Thread. – Ganpat Sep 20 '12 at 11:20
  • @Gani, you're much better off abandoning the `TTimer` and using a thread, mainly because of the nature of Service Applications. #2 above is the same as `mjn` recommends in his answer. It's standard practice to use a thread in a service. – Jerry Dodge Sep 20 '12 at 23:25
1

When you say "service terminates after 15 seconds" it makes me think you are debugging the code.

If you don't have any option and can't use what others suggested, with the code above the timer event is triggered properly when you install and start the service via services.msc. However, if you are debugging the service, the timer event will not be triggered and the aplication will terminate (as you said). I would create a procedure to be called inside timer event, and call it once in ServiceExecute event, so you could debug like this:

procedure TSomeService.ServiceExecute(Sender: TService);
begin
  ExecuteSomeProcess(); // Put breakpoint here to debug
  while not self.Terminated do
    ServiceThread.ProcessRequests(true);
end;

procedure TSomeService.TimerTimer(Sender: TObject);
begin
  timer.Enabled := false;
  ExecuteSomeProcess(); // This can't throw any exception!
  timer.Enabled := true;
end;
Alisson Reinaldo Silva
  • 10,009
  • 5
  • 65
  • 83