Иногда, когда я удаляю установку (сделанную с помощью WIX), служба остается помеченной для удаления, и пользователь должен перезагрузить компьютер для повторной установки. Как я могу убедиться, что служба помечена для удаления, и сказать пользователю, чтобы он перезагрузил компьютер перед выполнением другой установки?
Проверить, помечена ли услуга для удаления
Ответы (6)
Вообще говоря, этот сценарий возникает, когда что-то остается привязанным к этой службе, не позволяя Windows удалить свою конфигурацию в реестре. (В большинстве случаев это просто апплет служб - services.msc - случайно оставленный открытым в фоновом режиме.)
Для обнаружения я предлагаю вам прочитать CreateService и другой Service API . Например, вы получите ERROR_SERVICE_MARKED_FOR_DELETE после вызова CreateService, если служба помечена для удаления.
Что касается предложенного вами решения по перезагрузке ... Windows продвинулась достаточно далеко, чтобы не требовать перезагрузки практически по любой причине. Если вы не устанавливаете специализированные драйверы ядра, перезагрузка не требуется. Не ленитесь! Помните о пользователе! Я рекомендую изменить логику установщика, чтобы обнаруживать потенциально конфликтующие запущенные программы, такие как апплет служб, и предлагать закрытие.
Я не могу найти способ сделать это через API (который не требует вызова CreateService
или DeleteService
, оба имеют нежелательные побочные эффекты), но HKLM\SYSTEM\CurrentControlSet\Services\ServiceName
, содержащий значение DeleteFlag=1
(REG_DWORD), кажется довольно показательным для этого неудачного состояния.
Вот сообщение SO, которое может вам помочь. Хотя исходный вопрос касается установки службы, ответ также касается удалений и статусов.
Как программно установить службу Windows на C #?
Вот статья, в которой объясняется, почему вы можете получить сообщение «помечено для удаления» и как это обойти.
ИЗМЕНИТЬ
Согласно комментарию Кристофера Пейнтера, я обновляю этот ответ, чтобы продвигать передовой опыт. Хотя получение сообщения «помечено для удаления» чаще (по моему опыту) было результатом наличия консоли 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>
Я также искал решение, которое позволяет определить, существует ли служба или помечена для удаления, даже если 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 должен справиться с этим за вас.
В моем случае служба была помечена для удаления после ее удаления, потому что я неправильно удалил объект (в моем случае rabbitmq-connection).
Это не прямой ответ на вопрос, но может помочь решить его коренную проблему.
ServiceController
:ServiceController.GetServices()
, но попробовать стоит. - person Jaroslav Jandek   schedule 16.03.2011