有时候我卸载使用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
成功。我还发现 ChangeServiceConfig
返回的错误代码是 ERROR_SERVICE_MARKED_FOR_DELETE
。因此,以下是通过 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应该能够为您处理此操作。