【问题标题】:The specified service is marked for deletion on a delphi application指定的服务在 Delphi 应用程序上被标记为删除
【发布时间】:2013-06-28 09:13:50
【问题描述】:

我编写了一个Delphi应用程序(基本上是一个用于管理服务的GUI,它具有以下功能:允许用户设置服务使用的一些参数以及启动/停止/unintsall/安装新版本)。 因此,在所有功能中,有一个“行为不正常”:在某个时候,应用程序会尝试卸载并安装新版本的 a 服务。

使用ShellExecute 我运行以下命令:

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

如果服务已经在运行,它会被成功卸载(我收到“成功卸载”消息),但如果我打开 services.msc,我会看到 myService 仍在服务列表中,但它的启动和停止已禁用popupmenu(虽然我希望它根本不列出)。

此时,如果我尝试安装该服务,我会收到以下错误: "指定的服务被标记为删除"

请注意,如果我从命令提示符运行卸载和安装命令,则卸载正常,并且该服务不在 services.msc 列表中。注意:在这种情况下,我的意思是根本不使用 Delphi(或编译的 exe)。

我尝试了许多技巧,包括在卸载后放置 Sleep(10000),但它不起作用我还尝试关闭 services.msc(我读到它可能是打开它的问题)。

我通过以下步骤找到了一个成功的技巧:

1) 我在从 Delphi 调用 Uninstall 后放置了一个断点

2)我去 services.msc :该服务仍在列表中,即使在“刷新”之后它仍然在列表中

3) 我中断(从 IDE:CTRL+F2)应用程序的执行

4)我再次进入 services.msc 我点击“刷新”按钮:myservice 已从列表中删除,因为它应该是

所以我怀疑 Delphi XE2(在 IDE 中调试或运行 exe)以某种方式“锁定服务”不允许它被完全卸载。

注意:该服务是使用另一个 delphi 项目构建的!

您能帮我理解为什么ShellExecute 进行的服务卸载确实会出现此错误吗?

非常感谢。

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

【问题讨论】:

  • 在卸载之前停止服务 (net stop "yourservicename")
  • 是的,即使不使用 net stop,但使用 WinSvc 方式,我也会停止它
  • 可以直接调用服务管理器的API,为什么还要使用shell execute?使用 SvCom 将使服务 exe 能够通过使用 service.exe /uninstall 进行(卸载)安装,并且它会调用适当的 API 来执行此操作。请注意,尽管从未尝试(卸载)安装我们从 IDE 运行的服务之一。
  • 如果还有其他进程具有该服务的当前句柄,则该服务被标记为删除。仔细检查您的代码并关闭您从服务管理器获得的所有句柄。我知道我在 msdn 文档中读过一次有关此内容的信息,但我现在正在使用手机。稍后我会尝试回复并做出正确的回答。
  • @user193655,我写的更多。这能回答你的问题吗?

标签: delphi service delphi-xe2 uninstallation


【解决方案1】:

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

您可以通过调用DeleteService 来卸载服务。在备注部分中写道:

DeleteService 函数将服务标记为从服务控制管理器数据库中删除。直到通过调用 CloseServiceHandle 函数关闭服务的所有打开句柄并且服务未运行时,才会删除数据库条目。通过使用 SERVICE_CONTROL_STOP 控制代码调用 ControlService 函数来停止正在运行的服务。如果无法停止服务,则在系统重新启动时删除数据库条目。

因此,它必须停止并且您应该关闭所有句柄。下面的代码应该可以解决问题:

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),但为了测试这段代码是否适合您,我认为最好将其编写在一个简单的解决方案中。最后一点,不要忘记进程需要足够的权限才能成功执行此代码。

【讨论】:

  • 完美,成功了!使用相同的功能。非常感谢!
  • 不客气。谢谢你的300分。我现在觉得自己很有男子气概 :-) 在更重要的一点上,我刚刚注意到上面代码 sn-p 中的一个非常重要的错误。我更正了,请复制。重复直到块中的超时比较是错误的。如果您的服务及时停止,您可能不会注意到这一点。否则你将永远处于循环中,这是很长一段时间。
  • 感谢更新,我会检查它。你应该得到 300 分,这就是 StackOverflow 的工作原理!
【解决方案2】:

我认为您的命令提示符具有提升的权限,因此可以实际停止服务。 Delphi 可能不是,或者至少您的项目不是,因此允许卸载服务(这只不过是从注册表中删除一些值),但它实际上不能停止服务。

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

如果我猜对了,那么解决方案就是以管理员身份运行您的程序(本质上是一个安装程序),因此它也有权立即停止和删除该服务。

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

【讨论】:

  • 哦,我忘了提到我正在使用 IDE 和命令提示符“作为管理员”。我会更新问题。
  • 是的,我是这么认为的。但是我认为您的项目没有继承这些管理员权限,因此即使您的 IDE 以管理员身份运行,您自己的程序,即使从 IDE 启动,也不会。不过,这不是 100% 确定的。
  • 由提升的 IDE 启动的程序继承了提升
  • @DavidHeffernan 感谢您的澄清。真的很奇怪,ShellExecute 的行为与从提示符运行命令不同。
  • 我也会尝试使用“net stop”并让您知道
猜你喜欢
  • 2016-12-30
  • 2013-12-31
  • 2016-01-22
  • 2011-05-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多