Recent

Author Topic: Getting started with a service (LazDaemon)  (Read 33089 times)

JohnSaltwell

  • Jr. Member
  • **
  • Posts: 78
    • Personal blog
Getting started with a service (LazDaemon)
« on: March 16, 2013, 04:19:30 pm »
I've created a number of working programs with Borland Delphi 1, 2 and 5 (and still use Delphi at work) so am familiar with most of the basic conventions. However, I'm mystified when it comes to service apps. I've had a stab at a service that should simply write numbers to a text file using an fptimer, but I'm frustrated to find that I can't test it.

From http://wiki.freepascal.org/Daemons_and_Services:

When the daemon is started the command line parameters are parsed. The following are predefined:
-i --install: register the daemon. This has no effect under unix.
-u --uninstall: unregister the daemon. This has no effect under unix.
-r --run: start the daemon. Windows does this normally itself.


This implies that all I need to do after compiling my program is to type <programname> -i - in my case, bdaemon - i. I compiled it successfully, opened the folder and could see bdaemon.exe there. I right-clicked Windows Command Processor on my menu and selected "Run as administrator" and typed bdaemon -i. No error: good.

I realise that this won't start the service, and that I need to do this manually, or by restarting Windows. However, when I go into Computer | Manage | Services and Applications | Services, surely I should then see bdaemon (or Bdaemon) in the alphabetical list? It's not there and I have no idea why. Down at the first hurdle...

I can't find an example of how to use Lazdaemon with a step-by-step description of the requirements for Windows services. The Wiki is "bare bones" to say the least, and lumps Windows together with Unix. I'm completely unable to translate the following into what I need to do in my code.

TDaemonMapper

This component handles the service registration. Each instance needs one entry in the property DaemonDefs.


My project does contain the following standard code, added automatically by Lazarus:

Code: [Select]
type
  TDaemonMapper1 = class(TDaemonMapper)
  private
    { private declarations }
  public
    { public declarations }
  end;

var
  DaemonMapper1: TDaemonMapper1;

implementation

procedure RegisterMapper;
begin
  RegisterDaemonMapper(TDaemonMapper1)
end;

{$R *.lfm}

initialization
  RegisterMapper;
end.
                         

I'm fairly sure I've missed a property but have no idea what to add, or where. I would be grateful if anyone is able to tell me what I've done wrong, or point me to an example of how to use LazDaemon with Windows.
« Last Edit: March 16, 2013, 08:41:39 pm by JohnSaltwell »
John H, UK
Lover of the old (reasonably priced!) Delphi
Still inexperienced with FPC/Lazarus. Not an instinctive programmer!

BigChimp

  • Hero Member
  • *****
  • Posts: 5740
  • Add to the wiki - it's free ;)
    • FPCUp, PaperTiger scanning and other open source projects
Re: Getting started with a service (LazDaemon)
« Reply #1 on: March 16, 2013, 04:27:41 pm »
FYI, for easier reading, use the # button in the editor bar to mark your code. This allows copy/pasting as well as formatting.

Have no experience with writing services with fpc, sorry, so no advice there.
Want quicker answers to your questions? Read http://wiki.lazarus.freepascal.org/Lazarus_Faq#What_is_the_correct_way_to_ask_questions_in_the_forum.3F

Open source including papertiger OCR/PDF scanning:
https://bitbucket.org/reiniero

Lazarus trunk+FPC trunk x86, Windows x64 unless otherwise specified

JohnSaltwell

  • Jr. Member
  • **
  • Posts: 78
    • Personal blog
Re: Getting started with a service (LazDaemon)
« Reply #2 on: March 16, 2013, 06:46:13 pm »
Thanks for the hint, BigChimp. I found the following article which looked useful

http://www.turbog.com/wp-content/uploads/2012/04/daemons.pdf

...unfortunately this concentrates on FreePascal alone rather than Lazarus. Most of the code for the visual version is missing and I can't fill in the gaps. I've discovered DaemonMapper1 in the Lazarus default application via the Object Inspector. I changed DaemonDefs from 0 items to 1 item and allowed Lazarus to fill in the default name DaemonMapper1 (I haven't changed any of the other component or object default names). This does compile and now, when I issue the command bdaemon -i the result is

Code: [Select]
exception at 0040FCFF:
Unknown daemon class name: .

Hopefully someone has an example somewhere with Lazarus code, or can give me some hints on what I've missed. Translating the description of the mapper in the article above into Pascal code in the right places is beyond my knowledge. Any help would be gratefully received.
John H, UK
Lover of the old (reasonably priced!) Delphi
Still inexperienced with FPC/Lazarus. Not an instinctive programmer!

JohnSaltwell

  • Jr. Member
  • **
  • Posts: 78
    • Personal blog
Re: Getting started with a service (LazDaemon)
« Reply #3 on: March 16, 2013, 09:02:55 pm »
Following the create example for FP in the article, I've tried adding the following in mapunit and still get the error "Unknown daemon class name: ." when I try to install. For some reason, my daemon class name is not being picked up (incidentally, I tried D.DaemonClassName both with and without the initial T, recompiling after each edit).

Code: [Select]
procedure TDaemonMapper1.DaemonMapper1Create(Sender: TObject);
Var D: TDaemonDef;
begin
     D:= DaemonDefs.Add as TDaemonDef;
     D.DisplayName:= 'Booking Service';
     D.Name:= 'BookingService';
     D.DaemonClassName:= 'TDaemonMapper1';
     D.WinBindings.ServiceType:= stWin32;
end; 

The article began this bit of code with "inherited Create(AOwner)". I tried inserting this but modifying it to inherited Create(Sender); Of course, that doesn't compile as Sender is the wrong object type.
John H, UK
Lover of the old (reasonably priced!) Delphi
Still inexperienced with FPC/Lazarus. Not an instinctive programmer!

JohnSaltwell

  • Jr. Member
  • **
  • Posts: 78
    • Personal blog
Re: Getting started with a service (LazDaemon)
« Reply #4 on: March 17, 2013, 12:18:37 pm »
I've now removed the code above and, by clicking into things I didn't even realise had click options, have managed to edit the properties for TDaemonMapper1 in the Object Inspector. I have 1 item in DaemonDefs called DaemonMapper1. Clicking on the line "1 item" gives me Editing DaemonMapper1.DaemonDefs[0] - evidently a zero-based array. In the Properties list I've added the same properties as in my code quoted, leaving UserName blank, which the help file says will run the service under the System account - as I want.

I've recompiled the service but now, typing bdaemon -i at the Windows command prompt from the folder with the .exe file in it gives me

Code: [Select]
exception at 0040FCFF:
Unknown daemon class name: TDaemonMapper1.


Evidently something is still missing, but what?
John H, UK
Lover of the old (reasonably priced!) Delphi
Still inexperienced with FPC/Lazarus. Not an instinctive programmer!

BigChimp

  • Hero Member
  • *****
  • Posts: 5740
  • Add to the wiki - it's free ;)
    • FPCUp, PaperTiger scanning and other open source projects
Re: Getting started with a service (LazDaemon)
« Reply #5 on: March 17, 2013, 12:22:36 pm »
Attaching the project or uploading it somewhere would probably make it easiers for other to have a look and see what's wrong...
Want quicker answers to your questions? Read http://wiki.lazarus.freepascal.org/Lazarus_Faq#What_is_the_correct_way_to_ask_questions_in_the_forum.3F

Open source including papertiger OCR/PDF scanning:
https://bitbucket.org/reiniero

Lazarus trunk+FPC trunk x86, Windows x64 unless otherwise specified

BigChimp

  • Hero Member
  • *****
  • Posts: 5740
  • Add to the wiki - it's free ;)
    • FPCUp, PaperTiger scanning and other open source projects
Re: Getting started with a service (LazDaemon)
« Reply #6 on: March 17, 2013, 07:19:47 pm »
No idea if this is relevant to you:
http://bugs.freepascal.org/view.php?id=24067

Thanks,
BigChimp
Want quicker answers to your questions? Read http://wiki.lazarus.freepascal.org/Lazarus_Faq#What_is_the_correct_way_to_ask_questions_in_the_forum.3F

Open source including papertiger OCR/PDF scanning:
https://bitbucket.org/reiniero

Lazarus trunk+FPC trunk x86, Windows x64 unless otherwise specified

JohnSaltwell

  • Jr. Member
  • **
  • Posts: 78
    • Personal blog
Re: Getting started with a service (LazDaemon)
« Reply #7 on: March 17, 2013, 07:39:39 pm »
Thanks everyone for trying to help. I have finally managed to get my daemon to run. It looks as if it was my own silly mistake and that I had entered the daemon mapper name in the properties where I should have entered the daemon name. Seems I'm still groping my way along with a lot of trial and error. :-[

Latest problem: It seems fptimer doesn't work in a daemon (Windows service) because, although the timer is implemented using threads, I have been calling it from the main thread of my daemon, which (by design of Windows/FreePascal) has no message queue. Is there any simple way of ensuring the timer runs, i.e. without trying to write a threaded application - which I think is beyond my capabilities? (I suspect the answer is no, but it may be worth asking.)
« Last Edit: March 17, 2013, 08:02:55 pm by JohnSaltwell »
John H, UK
Lover of the old (reasonably priced!) Delphi
Still inexperienced with FPC/Lazarus. Not an instinctive programmer!

mdalacu

  • Full Member
  • ***
  • Posts: 202
    • dmSimpleApps
Re: Getting started with a service (LazDaemon)
« Reply #8 on: May 31, 2013, 03:42:30 pm »
Does anyone know how to cleanly stop a thread started by demon? If i call FThread.Terminate from TDaemon1.DataModuleStop, the thread never gets a chance to finish. The app just exits.

ChrisF

  • Hero Member
  • *****
  • Posts: 542
Re: Getting started with a service (LazDaemon)
« Reply #9 on: June 02, 2013, 06:33:10 pm »

There has been a few other topics about daemons/services for windows recently.

The second link is probably closer to your question:
- http://www.lazarus.freepascal.org/index.php/topic,20308.0.html
- http://www.lazarus.freepascal.org/index.php/topic,20652

Here is a modification of my daemon test code for the second topic (see attached file). I've just added an indication when the main thread task is ended (inside the TTestThread.Execute procedure):
'Application.Log(etcustom,'End of Thread');'

I can clearly see this entry in my windows log.

BTW, you may also have a look at this bug report, if your using 'standard' thread inside a 'standard' daemon project:
 http://bugs.freepascal.org/view.php?id=24320


mdalacu

  • Full Member
  • ***
  • Posts: 202
    • dmSimpleApps
Re: Getting started with a service (LazDaemon)
« Reply #10 on: June 03, 2013, 08:55:18 am »
Hi ChrisF. Thank you for taking time to reply.
I have tried everything  with no success. I have modified your attached example and still the thread does not close cleanly.
Code: [Select]
procedure TTestThread.Execute;
var
  logFile: TextFile;
begin
 AssignFile(logFile,'d:\Programare\Lazarus\NoWifiNetBridge\log.txt');
 Rewrite(logFile);
 while not Terminated  do
    begin
      Append(logFile);
      sleep(1000);
      Writeln(logFile,DateTimeToStr(Now));
      Writeln(logFile,Terminated);
      CloseFile(logFile);
    end;
 Append(logFile);
 Writeln(logFile,'#Clean Shutdown.');
 CloseFile(logFile);

end;   
This is the only part of the code that i have modified.
The "Clean Shutdown" part never gets into the log. What am i doing wrong. Thank you very much.

ChrisF

  • Hero Member
  • *****
  • Posts: 542
Re: Getting started with a service (LazDaemon)
« Reply #11 on: June 03, 2013, 03:05:07 pm »

If we are using the same code, I guess something might be different in the way we use the service.


Attached , the same sample with a bit of 'code-cleanup' and a log to a external file instead of the windows log; please, modify the path and name for this file according to your own needs: see "CLOG_FILENAME" in the "CallWriteLn" procedure.

Here is my own results with this sample:
Code: [Select]
14:24:39: Daemon Install: -1
14:24:46: Daemon Start: -1
14:24:46: Daemon Execute: 0
14:24:46: Beginning of thread
14:24:47: Tick : 1
14:24:48: Tick : 2
...
14:25:03: Tick : 17
14:25:04: Tick : 18
14:25:05: Daemon Stop: -1
14:25:05: Tick : 19
14:25:05: End of thread
14:25:19: Daemon UnInstall: -1

Obviously, the 'End of thread' entry is present.


I've even tried with a longer sleep inside the thread (10 s instead of 1 s); just in case the thread might be canceled directly by windows when the service is stopped. Still no problems: the 'End of thread' entry is logged when the next timer tick occurs.


Here is how I'm made my own tests, in order you can see if there is a difference with yours:

1/ Install the service:
- type in console: daemon -i

2/ Start the service:
-'start' menu inside the windows services manager for the corresponding service (called 'Test daemon')

3/ Stop the service (this could be different for you ?):
-'stop' menu inside the windows services manager for the corresponding service

4/ Uninstall the service:
- type in console: daemon -u


I've also applied the FPC patch concerning the 100% CPU usage, but I don't think it could explain the difference.

A final possibility is that there could be a something relative to the windows version: 'cause I'm still using windows XP for my tests.

mdalacu

  • Full Member
  • ***
  • Posts: 202
    • dmSimpleApps
Re: Getting started with a service (LazDaemon)
« Reply #12 on: June 04, 2013, 08:33:27 am »
Hi ChrisF. Again, thank you for taking time with me.
I think you are right regarding windows version. I am using Win8 %) All the steps are done in the same way/order like you.
This is the log file:
Code: [Select]
9:29:37: Daemon Start: -1
9:29:37: Beginning of thread
9:29:37: Daemon Execute: 0
9:29:38: Tick : 1
9:29:39: Tick : 2
9:29:40: Tick : 3
9:29:41: Tick : 4
9:29:42: Tick : 5
9:29:43: Tick : 6
9:29:44: Tick : 7
9:29:45: Daemon Stop: -1
What else can i do?

ChrisF

  • Hero Member
  • *****
  • Posts: 542
Re: Getting started with a service (LazDaemon)
« Reply #13 on: June 04, 2013, 12:20:53 pm »

I don't have any direct access to a windows 8 computer, but I know someone else who can make some tests with it for me. I'll let you know about the results; so, we'll see if windows 8 is responsible of your troubles or not.

You can still make another test, if you wish.

Attached, another simple test program, but this time using a thread directly managed through windows API calls; not using the TThread class any more. This new test program is pretty close to the former one concerning the daemon/service part, but all the thread part has been replaced. I've not included the thread part into the TTestDaemon class, but anyway it's just to make some basic tests.

The results are similar to what I've already got with my former tests: what about you ?

Code: [Select]
11:53:08: Daemon Install: -1
11:53:51: Daemon Start: -1
11:53:51: Start Timer Thread
11:53:51: Daemon Execute: 0
11:53:51: Beginning of thread
11:53:52: Tick : 1
11:53:53: Tick : 2
...
11:54:04: Tick : 13
11:54:05: Tick : 14
11:54:05: Daemon Stop: -1
11:54:05: Stop Timer Thread
11:54:05: End of thread
11:54:26: Daemon UnInstall: -1

mdalacu

  • Full Member
  • ***
  • Posts: 202
    • dmSimpleApps
Re: Getting started with a service (LazDaemon)
« Reply #14 on: June 04, 2013, 03:16:13 pm »
Hi, i have tested this and it works.
Code: [Select]
16:11:58: Daemon Start: -1
16:11:58: Start Timer Thread
16:11:58: Beginning of thread
16:11:58: Daemon Execute: 0
16:11:59: Tick : 1
16:12:00: Tick : 2
16:12:01: Tick : 3
16:12:02: Tick : 4
16:12:03: Daemon Stop: -1
16:12:03: Stop Timer Thread
16:12:03: End of thread
If you need me to make same more test in order for you to patch  daemonapp, please do not hesitate.
Thank you again.