我编写了一个Delphi应用程序(基本上是一个用于管理服务的GUI,它具有以下功能:允许用户设置服务使用的一些参数以及启动/停止/取消安装/安装新版本)。 因此,在所有功能中,有一个“行为不正常”:在某个时刻,应用程序尝试卸载并安装新版本的服务。
使用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(虽然我希望它根本没有列出)。
此时如果我尝试安装该服务,我会收到以下错误: “指定的服务标记为删除”
请注意,如果我从命令提示符运行uninstall和install命令,则卸载很好,并且该服务不在services.msc列表中。注意:在这种情况下,我的意思是根本不使用Delphi(或编译的exe)。
我尝试了许多技巧,包括在卸载后放置Sleep(10000)
但它不起作用我也尝试通过保持services.msc关闭(因为我读到它可能是一个问题,让它打开)。< / p>
我使用以下步骤找到了成功的技巧:
1)我在从Delphi调用Uninstall之后放了一个断点
2)我去了services.msc:服务仍然在列表中,即使在“刷新”之后它仍然是列表中的stil
3)我打破(从IDE:CTRL + F2)应用程序的优势
4)我再次在services.msc中点击“刷新”按钮:myservice从列表中删除,因为它应该是
所以我怀疑Delphi XE2(在IDE中调试或运行exe)以某种方式“锁定服务”而不允许它完全卸载。
注意:该服务是使用另一个delphi项目构建的!
您能否帮助我理解为什么ShellExecute
进行的服务卸载会出现此错误?
非常感谢。
重要: 我忘了提到我使用IDE和cmd.exe作为管理员。
答案 0 :(得分:5)
我有类似的经历。在我的代码中,我发现我使用了一个变量来保持与服务控制管理器的开放连接。如今,我将所有句柄声明为本地变量和服务即时安装和卸载。
您可以致电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)中删除上面的代码,但是为了测试这段代码是否适合你,我认为最好用一个简单的解决方案来编写它。最后,请注意,进程需要足够的权限才能成功执行此代码。
答案 1 :(得分:2)
我认为您的命令提示符具有提升的权限,因此允许实际停止该服务。 Delphi可能不是,或者至少你的项目不是,所以允许卸载服务(这只是从注册表中删除一些值),但它实际上不能停止服务。
然后,该服务被“标记为删除”,因为它已经被卸载,但仍在运行。如果您重新启动电脑,该服务将无法再次启动,您的工具可以安装新版本。
如果我猜对了,那么解决方案就是运行你的程序 - 本质上是一个安装程序 - 作为管理员,所以它也有权立即停止和删除服务。
您可能尝试的另一件事是首先致电net stop <service>
停止服务,但我怀疑这是否解决了这个问题。