Recent

Author Topic: Windows service with FPTimer  (Read 13594 times)

TyneBridges

  • Full Member
  • ***
  • Posts: 150
    • Personal blog
Windows service with FPTimer
« on: March 20, 2013, 08:08:03 am »
Lazdaemon is useful to have as a Lazarus application type but it presents lots of challenges to less capable programmers like me. A timer must be one of the most frequently used elements within a service and I have seen several threads on this, but only very general information on how it can be done. By default, timer events are ignored in a service because the main thread has no message queue. http://wiki.freepascal.org/Multithreaded_Application_Tutorial gives an example of how to use TThread but there is nothing there on ensuring that the thread will have a message queue (I'm guessing that, by default, it won't when spawned from a service thread that doesn't) and even where to put the code for the methods to run within it - in this case, Timer.OnCreate and Timer.OnTimer - isn't clear to me.

Is anyone prepared to share their code, or point me to an example that shows how to run a timer (presumably FPTimer) in a service? I'm sure this would be valuable to many forum readers.
« Last Edit: April 01, 2013, 06:06:03 pm by JohnSaltwell »
John H, north east England
Lover of the old Delphi, still inexperienced with FPC/Lazarus and not an instinctive programmer

ChrisF

  • Hero Member
  • *****
  • Posts: 542
Re: Windows service with FPTimer
« Reply #1 on: March 20, 2013, 06:16:10 pm »
Hello,

I don't know the possibilities offered by Lazarus/FPC, but if your application is intended to run only on Windows, you could use Events in a dedicated thread (using only the Windows API).

For instance, this is a quick sample for a console program, emulating the function of a timer with this kind of solution. I guess it should also work the same way in a windows service.

Code: [Select]
program ttest;

{$mode objfpc}{$H+}

uses Windows;

const TIMER_INTERVAL  = 5000;     // (ms)
const TIMER_NBREVENTS = 1;        // Number of External Events to look for in the Timer Thread (Only 1 in this sample)

const TIMER_MAXTICKS  = 4;        // Maximum Number of Timer Ticks (To simulate a way to exit from the test program)

var
  HandEvGloEnd: Longword = 0;     // Handle for Event : Global End
  HandEvReqEnd: Longword = 0;     // Handle for Event : Request End
  HandleThread: Longword = 0;     // Handle for Timer Thread

  NbrTimerTicks: Integer = 0;     // Current Number of Timer Ticks

// Called on Each Timer Tick
procedure ProcToCall();
begin
  Writeln('Timer Tick');
  Inc(NbrTimerTicks);
  if NbrTimerTicks>=TIMER_MAXTICKS then
    SetEvent(HandEvGloEnd);   // Ask to quit the main infinite waiting
end;

function  TimerThread(PP: Pointer): Integer;
var ArrEvHandle: array [0..Pred(TIMER_NBREVENTS)] of Longword;  // Array of Events to Look for
begin
  ArrEvHandle[0]:=HandEvReqEnd;
  while True do
    begin
      Write('Waiting ...  ');
      WaitForMultipleObjects(TIMER_NBREVENTS,@ArrEvHandle,False,TIMER_INTERVAL);
      if WaitforSingleObject(HandEvReqEnd,0)<>WAIT_TIMEOUT then // Request to End ?
        begin
          Writeln('No more waiting');
          Break;
        end;
      ProcToCall();
    end;
  Result:=0;
  EndThread(0);
end;

procedure StartTimerThread();
var i1: Longword;
begin
  Writeln();
  Writeln('Start Timer Thread');
  HandEvReqEnd:=CreateEvent(Nil,True,False,PChar('MyEventNames_ReqEnd'));
  HandleThread:=BeginThread(Nil,0,TThreadFunc(@TimerThread),Nil,0,i1);
end;

procedure StopTimerThread(Const ExitForced: Integer);
begin
  Writeln('Stop Timer Thread');
   if ExitForced=0 then
      begin
         SetEvent(HandEvReqEnd);
         WaitForSingleObject(HandleThread,3000);       // Wait 3s mawimum for the "real" ending of the thread (it should be quite enough)
      end;
  CloseHandle(HandEvReqEnd);
  CloseHandle(HandleThread);
end;

begin
  HandEvGloEnd:=CreateEvent(Nil,True,False,PChar('MyEventNames_GloEnd'));
  StartTimerThread();
  WaitforSingleObject(HandEvGloEnd,INFINITE);   // Simulate an infinite waiting
  StopTimerThread(0);
  CloseHandle(HandEvGloEnd);
end.

Here, a dedicated timer thread is created, which is responsible of calling various functions (only 1 function in this sample) each time it falls down (5s interval in this sample).

For the sample purposes, the main program simulates a infinite waiting, and finally ends after 4 timer ticks. Note that all this part in only for the sample purposes, and has nothing to do really with the timer thread itself.

The most important thing in this sample, is the fact that the concerned targeted function (i.e. ProcToCall) is called on each timer ticks (each 5s here).

To make it simple, there are no controls made on the return values given by the Windows API: which is of course quite incorrect. They should be always be tested. For instance:
Code: [Select]
  ...
  MyEvent:=CreateEvent(  ... Parameters... );
  if MyEvent=0 then         // Error
     begin
        // Code To Process The Error
     end
  ...
[code]

**Edit**: code modified for a better comprehension.

« Last Edit: March 20, 2013, 06:56:09 pm by ChrisF »

TyneBridges

  • Full Member
  • ***
  • Posts: 150
    • Personal blog
Re: Windows service with FPTimer
« Reply #2 on: March 21, 2013, 04:53:07 pm »
Thanks, Chris! That turned out to be just what I was looking for and I think others will find it useful as well. I'm indebted to you.
« Last Edit: March 23, 2013, 08:55:51 am by JohnSaltwell »
John H, north east England
Lover of the old Delphi, still inexperienced with FPC/Lazarus and not an instinctive programmer

TyneBridges

  • Full Member
  • ***
  • Posts: 150
    • Personal blog
Re: Windows service with FPTimer
« Reply #3 on: April 01, 2013, 06:16:31 pm »
Unfortunately I spoke too soon. Once I added a SimpleIPC client to my code I found that the service was crashing unexpectedly, which seems to be because my IPC client was within the data module definition and the thread that tried to start it wasn't a property of the module. However, when I amended the code on the following lines (using the proper practice of defining all methods within objects where appropriate), I found that it wouldn't compile.

The code below gives "Error: variable identifier expected" at @TimerThread (I also have function TDaemon1.TimerThread(PP: Pointer): Integer;). I've discovered (unsurprisingly) that Self.@TimerThread and @Self.TimerThread don't compile either.)

Code: [Select]
procedure TDaemon1.StartTimerThread();
var i1: Longword;
begin
HandEvReqEnd:= CreateEvent(Nil, True, False, PChar('MyEventNames_ReqEnd'));
HandleThread:= BeginThread(Nil, 0, TThreadFunc(@TimerThread), Nil, 0, i1);
end;
   

After that, I tried moving all the thread code from the data module object back to the application level and qualifying references on the lines shown below. This allowed the application to compile but the code below only runs as far as the log message before crashing silently and only showing me the assembler (i.e. no error message), even with debug info on.

Code: [Select]
Writeln(DLog, 'Testing IPC. If no text appears below then the crash is caused by lines immediately following.');
Flush(DLog);
Daemon1.IPCC.ServerID:= 'IPCS'; 
 

Does anyone know of a simple way that I can adapt the code?
« Last Edit: April 02, 2013, 12:19:06 am by JohnSaltwell »
John H, north east England
Lover of the old Delphi, still inexperienced with FPC/Lazarus and not an instinctive programmer

ChrisF

  • Hero Member
  • *****
  • Posts: 542
Re: Windows service with FPTimer
« Reply #4 on: April 02, 2013, 07:32:23 pm »
I'm not sure you can use the timer thread as a member of the daemon object. But, I could be wrong as I've just had a very quick look at all the daemon/service process within Lazarus/FPC.

Anyway, attached the source code for a basic sample using a Lazarus/FPC daemon project, and using a timer (as proposed in my first post). I's just a very simple one as I'm not familiar with Lazarus/FPC daemons.

I've logged all the interesting events, but you'll have to modify the path/filename for the concerned log file; it's statically defined as 'C:\TestSer\TestSer.Log' in this sample (see procedure CallWriteLn), with a project supposed to be located in the 'C:\TestSer' directory.

The concerned service is called 'My Service Test' (if you want to deal with it directly within the windows service tool).

Just a final words: I've found in the past that Delphi doesn't like too much to start/stop the same thread again and again, especially concerning memory leaks (don't know for Lazarus/FPC). That's why I've preferred to start/stop the timer thread only once during a session within my sample. Which means that even if the service is stopped, the timer thread is still running. The CPU usage is honestly very, very minimal, but well, one could say that it's not quite "clean".

If you prefer, you can try to (start and) stop it inside the (DataModuleStart and) DataModuleStop procedure(s) instead.

Anyway, I guess that the concerned service is not supposed to be started an stopped manually a lots of time during the same session (i.e. when windows is running). So, the results will probably the same in both cases.


** EDIT** : I'm not quite sure if it can really help you, as its seems your troubles are not directly related to the timer thread itself, but due to "interactions" between this thread and the rest of your application. Please explain your problems a bit more in this case (with possible concerned code).

Theoretically you can call all kind of procedures/functions when the timer function is called in the timer thread; except those concerning the LCL (well, I supposed so, as it's the case for the VCL in Delphi). But services are not supposed to use forms. Furthermore, it's still possible to interact within forms within the VCL/LCL from an other thread than the VCL one, but it requires additional code.


**EDIT2 ** : Are you sure your problems are not only relative to the IPC part of your code ?
« Last Edit: April 02, 2013, 07:49:21 pm by ChrisF »

TyneBridges

  • Full Member
  • ***
  • Posts: 150
    • Personal blog
Re: Windows service with FPTimer
« Reply #5 on: April 02, 2013, 08:25:45 pm »
Many thanks for your help, Chris. I haven't had a chance to look at your example yet but will investigate it and give more detail as requested if I make no progress. I'm afraid I'm rather a "trial and error" type of programmer and often work by taking and modifying other people's code rather than starting from scratch. I've never managed to acquire the in-depth knowledge needed to write code for the trickier operations on my own.

I'm not certain that the problems are only related to IPC, but the service seemed to be stable until I added the IPC server and then began crashing consistently whenever the IPC functions were called.

I don't think I need to stop and restart the timer - I'll just skip some of the functions it calls by using conditions rather than complicating things further. I'm not using forms unless the Lazarus service "data module" counts as a form (am using IPC to another application that does the display), or any visual components.
« Last Edit: April 03, 2013, 10:48:44 am by JohnSaltwell »
John H, north east England
Lover of the old Delphi, still inexperienced with FPC/Lazarus and not an instinctive programmer

TyneBridges

  • Full Member
  • ***
  • Posts: 150
    • Personal blog
Re: Windows service with FPTimer
« Reply #6 on: April 03, 2013, 06:53:06 pm »
I tried your example, Chris, and it worked perfectly for me - until I added an IPC client. Following that, the service was able to install but couldn't even start properly. With your project as supplied, the output was as follows:

17:32:23: Start Timer Thread
17:32:23: Waiting ... 
17:32:28: Timer Tick
17:32:28: Waiting ... 
17:32:33: Timer Tick
17:32:33: Waiting ... 
17:32:38: Timer Tick
17:32:38: Waiting ...

With my minor amendments, this changed to the following:

17:42:19: Create
17:42:19: Destroy
17:43:02: Create
17:43:02: Install
17:43:02: Destroy
17:43:12: Create

I've attached the whole project this time. Does this mean there is some basic incompatibility between this threading technique and TSimpleIPC components?

Many thanks for your time.
John H, north east England
Lover of the old Delphi, still inexperienced with FPC/Lazarus and not an instinctive programmer

ChrisF

  • Hero Member
  • *****
  • Posts: 542
Re: Windows service with FPTimer
« Reply #7 on: April 03, 2013, 10:53:18 pm »

The problem is really concerning the IPC part, and not any interactions between it and the "timer".

I've modified your code, in order to protect all the IPC calls and avoid any service crash. One again, I'm not sure you can put the IPC client directly inside the Daemon object.

The IPC client is now trying to connect to a server with 'MyUniqueIPCServ' as a server ID. I've included a small graphical project for such an IPC server (BTW, this sample is not really working perfectly, just enough for some basic tests).

So, currently no more service crashes.

Except that it will probably not satisfy you, as the IPC client (inside the service) can't connect to the IPC server (inside the graphical application).

I can only obtain a connection on Windows XP, and by authorizing the service to interact with the Desktop before starting it.

Without that, I always get:
Timer Tick
IPC Client About to Start
IPC Client: After Create
IPC Client: After ServerID
ERROR IPC Client: Can't Connect to Server MyUniqueIPCServ

Inside Windows XP, and with an interaction with the Desktop for the service:
Timer Tick
IPC Client About to Start
IPC Client: After Create
IPC Client: After ServerID
IPC Client: After Connect
IPC server detected - hooray!

As I've understood this service is intended to run on Windows 7, I'm afraid you're a bit out of luck.

If you're trying to connect to an IPC server which is also running inside a service, it might work (well, it should work in fact).

If not, either it's not possible currently with the Lazarus/FPC IPC design, either I've missed something concerning the IPC client/service options (and it's quite possible).

I remember I've also some troubles in the past, when I've coded my own IPC implementation for exchanging data between a windows service and a graphical application; especially concerning the security attributes. But I've finally succeeded, so I'm sure it's possible. It's done by using 'named pipes', as recommended by Microsoft.

I'm afraid I can't help you any further, as it seems there is a problem within the Lazarus/FPC IPC implementation itself.

You may have any more chance with someone else, with a better experience for it than mine.

Or by patching the concerned code by yourself, if it's required...

Any suggestions ?

TyneBridges

  • Full Member
  • ***
  • Posts: 150
    • Personal blog
Re: Windows service with FPTimer
« Reply #8 on: April 04, 2013, 12:15:21 pm »
Thanks for all your time and effort, Chris. My project does need to run on Windows 7 and the aim is to connect to GUI programs to display PC status both before and after logon (a screen saver and a tray icon respectively), so it looks as if I need to look at the named pipes option instead of IPC.
John H, north east England
Lover of the old Delphi, still inexperienced with FPC/Lazarus and not an instinctive programmer

ChrisF

  • Hero Member
  • *****
  • Posts: 542
Re: Windows service with FPTimer
« Reply #9 on: April 04, 2013, 03:06:42 pm »

Well, handling named pipes is another pretty good challenge, if you ask to me. You are definitively not choosing the simplest ways :) .

For the server, you'll have to create an additional thread, which is dedicated to listen to client calls.

Even for the clients, you may have to use a thread too, if you intend to ask some infos to the server, and then receive the concerned data as an answer. I say a thread because if not, your client program may hang up otherwise, if the server is not responding.

Plus a few security considerations (but only some basic ones), if you want that your service and your graphical applications are able to communicate each others.

A few links:
-a good Microsoft start page for named pipes:
http://msdn.microsoft.com/en-us/library/windows/desktop/aa365590%28v=vs.85%29.aspx
-concerning security considerations, a few links (just given by google):
http://stackoverflow.com/questions/6684387/named-pipes-from-windows-service-to-client-application
http://synopse.info/forum/viewtopic.php?id=43

As your service and graphical applications are apparently supposed to run on the same computer each time, the method described into the above links should work fine (it did for me).

But, if you intend to run a common service on a unique computer, and to connect your graphical applications from other computers, that's another matter...

ChrisF

  • Hero Member
  • *****
  • Posts: 542
Re: Windows service with FPTimer
« Reply #10 on: April 04, 2013, 04:01:51 pm »

BTW, do you really need "real-time" communications between your service and your other programs ?

What about a flat text file, periodically checked ? Or even better (no read/write issues), a small SQLite base ?


TyneBridges

  • Full Member
  • ***
  • Posts: 150
    • Personal blog
Re: Windows service with FPTimer
« Reply #11 on: April 04, 2013, 09:03:44 pm »

BTW, do you really need "real-time" communications between your service and your other programs ?

What about a flat text file, periodically checked ? Or even better (no read/write issues), a small SQLite base ?

I think you're right, Chris - have been trying to find out how pipes are done in Lazarus on Windows and have made no progress at all so far. I know that Delphi was lightning fast at reading and writing to text files so, if Lazarus approaches its performance, the text file or local database is probably a much better idea. Thanks again.
John H, north east England
Lover of the old Delphi, still inexperienced with FPC/Lazarus and not an instinctive programmer

avra

  • Hero Member
  • *****
  • Posts: 2514
    • Additional info
Re: Windows service with FPTimer
« Reply #12 on: April 05, 2013, 09:14:20 am »
Maybe using some simple TCP/IP messaging could simplify things?
ct2laz - Conversion between Lazarus and CodeTyphon
bithelpers - Bit manipulation for standard types
pasettimino - Siemens S7 PLC lib

 

TinyPortal © 2005-2018