2013-06-28 38 views
3

我写Delphi应用程序(基本上是为了管理的服务的图形用户界面,它具有以下特征:允许用户设置了由服务使用的一些参数和启动/停止/取消安装/安装新版本)。 因此,所有的功能中有一个“不检点”:在某一点上的应用程序会尝试卸载和安装服务的新版本。指定的服务标记为删除上Delphi应用程序

随着ShellExecute我运行以下命令:

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

如果该服务已经在运行它成功地卸载了(我得到“成功地卸载”消息),但如果我打开SERVICES.MSC我看到我的服务仍然在服务列表中,但从其popup菜单中禁用了启动和停止(虽然我希望它没有列出)。

在这一刻,如果我尝试安装,我得到以下错误的服务: “指定的服务被标记为删除”

需要注意的是,如果我运行卸载和命令安装命令提示卸载很好,服务不在services.msc列表中。注意:在这种情况下,我的意思是根本不使用Delphi(或编译的exe)。

我尝试了很多技巧,包括卸载后放置一个Sleep(10000),但它没有工作我也试图保持services.msc关闭(因为我读它可能是一个问题,让它打开)。

我发现了一个成功的窍门使用以下步骤:

1)我把一个断点刚刚从德尔福

调用卸载后

2)我去SERVICES.MSC:该服务仍处于列表,即使在“刷新”是STIL列表

3)我打破(从IDE:CTRL + F2)应用

4)我在SERVICES.MSC再去我点击的exceution “刷新”按钮:因为它,myservice将从列表中删除应该是

所以我怀疑德尔福XE2(无论是在IDE中调试或运行EXE)以某种方式“锁定服务”,不允许它被完全卸载。

注:该服务使用另一个德尔福项目建成!

你能帮助我理解由ShellExecute做确实给这个错误,为什么服务卸载?

非常感谢。

重要提示: 我忘了提及我使用IDE和cmd.exe作为管理员。

+2

停止该服务,然后将其卸载(净停止“yourservicename”) – whosrdaddy

+0

是的,我停止它,即使不使用净停止,但使用WinSvc方式 – LaBracca

+0

也与净停止我得到了相同.... – LaBracca

回答

3

我有过类似的经历。在我的代码中,事实证明我使用了一个变量来保持与服务控制管理器的开放连接。现在,我将所有句柄声明为局部变量,并且将服务安装和卸载。

您可以通过调用DeleteService来卸载服务。在备注部分中,它的内容如下:

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.

因此,必须停止并且应关闭所有手柄。下面的代码应该做的伎俩:

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; 

我就砍上面的代码中的几个子功能(即QueryServiceStatus,StopService和UninstallService),但用于测试该代码是否适用于你,我想最好还是写它在一个简单的解决方案。最后一点,不要忘记进程需要足够的权限才能成功执行此代码。

+0

完美,这工作!使用这个相同的功能。非常感谢! – LaBracca

+1

不客气。感谢您的300分。我现在觉得非常有把握:-)更重要的一点是,我刚刚注意到上面代码片段中的一个重要的错误。我纠正它,请复制。重复直到块的超时比较是错误的。如果您的服务及时停止,您可能不会注意到这一点。否则,你将永远处于循环之中,这是一段很长的时间。 – SpaghettiCook

+0

谢谢您的更新,我会检查它。你应得的300分,这是StackOverflow的工作原理! – LaBracca

2

我觉得你的命令提示符已提升的权限,并为此是允许真正停止该服务。德尔福可能没有,或者至少你的项目是不是,所以它允许卸载服务(这是不是删除从注册表中的一些值更小),但它实际上并不能停止该服务。

该服务然后'标记为删除',因为它已被卸载,但仍在运行。如果你重新启动你的电脑,服务将不会再次启动,你的工具可以安装新版本。

如果我猜对了,那么解决方案就是运行你的程序 - 本质上是一个安装程序 - 作为管理员,所以它也有权立即停止和删除服务。

您可能会尝试的另一件事是先致电net stop <service>停止服务,但我怀疑这是否能解决问题。

+0

哦,我忘了提及,我正在使用IDE和命令提示符“作为管理员”。我会更新这个问题。 – LaBracca

+0

是的,我认为是。但我认为你的项目不会继承这些管理员权限,所以即使你的IDE以管理员身份运行,你自己的程序,即使从IDE启动,也不会。虽然不是100%确定的。 – GolezTrol

+2

由提升的IDE启动的程序继承了高程 –

相关问题