有时,当我卸载安装程序(使用 WIX 制作)时,服务仍然标记为删除,用户必须重新启动机器才能再次安装。如何验证该服务是否被标记为删除并告诉用户在进行其他安装之前重新启动计算机?
6 回答
一般来说,当某些东西仍然锁定在该服务上时,就会发生这种情况,从而阻止 Windows 删除其在注册表中的配置。(在大多数情况下,它只是服务小程序——services.msc——在后台意外打开。)
对于检测,我建议您阅读CreateService和其他Service API。例如,如果服务被标记为删除,您将在调用 CreateService 时收到 ERROR_SERVICE_MARKED_FOR_DELETE。
关于您提出的重新启动解决方案... Windows 已经足够先进,几乎不需要任何原因重新启动。除非您正在安装专门的内核驱动程序,否则您不需要重新启动。不要偷懒!牢记用户!我建议更改您的安装程序逻辑以检测可能存在冲突的正在运行的程序,例如服务小程序,并建议关闭。
这是一个可以帮助您的SO帖子。虽然最初的问题是关于服务安装的,但答案也包括卸载和状态。
这是一篇文章,解释了为什么您可能会首先收到“标记为删除”消息以及如何解决它。
编辑
根据 Christopher Painter 的评论,我正在更新此答案以促进最佳实践。虽然收到“标记为删除”消息(根据我的经验)更多是拥有 services.msc 控制台而不是未发布资源的结果,但编写自定义操作以重新启动并不是最好的方法。
要在 WiX 处理后安排重启,请使用 WiX XML(在此处解释如何使用 Wix# ),如下所示:
<?xml version='1.0' encoding='windows-1252'?>
<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>
...
<InstallExecuteSequence>
<ScheduleReboot After="InstallFinalize"/>
<InstallExecuteSequence>
</Wix>
我找不到 API 方法来做到这一点(这不涉及调用CreateService
或DeleteService
,两者都有不希望的副作用),但
HKLM\SYSTEM\CurrentControlSet\Services\ServiceName
包含DeleteFlag=1
(REG_DWORD) 值似乎很能说明这种不幸的状态。
我还搜索了确定服务是否存在或标记为删除(即使OpenService
成功)的解决方案。我还发现ERROR_SERVICE_MARKED_FOR_DELETE
返回的错误代码是ChangeServiceConfig
. ChangeServiceConfig
因此,这是通过C/C++检查服务是否标记为删除的方法:
BOOL SetDisplayName(SC_HANDLE schService, LPCTSTR lpDisplayName)
{
return ChangeServiceConfig(schService, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, lpDisplayName);
}
//
// You need to obtain the hSCManager through OpenSCManager first
//
SC_HANDLE schService = OpenService(hSCManager, pszServiceName, SERVICE_CHANGE_CONFIG);
if (schService != nullptr)
{
//
// obtain the display name of a service
//
TCHAR szDisplayName[0x1000] = { 0 };
DWORD cchDisplayName = ARRAYSIZE(szDisplayName);
bResult = GetServiceDisplayName(schService, pszServiceName, szDisplayName, &cchDisplayName);
if (bResult)
{
//
// change the display name to the original display name
//
bResult = SetDisplayName(schService, szDisplayName);
if (!bResult)
{
DWORD dwError = GetLastError();
switch (dwError) {
case ERROR_ACCESS_DENIED:
case ERROR_CIRCULAR_DEPENDENCY:
case ERROR_DUPLICATE_SERVICE_NAME:
case ERROR_INVALID_SERVICE_ACCOUNT:
break;
case ERROR_INVALID_HANDLE:
break;
case ERROR_INVALID_PARAMETER:
break;
//
// the service is marked for deletion
//
case ERROR_SERVICE_MARKED_FOR_DELETE:
break;
default:
break;
}
}
}
}
else
{
DWORD dwError = GetLastError();
switch (dwError) {
case ERROR_ACCESS_DENIED:
break;
case ERROR_INVALID_HANDLE:
break;
case ERROR_INVALID_NAME:
break;
case ERROR_SERVICE_DOES_NOT_EXIST:
break;
default:
break;
}
}
您是否在卸载期间使用 ServiceControl 元素/表来停止服务?如果是这样,您的服务是否成功停止?如果没有,请查看您的服务内部发生了什么,以确保它释放所有资源并在请求时正常关闭。
您无需编写任何自定义操作即可以编程方式调用 SCM API。Windows Installer 应该能够为您处理这个问题。
在我的情况下,该服务在卸载后被标记为删除,因为我没有正确处理对象(在我的情况下是rabbitmq-connection)。
这不是问题的直接答案,但可能有助于解决问题的根本问题。