In order to use a loop correctly in the OnExecute event, you must call ProcessRequests() with WaitForMessage=false. Using WaitForMessage=true, ProcessRequests() will not return until the service is stopped, and the stop will set Terminated=true, thus making such a loop useless.
If you want to prevent the service from being stopped at all while the TProcess is busy, then you can do one of the following:
when starting the TProcess, set the service's AllowStop property to false and call the ReportStatus() to update the SCM, and then once the TProcess has finished you can set AllowStop back to true and call ReportStatus() again.
leave AllowStop set to true, and have the OnStop event return Stopped=false (and set the service's Win32ErrCode or ErrCode property accordingly) if the TProcess is currently busy.
On the other hand, if you want the service to stop normally but wait for the TProcess to finish, then you can handle that in the OnStop event. The service's Status is already csStopPending when the OnStop event is entered. Simply run a loop inside of the OnStop event to call ReportStatus() at regular intervals (not exceeding the WaitHint property) while the TProcess is busy so the SCM knows the service is not frozen. Once the TProcess has finished, then OnStop can return Stopped=true, which will update the SCM with a status of csStopped.
As I explained earlier, the OnExecute code you have shown is basically useless and should be eliminated completely.
I would not use that approach. The init code should be in the OnStart event. The termination code should be in OnStop event. The OnExecute event should be eliminated whenever possible.
Yes, and yes.
Yes. For example:
void __fastcall TService1::ServiceStart(TObject *Sender, bool &Started)
{
TProcess->Enabled = true;
log1.Log(L"Service started");
Started = true;
}
void __fastcall TService1::ServiceStop(TObject *Sender, bool &Stopped)
{
while (TProcess->Enabled)
{
Sleep(WaitHint-100);
ReportStatus();
}
log1.Log(L"Service STOPPED");
Stopped = true;
}
void __fastcall TService1::TProcessTimer(TObject *Sender)
{
...
if (some condition)
{
TProcess->Enabled = false;
if (Status != csStopPending)
ServiceController(SERVICE_CONTROL_STOP);
}
...
}
One thing to keep in mind - you keep saying the TProcess is a "timer method". If you mean a TTimer, then such a loop in the OnStop event would block the timer from firing any further OnTimer events, unless you manually pump the service thread's message queue to dispatch WM_TIMER messages. You would have had to do that anyway in the OnExecute event, if you start the timer in the context of the service thread.
Personally, I never do any work in the service thread directly. I always have the OnStart event create a worker thread, and then have the OnStop event terminate the worker thread and wait on it. If I need to do things at regular intervals, I have the worker thread use a Waitable Timer Object or Waitable Event Object in a loop.