Watch, Follow, &
Connect with Us

For forums, blogs and more please visit our
Developer Tools Community.


Welcome, Guest
Guest Settings
Help

Thread: How to add Windows event when a service stops by error


This question is answered. Helpful answers available: 2. Correct answers available: 1.


Permlink Replies: 5 - Last Post: Aug 8, 2017 10:52 AM Last Post By: Remy Lebeau (Te...
Jayme Jeffman

Posts: 20
Registered: 3/2/98
How to add Windows event when a service stops by error  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Aug 7, 2017 7:48 AM
I am using C++Builder 6.0 Professional Edition, Devart ODAC components and Indy 10 components.

I have built a Windows service which is scheduled to start when Windows starts.

I could not find out why sometimes the service stops, probably an error has occurred but it is not being reported on the Windows events.

How can I force the error too be logged as a Windows event ?

Thank you very much.

Jayme jeffman
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: How to add Windows event when a service stops by error  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Aug 7, 2017 10:40 AM   in response to: Jayme Jeffman in response to: Jayme Jeffman
Jayme Jeffman wrote:

I could not find out why sometimes the service stops, probably an
error has occurred but it is not being reported on the Windows events.

Does the problem still occur when you start the service manually after
Windows is up and running?

Does your service have any dependencies on other services that may not
be running yet at startup when your service tries to start?

Typically, a VCL service stops running when its main worker thread is
terminated, either through an explicit stop request from the SCM, or an
uncaught exception. The latter should be getting logged in the Windows
Event Log automatically.

How can I force the error too be logged as a Windows event ?

You can't force it if it is not already being logged and you don't know
what is going wrong to begin with. You are just going to have to debug
your code. And if the problem only occurs during Windows startup, you
will have to implement you own logging to figure out how far the code
is actually getting before it fails.

--
Remy Lebeau (TeamB)
Jayme Jeffman

Posts: 20
Registered: 3/2/98
Re: How to add Windows event when a service stops by error  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Aug 7, 2017 1:18 PM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Hi Remy. Thank you for answering me.

{quote:title=Remy Lebeau (TeamB) wrote:}
You can't force it if it is not already being logged and you don't know
what is going wrong to begin with. You are just going to have to debug
your code. And if the problem only occurs during Windows startup, you
will have to implement you own logging to figure out how far the code
is actually getting before it fails.

--
Remy Lebeau (TeamB)

As a matter of fact I have enclosed the method which is executed by the service in a try-catch block. The method belongs to a DataModule which contains the ODAC components and the database tasks. Inside the method itself there are also try-catch blocks on every code where could happen any process error, and the errors are being logged to a file.

void __fastcall TMDMColetor::ServiceExecute(TService *Sender)
{
  if( FDebug && FLog ) FLog->AddLogEntry("Iniciando Execução");
  ORA->SetLastMDCActivity(); // Estabelecer a Data e Hora da última leitura feita no MDC
  if( FDebug && FLog ) FLog->AddLogEntry("Executando varredura");
  try{
    ORA->ExecutarVarredura();  // Função não retorna enquanto processamento não é interrompido = comportamento do serviço
  }
  catch(Exception *E){
    if(FLog) FLog->AddLogEntry("MDMColetor - Erro Fatal: "+E->Message);
    ORA->AddEventoMed( "MDMColetor - Erro Fatal: "+E->Message, "READER", "MDM", NULL, sevGrave, false, "", "");
  }
 
  if(FLog) FLog->AddLogEntry("MDM_Coletor encerrado.");
  FLog->UpdateFile();
  exit(0);
}


I have made a VCL Form to emulate the TService behavior and being able to debug the code. I have never caught an error which I have not corrected. The service stops sometimes and I do not know why.

The service works fine for more than a whole day or even a week and then it suddenly stops.

As you said that I have not any way of telling Windows to report any uncaught exception on the service, I have not any way to discover when the service stops. I wish the main try-catch block could do the trick but it did not log anything when the service stops due to error.

Thank you very much.

Kind regards.

Jayme Jeffman

Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: How to add Windows event when a service stops by error  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Aug 7, 2017 2:37 PM   in response to: Jayme Jeffman in response to: Jayme Jeffman
Jayme Jeffman wrote:

As a matter of fact I have enclosed the method which is executed by
the service in a try-catch block.

There are many things inside of TService that are already wrapped in
try/except blocks, which call TService::LogMessage() when uncaught
exceptions are thrown:

- TServiceApplication::RegisterServices(), which (un)installs the
service.

- The TService::AfterInstall event handler.

- TService::DoStart(), which is called at startup to create and start a
service's main worker thread (TServiceThread) and wait for it to
terminate.

- TServiceThread::Execute(), which calls the TService::OnStart and
TService::OnExecute event handlers.

- TServiceThread::ProcessRequests() when calling TService event
handlers.

- any internal VCL UI message handler, as TServiceApplication
internally assigns an event handler to the
Vcl::Forms::TApplication::OnException event.

The method belongs to a DataModule which contains the ODAC components
and the database tasks. Inside the method itself there are also try-
catch blocks on every code where could happen any process error, and
the errors are being logged to a file.

And, are you seeing those log messages? Are you sure your logging is
not throwing exceptions that kill the service? I don't see you trying
to catch log errors in your code (unless that is being handled inside
of FLog->AddLogEntry()).

On a side note, if TMDMColetor::ServiceExecute() is being called by
itself inside of the TService::OnExecute event, then your service will
not respond to SCM requests. An OnExecute event handler needs to make
explicit calls to TService::ServiceThread::ProcessRequests() to keep
the service responsive. Otherwise, move the call to
TMDMColetor::ServiceExecute() to a worker thread that is started in the
TService:::OnStart event and terminated in the
TService::OnStop/Shutdown events.

And, in any case, definately DO NOT DO NOT DO NOT call exit()!!! If
the service is responsive to the SCM, it will stop itself normally when
requested. If the service wants to stop itself internally, the correct
way is to call ServiceController(SERVICE_CONTROL_STOP) or
ServiceThread->Terminate(), or just let the TService::OnExecute event
handler exit.

The service stops sometimes and I do not know why.

That is what debugging is for.

The service works fine for more than a whole day or even a week and
then it suddenly stops.

That can only happen if something external is requesting it to stop
(unlikely, but possible), or your code is crashing and you are not
catching it (more likely).

As you said that I have not any way of telling Windows to report any
uncaught exception on the service, I have not any way to discover
when the service stops.

That is not true. In fact, if you were letting TService handle
uncaught exceptions, it would be logging them for you.

I wish the main try-catch block could do the trick

TService is heavily threaded, so any crashes are likely to occur in
worker threads, not in the main thread. The only code that runs in the
main thread is when (un)installing the service, or creating the
TService object during service start and then starting a worker thread
that then starts service message dispatching.

--
Remy Lebeau (TeamB)
Jayme Jeffman

Posts: 20
Registered: 3/2/98
Re: How to add Windows event when a service stops by error  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Aug 8, 2017 6:42 AM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Remy Lebeau (TeamB) wrote:

There are many things inside of TService that are already wrapped in
try/except blocks, which call TService::LogMessage() when uncaught
exceptions are thrown:

- TServiceApplication::RegisterServices(), which (un)installs the
service.

- The TService::AfterInstall event handler.

- TService::DoStart(), which is called at startup to create and start a
service's main worker thread (TServiceThread) and wait for it to
terminate.

- TServiceThread::Execute(), which calls the TService::OnStart and
TService::OnExecute event handlers.

- TServiceThread::ProcessRequests() when calling TService event
handlers.

- any internal VCL UI message handler, as TServiceApplication
internally assigns an event handler to the
Vcl::Forms::TApplication::OnException event.

As the service starts without any error the blocks you have mentioned are not responsible for any service stop but, of course, the loop code I have made is the only responsible for the stops.

The method belongs to a DataModule which contains the ODAC components
and the database tasks. Inside the method itself there are also try-
catch blocks on every code where could happen any process error, and
the errors are being logged to a file.

And, are you seeing those log messages? Are you sure your logging is
not throwing exceptions that kill the service? I don't see you trying
to catch log errors in your code (unless that is being handled inside
of FLog->AddLogEntry()).

Yes, I can see the messages, although the AddLogEntry method might cause errors in case of lack of user rights to write on the service folder or conversions errors from the UTF8Encode method, if somehow it can fail.


On a side note, if TMDMColetor::ServiceExecute() is being called by
itself inside of the TService::OnExecute event, then your service will
not respond to SCM requests. An OnExecute event handler needs to make
explicit calls to TService::ServiceThread::ProcessRequests() to keep
the service responsive. Otherwise, move the call to
TMDMColetor::ServiceExecute() to a worker thread that is started in the
TService:::OnStart event and terminated in the
TService::OnStop/Shutdown events.

Yes, I have found that the service do not respond to SCM requests, but I wonder if it is not a feature instead of a bug, because it prevents that the process can be easily stopped by accident. This service can only be stopped if it has finished the loop and is sleeping for a while, and this can be done by editing an ini file and setting a parameter to make it stop when the service is going to sleep.


And, in any case, definately DO NOT DO NOT DO NOT call exit()!!! If
the service is responsive to the SCM, it will stop itself normally when
requested. If the service wants to stop itself internally, the correct
way is to call ServiceController(SERVICE_CONTROL_STOP) or
ServiceThread->Terminate(), or just let the TService::OnExecute event
handler exit.

I will change it. Thank you.

The service works fine for more than a whole day or even a week and
then it suddenly stops.

That can only happen if something external is requesting it to stop
(unlikely, but possible), or your code is crashing and you are not
catching it (more likely).

Yes, for sure, as the Oracle server is connected at the beginning of the internal loop and disconnected before the service go into wait (sleep) and the Oracle server might be unavailable, of course a database error can be raised, but there is a try-catch block that catch it if happens and log a message and stops the service.

I think I will put the service to sleep again and log a message instead of stopping the service.


As you said that I have not any way of telling Windows to report any
uncaught exception on the service, I have not any way to discover
when the service stops.

That is not true. In fact, if you were letting TService handle
uncaught exceptions, it would be logging them for you.

So, why the service stops without logging anything in the Windows events nor in the log file?? If I have any uncaught exception it should be added to the windows events, isn't it ?

Kind regards,

Jayme Jeffman
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: How to add Windows event when a service stops by error  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Aug 8, 2017 10:52 AM   in response to: Jayme Jeffman in response to: Jayme Jeffman
Jayme Jeffman wrote:

the AddLogEntry method might cause errors

Then you need to handle them. The places you are calling AddLogEntry()
will kill the service if AAddLogEntry() throws an exception, since you
are not catching it.

in case of lack of user rights to write on the service folder

By default, VCL services run in the SYSTEM account, so they have full
access to the local machine. But even if you change the user account
used, you still should not be writing to the service folder anyway.
Windows sets aside special folders that are writable to users, you
should be using those folders.

or conversions errors from the UTF8Encode method, if somehow
it can fail.

Yes, it can fail, if sufficient memory is not available.

Yes, I have found that the service do not respond to SCM requests,
but I wonder if it is not a feature instead of a bug

It is by design. If no handler is assigned to the TService::OnExecute
event, TService will call ProcessRequests() internally for you, and
thus is responsive to the SCM. If you assign your own OnExecute event
handler, you are responsible for manually calling ProcessRequests()
periodically. Except for the very bare-bones of cases, you should
never assign an OnExecute event handler, use a separate worker thread
instead, as I described earlier.

This service can only be stopped if it has finished the loop and is
sleeping for a while

The best way to handle that is to simply set the TService::AllowStop
property to false while the loop is busy, and then set it back to true
when idle. That will prevent the SCM from issuing stop requests to the
service.

Otherwise, move your loop to a worker thread that is started in the
TService::OnStart event, and then have the TService::OnStop event
handler return Stopped=false (and reset the TService::Status property
back to csRunning) if the thread is busy, otherwise terminate the
thread and return Stopped=true.

and this can be done by editing an ini file and setting a parameter to
make it stop when the service is going to sleep.

You should not be using an external file to control service execution.

Yes, for sure, as the Oracle server is connected at the beginning of
the internal loop and disconnected before the service go into wait
(sleep) and the Oracle server might be unavailable, of course a
database error can be raised, but there is a try-catch block that
catch it if happens and log a message and stops the service.

There are many things that can crash, not just database errors. It is
clear that you are not catching all possible errors in your code, that
is why your service is failing at times.

--
Remy Lebeau (TeamB)
Legend
Helpful Answer (5 pts)
Correct Answer (10 pts)

Server Response from: ETNAJIVE02