最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

winapi - Delphi Windows service stuck at START_PENDING - Stack Overflow

programmeradmin4浏览0评论

I'm using Delphi 11 and I'm trying to learn about Windows services.

I've created a very basic minimalistic service that should basically just start and sleep in a loop until stopped. I got most of the code directly from Embarcadero's blog "Developing Windows Services in Windows 11: Best Practices and Tools". But when I install the service with this Powershell command with Administrator access:

sc create TestSvc binPath= "C:\Actual\Path\TestSvc.exe" start= demand

and then try to run it via sc start TestSvc the outcome is:

SERVICE_NAME: TestSvc
        TYPE               : 10  WIN32_OWN_PROCESS
        STATE              : 2  START_PENDING
                                (NOT_STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN)
        WIN32_EXIT_CODE    : 0  (0x0)
        SERVICE_EXIT_CODE  : 0  (0x0)
        CHECKPOINT         : 0x0
        WAIT_HINT          : 0x7d0
        PID                : 7292
        FLAGS              :

And it stays forever stuck in the START_PENDING state, while the Task Manager keeps showing it as:

Starting

My TService1 class has only these few methods:

procedure ServiceController(CtrlCode: DWord); stdcall;
begin
  Service1.Controller(CtrlCode);
end;

function TService1.GetServiceController: TServiceController;
begin
  Result := ServiceController;
end;

procedure TService1.ServiceContinue(Sender: TService; var Continued: Boolean);
begin
  Continued := True;
end;

procedure TService1.ServiceExecute(Sender: TService);
begin
  while not Terminated do
  begin
    TThread.Sleep(1000);
  end;
end;

procedure TService1.ServicePause(Sender: TService; var Paused: Boolean);
begin
  Paused := True;
end;

procedure TService1.ServiceStart(Sender: TService; var Started: Boolean);
begin
  //  TFile.AppendAllText('log.txt', 'ServiceStart tries to initialize PeriodicThread...')
  Started := True;
end;

procedure TService1.ServiceStop(Sender: TService; var Stopped: Boolean);
begin
  Stopped := True;
end;

The ServiceStart(), ServiceStop(), ServicePause(), ServiceContinue() and ServiceExecute() methods are all correctly hooked to corresponding events of TService1 (I can see them in the Object Inspector just fine). But when I try to add a line that would write a message to a log file (now commented out), the message is never written. It's almost like the ServiceStart() method never gets executed and the service just keeps hanging there in some limbo without even trying to start.

What am I missing?


Edit:

To clarify: I used the article Developing Windows Services in Windows 11: Best Practices and Tools to guide me.

I did try the version with the ServiceThread.ProcessRequests(false); line in the ServiceExecute() loop, and I did try even adding the FBackgroundThread, but the results were always exactly the same.

I'm using Delphi 11 and I'm trying to learn about Windows services.

I've created a very basic minimalistic service that should basically just start and sleep in a loop until stopped. I got most of the code directly from Embarcadero's blog "Developing Windows Services in Windows 11: Best Practices and Tools". But when I install the service with this Powershell command with Administrator access:

sc create TestSvc binPath= "C:\Actual\Path\TestSvc.exe" start= demand

and then try to run it via sc start TestSvc the outcome is:

SERVICE_NAME: TestSvc
        TYPE               : 10  WIN32_OWN_PROCESS
        STATE              : 2  START_PENDING
                                (NOT_STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN)
        WIN32_EXIT_CODE    : 0  (0x0)
        SERVICE_EXIT_CODE  : 0  (0x0)
        CHECKPOINT         : 0x0
        WAIT_HINT          : 0x7d0
        PID                : 7292
        FLAGS              :

And it stays forever stuck in the START_PENDING state, while the Task Manager keeps showing it as:

Starting

My TService1 class has only these few methods:

procedure ServiceController(CtrlCode: DWord); stdcall;
begin
  Service1.Controller(CtrlCode);
end;

function TService1.GetServiceController: TServiceController;
begin
  Result := ServiceController;
end;

procedure TService1.ServiceContinue(Sender: TService; var Continued: Boolean);
begin
  Continued := True;
end;

procedure TService1.ServiceExecute(Sender: TService);
begin
  while not Terminated do
  begin
    TThread.Sleep(1000);
  end;
end;

procedure TService1.ServicePause(Sender: TService; var Paused: Boolean);
begin
  Paused := True;
end;

procedure TService1.ServiceStart(Sender: TService; var Started: Boolean);
begin
  //  TFile.AppendAllText('log.txt', 'ServiceStart tries to initialize PeriodicThread...')
  Started := True;
end;

procedure TService1.ServiceStop(Sender: TService; var Stopped: Boolean);
begin
  Stopped := True;
end;

The ServiceStart(), ServiceStop(), ServicePause(), ServiceContinue() and ServiceExecute() methods are all correctly hooked to corresponding events of TService1 (I can see them in the Object Inspector just fine). But when I try to add a line that would write a message to a log file (now commented out), the message is never written. It's almost like the ServiceStart() method never gets executed and the service just keeps hanging there in some limbo without even trying to start.

What am I missing?


Edit:

To clarify: I used the article Developing Windows Services in Windows 11: Best Practices and Tools to guide me.

I did try the version with the ServiceThread.ProcessRequests(false); line in the ServiceExecute() loop, and I did try even adding the FBackgroundThread, but the results were always exactly the same.

Share Improve this question edited Feb 7 at 20:46 AmigoJack 6,0991 gold badge19 silver badges34 bronze badges asked Feb 7 at 13:00 happyNoobhappyNoob 653 bronze badges 4
  • 1 It would help if you linked to the blog post your following rather than expecting others to find the resource you're using. If, for instance, you're following this one I believe an important part you're missing is the call to ProcessRequests inside the ServiceExecute method. – Damien_The_Unbeliever Commented Feb 7 at 13:43
  • @Damien_The_Unbeliever edited the question to clarify that, yes, that is the post I used, and yes, I did try to have that line there and it didn't help. – happyNoob Commented Feb 7 at 16:08
  • 1 Don't implement your service in the SCM thread, start a new thread from the onstart event. Check out my answer here: stackoverflow.com/a/10538102/800214 – whosrdaddy Commented Feb 8 at 13:59
  • @whosrdaddy I did try this too, creating new thread in OnStart event. The same thing happened - stuck at START_PENDING, the code inside ServiceStart method never got executed (not even a basic logging code I put on first line). Since the service won't ever even reach the OnStart event - at least it seems so - nothing I can put in there seems to change anything. – happyNoob Commented 2 days ago
Add a comment  | 

2 Answers 2

Reset to default 9

TService sets its status to csStartPending before starting its worker ServiceThread. That worker thread then triggers the OnStart and OnExecute events, setting the status to csRunning after OnStart and before OnExecute.

So, getting stuck in a START_PENDING state would imply that TService is not able to run its ServiceThread at all, which should be impossible under normal conditions.

The only way the ServiceThread could freeze before the OnStart event is triggered is if Application.DelayInitialize is true (it is false by default), in which case TServiceThread calls Application.Initialize() before triggering OnStart. So, if something inside of Application.Initialize() would freeze, you would get the behavior you describe.

I've used TService for over 20 year and have never seen it not run its ServiceThread or trigger events. So, you are just going to have to debug your service at runtime to see where it is actually freezing up.


That being said, your OnExecute event handler is not processing any subsequent SCM requests after the OnStart event. When you assign an OnExecute handler, you become responsible for handling SCM requests and status updates, eg:

procedure TService1.ServiceExecute(Sender: TService);
begin
  while not Terminated do
  begin
    ServiceThread.ProcessRequests(false); // <-- ADD THIS!
    TThread.Sleep(1000);
  end;
end;

If you simply get rid of the OnExecute handler completely, then TService will process all SCM requests for you.

When you are ready to have your service do actual work, it is generally easier to just avoid the OnExecute event for this very reason. Instead, have your OnStart handler launch a worker thread for your business logic, and then have your OnStop and OnShutdown handlers terminate that thread. Let TService deal with the SCM, you focus on your business logic.

So I found the solution, even though I'm not 100% sure why it works. When Remy Lebeau pointed out it has to be an initialization issue, I went through dpr file and noticed this bit:

// Application.DelayInitialize := True;
//
if not Application.DelayInitialize or Application.Installing then
  Application.Initialize;

As Remy said, DelayInitialize should be false by default and this bit doesn't suggest otherwise (the line that would set it True has been commented out since the begining), so initialization should happen. But being completely out of any better ideas, I decided to inspect the Installing part of the condition. I found it simply ment whether the service exe was run with /install parameter or not. So I tried that. I used

C:\Actual\Path\TestSvc.exe /install

instead of

sc create TestSvc binPath= "C:\Actual\Path\TestSvc.exe" start= demand

and now I can start it by sc start Service1 without an issue. I later added a background thread to do any work and got rid of ServiceExecute just as suggested and it keeps working fine. So for whatever reason, using different command to install the service did the trick.

I kept asking around and someone said it appears to be a "known issue" with Delphi and TServiceThread, though I couldn't find any mention of it anywhere online.

发布评论

评论列表(0)

  1. 暂无评论