Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

The specified service is marked for deletion on a delphi application

I write a Delphi application (basically a GUI for managing a service, it has the following features: allow the user to set some parameters that are used by the service and start/stop/unintsall/install new version). So among all the features there is one that "doesn't behave properly": at a certain point the app tries to uninstall and install a new version of the a service.

With ShellExecute I run the following commands:

C:\myPath\myService.exe /Uninstall
C:\myPath\myService.exe /Install  // this is tipically done to install a newer version of it

In case the service is already running it is uninstalled succesfully (i get "succesfully uninstalled" message), but if I open services.msc I see that myService is still in the list of services but with Start and Stop disabled from its popupmenu (while I would like it is not listed at all).

At this moment if I try to install the service I get the following error: "The specified service is marked for deletion"

Note that if i run the uninstall and install commands from the command prompt the uninstalling is fine and the service is not in services.msc list. Note: in this case I mean not using Delphi (or compiled exe) at all.

I tried with many tricks, including putting a Sleep(10000) after uninstallation but it didn't work i also tried by keeping services.msc closed (as i read it could be a problem to leave it opened).

I found a succesful trick using the following steps:

1) I put a breakpoint just after calling Uninstall from Delphi

2) I go to services.msc : the service is still in the list, even after "Refresh" it is stil in the list

3) I break (from the IDE: CTRL+F2) the exceution of the application

4) I go again in services.msc I click the "refresh" button : myservice is removed from the list as it should be

So i suspect Delphi XE2 (either debugging in the IDE or running the exe) is somehow "locking the service" not allowing it to be totally uninstalled.

NOTE: The service is built using another delphi project!

Could you help me understanding why service uninstallation made by ShellExecute does give this error?

Thanks a lot.

IMPORTANT: i forgot to mention that i use IDE and cmd.exe as admin.

like image 306
LaBracca Avatar asked Mar 23 '23 09:03

LaBracca


1 Answers

I have had a similar experience. In my code it turned out I used a variable to keep an open connection to the service control manager. Nowadays, I declare all handles as local variables and services install and uninstall on the fly.

You can uninstall a service by calling DeleteService. In the remarks section it reads:

The DeleteService function marks a service for deletion from the service control manager database. The database entry is not removed until all open handles to the service have been closed by calls to the CloseServiceHandle function, and the service is not running. A running service is stopped by a call to the ControlService function with the SERVICE_CONTROL_STOP control code. If the service cannot be stopped, the database entry is removed when the system is restarted.

Thus, it must be stopped and you should close all handles. The code below should do the trick:

function  UninstallService(aServiceName: String; aTimeOut: Cardinal): Boolean;
var
    ComputerName: array[0..MAX_COMPUTERNAME_LENGTH + 1] of Char;
    ComputerNameLength, StartTickCount: Cardinal;
    SCM: SC_HANDLE;
    ServiceHandle: SC_HANDLE;
    ServiceStatus: TServiceStatus;

begin
    Result:= False;

    ComputerNameLength:= MAX_COMPUTERNAME_LENGTH + 1;
    if (Windows.GetComputerName(ComputerName, ComputerNameLength)) then
    begin
        SCM:= OpenSCManager(ComputerName, nil, SC_MANAGER_ALL_ACCESS);
        if (SCM <> 0) then
        begin
            try
                ServiceHandle:= OpenService(SCM, PChar(aServiceName), SERVICE_ALL_ACCESS);
                if (ServiceHandle <> 0) then
                begin

                    // make sure service is stopped
                    QueryServiceStatus(ServiceHandle, ServiceStatus);
                    if (not (ServiceStatus.dwCurrentState in [0, SERVICE_STOPPED])) then
                    begin
                        // Stop service
                        ControlService(ServiceHandle, SERVICE_CONTROL_STOP, ServiceStatus);
                    end;

                    // wait for service to be stopped
                    StartTickCount:= GetTickCount;
                    QueryServiceStatus(ServiceHandle, ServiceStatus);
                    if (ServiceStatus.dwCurrentState <> SERVICE_STOPPED) then
                    begin
                        repeat
                            Sleep(1000);
                            QueryServiceStatus(ServiceHandle, ServiceStatus);
                        until (ServiceStatus.dwCurrentState = SERVICE_STOPPED) or ((GetTickCount - StartTickCount) > aTimeout);
                    end;

                    Result:= DeleteService(ServiceHandle);
                    CloseServiceHandle(ServiceHandle);
                end;
            finally
                CloseServiceHandle(SCM);
            end;
        end;
    end;
end;

I would chop the code above in a few sub functions (i.e. QueryServiceStatus, StopService and UninstallService), but for testing whether this code works for you I thought it best to write it in one simple solution. On a final note, don't forget the process needs sufficient rights to execute this code successfully.

like image 176
SpaghettiCook Avatar answered Apr 13 '23 21:04

SpaghettiCook