Recent

Author Topic: Windows service, self stop on condition  (Read 8185 times)

cris75

  • Jr. Member
  • **
  • Posts: 59
Windows service, self stop on condition
« on: August 11, 2021, 12:27:01 pm »
Hi,
with the help of the wiki and this (quite old) discussion https://forum.lazarus.freepascal.org/index.php/topic,20274.0.html, I successfully wrote some windows services, and they work as expected;
as a base of developing I used the approach by Udo Schmal here, https://www.gocher.me/Daemon in which he sets the FreeOnTerminate property of the thread to true when he starts the service;
when the service is started, I usually check for the existence of a config file from which I retrieve a handful of settings to be used in the execution;
as of today, if the config file does not exist I just don't go on and terminate the thread, but a thing I thought it was easy to do, that I've been not able to (and would like), is to "stop" (automatically) the service if this condition is met; probably I'm missing something..
if I'm not getting confused, I saw more than one time an "example" of what I'm saying, of a windows service that once started (by pressing "start" or with "net start servicename") automatically stops (for some reason, obviously), how could I reproduce this behavior?
Do you think it's possible?
Lazarus: 3.0 / FPC: 3.2.2
[x86_64-win64-win32/win64]
OS IDE: Win10 64bit

cris75

  • Jr. Member
  • **
  • Posts: 59
Re: Windows service, self stop on condition
« Reply #1 on: August 12, 2021, 09:50:36 am »
..no reply of any kind until now to my post.. I'm wondering if my question is silly or poorly asked.. If so forgive me in advance.. :'(
Lazarus: 3.0 / FPC: 3.2.2
[x86_64-win64-win32/win64]
OS IDE: Win10 64bit

Bart

  • Hero Member
  • *****
  • Posts: 5275
    • Bart en Mariska's Webstek
Re: Windows service, self stop on condition
« Reply #2 on: August 12, 2021, 10:30:06 am »
It's a program, so you can stop it like any other program.
If it's derived from TCustomApplication then Application.Terminate should do.
And, of course, you could always do Halt();

You must have the OS configered not to restart this service when it stops.

Bart

cris75

  • Jr. Member
  • **
  • Posts: 59
Re: Windows service, self stop on condition
« Reply #3 on: August 12, 2021, 12:28:52 pm »
Quote
You must have the OS configered not to restart this service when it stops

I don't think the operating system will restart the service if this one is "stopped" as you would usually do via services manager or by shell command "net stop service"..
but I assume maybe I don't understand what you're trying to explain me..  ::)

to be clear, this is the execute method of the thread:

Code: Pascal  [Select][+][-]
  1. procedure TDaemonThread.Execute;
  2. var i: integer;
  3. begin
  4.   Parent.EventLog1.Info('Begin of Main thread');
  5.  
  6.   i:= 0;
  7.   Application.Log(etDebug, 'Thread.Execute');
  8.   try
  9.     repeat
  10.       Sleep(1000); //milliseconds
  11.       inc(i);
  12.       Application.Log(etDebug, 'Thread.Loop ' + Format('Tick :%d', [i]));
  13.       if i = 5 then Terminate;  // this is an example condition for which the thread will be terminated
  14.     until Terminated;  // the loop will be exited because of the thread terminated
  15.   finally
  16.     Application.Log(etDebug, 'Thread.LoopStopped');
  17.   end;
  18.   { at this point how could I stop the service? }
  19. end;

I suppose that to get the behavior I want, the daemon has to know about the termination of the thread and somehow "stop" itself;
I use "Parent" declared in line 24 (see attached code) for EventLog, I thought "Parent.Stop" could have done it (stop the service) but i was wrong..
Lazarus: 3.0 / FPC: 3.2.2
[x86_64-win64-win32/win64]
OS IDE: Win10 64bit

balazsszekely

  • Guest
Re: Windows service, self stop on condition
« Reply #4 on: August 12, 2021, 01:21:43 pm »
You should pass GenericDaemon to DaemonThread via the constructor. Something like this(not tested):

Code: Pascal  [Select][+][-]
  1.   TGenericDaemon = class; //forward declaration
  2.  
  3.   TDaemonThread = class(TThread)
  4.     Parent: TGenericDaemon;
  5.     FGenericDaemon: TGenericDaemon;  //add this
  6.     procedure SetParent(AOwner: TGenericDaemon);
  7.   protected
  8.     procedure Execute; override; //execute should be in protected section
  9.   public
  10.     constructor Create(AGenericDaemon: TGenericDaemon);   //add this                                                          
  11.     destructor Destroy; override;
  12.   end;

Then on constructor create, assign GenericDaemon to a local variable:
Code: Pascal  [Select][+][-]
  1. constructor TDaemonThread.Create(AGenericDaemon: TGenericDaemon);
  2. begin
  3.   inherited Create(True);
  4.   FGenericDaemon := AGenericDaemon;
  5. end;    

On Daemon start:
Code: Pascal  [Select][+][-]
  1.   //...
  2.   FThread:= TDaemonThread.Create(Self); //add this
  3.   FThread.SetParent(self);
  4.   FThread.FreeOnTerminate:= true;
  5.   FThread.Start;

Finally on thread execute:
Code: Pascal  [Select][+][-]
  1. procedure TDaemonThread.Execute;
  2. var i: integer;
  3. begin
  4.   Parent.EventLog1.Info('Begin of Main thread');
  5.  
  6.   i:= 0;
  7.   Application.Log(etDebug, 'Thread.Execute');
  8.   try
  9.     repeat
  10.       Sleep(1000); //milliseconds
  11.       inc(i);
  12.       Application.Log(etDebug, 'Thread.Loop ' + Format('Tick :%d', [i]));
  13.       if i = 5 then FGenericDaemon.Stop;//add this
  14.     until Terminated;  // the loop will be exited because of the thread terminated
  15.   finally
  16.     Application.Log(etDebug, 'Thread.LoopStopped');
  17.   end;
  18.   { at this point how could I stop the service? }
  19. end;
  20.  

cris75

  • Jr. Member
  • **
  • Posts: 59
Re: Windows service, self stop on condition
« Reply #5 on: August 12, 2021, 02:16:26 pm »
Thank you.
It compiles and run, but it seems it doesn't work, in windows services manager it is not shown as stopped (as I expected)  :'(
attached screenshot;
btw.. I can't manually stop the service anymore, I have to kill genericdaemon.exe process to uninstall the service now..

this is the log:
Code: [Select]
genericdaemon [2021-08-12 13:55:58.714 Debug] DaemonMapper.Create
genericdaemon [2021-08-12 13:55:58.715 Debug] DaemonMapper.Createted
genericdaemon [2021-08-12 13:55:58.715 Debug] DaemonMapper.Run
genericdaemon [2021-08-12 13:55:58.718 Debug] Daemon.Start: true
genericdaemon [2021-08-12 13:55:58.718 Debug] Daemon.Execute: false
genericdaemon [2021-08-12 13:55:58.718 Debug] Thread.Execute
genericdaemon [2021-08-12 13:55:59.734 Debug] Thread.Loop Tick :1
genericdaemon [2021-08-12 13:56:00.736 Debug] Thread.Loop Tick :2
genericdaemon [2021-08-12 13:56:01.739 Debug] Thread.Loop Tick :3
genericdaemon [2021-08-12 13:56:02.749 Debug] Thread.Loop Tick :4
genericdaemon [2021-08-12 13:56:03.749 Debug] Thread.Loop Tick :5
genericdaemon [2021-08-12 13:56:03.749 Debug] Daemon.Stop: true
Lazarus: 3.0 / FPC: 3.2.2
[x86_64-win64-win32/win64]
OS IDE: Win10 64bit

balazsszekely

  • Guest
Re: Windows service, self stop on condition
« Reply #6 on: August 12, 2021, 02:20:40 pm »
According to your log, the Service stopped. Please try to add:
Code: Pascal  [Select][+][-]
  1. function TGenericDaemon.Stop: boolean;
  2. begin
  3.   result:= inherited Stop;
  4.   Application.Log(etDebug, 'Daemon.Stop: ' + BoolToStr(result));
  5.  
  6.   FThread.Terminate;
  7.   FThread.WaitFor;
  8.  
  9.   FThread:= nil;
  10.   FreeAndNil(EventLog1);
  11.   Application.Terminate; //<-This line
  12. end;

cris75

  • Jr. Member
  • **
  • Posts: 59
Re: Windows service, self stop on condition
« Reply #7 on: August 12, 2021, 02:30:53 pm »
yes, you are right, the log say so..
..but the process is still there running..
and services manager still shows it running

tried your last suggestion, no change for me  :'(

p.s.
still can't manually stop it, have to kill the process
Lazarus: 3.0 / FPC: 3.2.2
[x86_64-win64-win32/win64]
OS IDE: Win10 64bit

cris75

  • Jr. Member
  • **
  • Posts: 59
Re: Windows service, self stop on condition
« Reply #8 on: August 12, 2021, 08:15:01 pm »
I double-checked everything, it seems okay, can't understand why the process doesn't close.. and why manually stopping the service hangs and return an error..

Does somebody have a clue..?
Lazarus: 3.0 / FPC: 3.2.2
[x86_64-win64-win32/win64]
OS IDE: Win10 64bit

trev

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 2020
  • Former Delphi 1-7, 10.2 user
Re: Windows service, self stop on condition
« Reply #9 on: August 13, 2021, 01:47:42 am »

cris75

  • Jr. Member
  • **
  • Posts: 59
Re: Windows service, self stop on condition
« Reply #10 on: August 13, 2021, 09:49:44 am »
Thank you all for your time;
thank you @trev, I'm going to give a look..
Lazarus: 3.0 / FPC: 3.2.2
[x86_64-win64-win32/win64]
OS IDE: Win10 64bit

cris75

  • Jr. Member
  • **
  • Posts: 59
Re: Windows service, self stop on condition
« Reply #11 on: August 13, 2021, 12:56:48 pm »
@trev I checked the link, thank you
..if I understood correctly, it should be done via ServiceManager
my approach was to add a private method to TGenericDaemon to connect to the service control manager and invoke the "stop" via TServiceManager method StopService
I assumed that my service is certainly running at the moment of the "self-stop service" invocation,
I limited to modify slightly the "IsServiceRunning" function in the Wiki for ServiceManager here https://wiki.freepascal.org/ServiceManager
now it works, but was asking myself if the approach is ok and/or I did it correctly or the result is a mess of code that produces a functioning result for a matter of luck...
if someone has some spare time to give a look at the code and/or review it i'd be glad, thank you all
the code is attached  :-[
Lazarus: 3.0 / FPC: 3.2.2
[x86_64-win64-win32/win64]
OS IDE: Win10 64bit

trev

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 2020
  • Former Delphi 1-7, 10.2 user
Re: Windows service, self stop on condition
« Reply #12 on: August 13, 2021, 01:18:27 pm »
You're welcome. I'm glad it helped.

 

TinyPortal © 2005-2018