Состояние сервиса
Как мы уже говорили, сервис может сообщить процессу управления сервисами свое состояние, для чего он должен вызвать функцию SetServiceStatus. Прототип этой функции мы привели ниже:
BOOL SetServiceStatus(
SERVICE_STATUS_HANDLE sshServiceStatus, // идентификатор
// состояния сервиса
LPSERVICE_STATUS lpssServiceStatus); // адрес структуры,
// содержащей состояние сервиса
Через параметр sshServiceStatus функции SetServiceStatus вы должны передать идентификатор состояния сервиса, полученный от функции RegisterServiceCtrlHandler.
В параметре lpssServiceStatus вы должны передать адрес предварительно заполненной структуры типа SERVICE_STATUS:
typedef struct _SERVICE_STATUS
{
DWORD dwServiceType; // тип сервиса
DWORD dwCurrentState; // текущее состояние сервиса
DWORD dwControlsAccepted; // обрабатываемые команды
DWORD dwWin32ExitCode; // код ошибки при запуске
// и остановке сервиса
DWORD dwServiceSpecificExitCode; // специфический код ошибки
DWORD dwCheckPoint; // контрольная точка при
// выполнении длительных операций
DWORD dwWaitHint; // время ожидания
} SERVICE_STATUS, *LPSERVICE_STATUS;
В поле dwServiceType необходимо записать один из перечисленных ниже флагов, определяющих тип сервиса:
Флаг | Описание | ||
SERVICE_WIN32_OWN_PROCESS | Сервис работает как отдельный процесс | ||
SERVICE_WIN32_SHARE_PROCESS | Сервис работает вместе с другими сервисами в рамках одного и того же процесса | ||
SERVICE_KERNEL_DRIVER | Сервис представляет собой драйвер операционной системы Microsoft Windows NT | ||
SERVICE_FILE_SYSTEM_DRIVER | Сервис является драйвером файловой системы | ||
SERVICE_INTERACTIVE_PROCESS | Сервисный процесс может взаимодействовать с программным интерфейсом рабочего стола Desktop |
В поле dwCurrentState вы должны записать текущее состояние сервиса. Здесь можно использовать одну из перечисленных ниже констант:
Константа | Состояние сервиса | ||
SERVICE_STOPPED | Сервис остановлен | ||
SERVICE_START_PENDING | Сервис находится в состоянии запуска, но еще не работает | ||
SERVICE_STOP_PENDING | Сервис находится в состоянии остановки, но еще не остановился | ||
SERVICE_RUNNING | Сервис работает | ||
SERVICE_CONTINUE_PENDING | Сервис начинает запускаться после временной остановки, но еще не работает | ||
SERVICE_PAUSE_PENDING | Сервис начинает переход в состояние временной остановки, но еще не остановился | ||
SERVICE_PAUSED | Сервис находится в состоянии верменной остановки |
Задавая различные значения в поле dwControlsAccepted, вы можете указать, какие команды обрабатывает сервис. Ниже приведен список возможных значений:
Значение |
Команды,, которые может воспринимать сервис |
SERVICE_ACCEPT_STOP |
Команда остановки сервиса SERVICE_CONTROL_STOP |
SERVICE_ACCEPT_PAUSE_CONTINUE |
Команды временной остановки SERVICE_CONTROL_PAUSE и продолжения работы после временной остановки SERVICE_CONTROL_CONTINUE |
SERVICE_ACCEPT_SHUTDOWN |
Команда остановки при завершении работы операционной системы SERVICE_CONTROL_SHUTDOWN |
Поле dwServiceSpecificExitCode используется в том случае, когда в поле dwWin32ExitCode указано значение ERROR_SERVICE_SPECIFIC_ERROR.
Теперь о поле dwCheckPoint.
Это поле должно содержать значение, которое должно периодически увеличиваться при выполнении длительных операций запуска, остановки или продолжения работы после временной остановки. Если выполняются другие операции, в это поле необходимо записать нулевой значение.
Содержимое поля dwWaitHint определяет ожидаемое время выполнения (в миллисекундах) длительной операции запуска, остановки или продолжения работы после временной остановки. Если за указанное время не изменится содержимое полей dwCheckPoint или dwCurrentState, процесс управления сервисами будет считать, что произошла ошибка.
В наших примерах для сообщения текущего состояния сервиса процессу управления сервисами мы используем функцию ReportStatus, исходный текст которой приведен ниже:
void ReportStatus(DWORD dwCurrentState,
DWORD dwWin32ExitCode, DWORD dwWaitHint)
{
static DWORD dwCheckPoint = 1;
if(dwCurrentState == SERVICE_START_PENDING)
ss.dwControlsAccepted = 0;
else
ss.dwControlsAccepted = SERVICE_ACCEPT_STOP;
ss.dwCurrentState = dwCurrentState;
ss.dwWin32ExitCode = dwWin32ExitCode;
ss.dwWaitHint = dwWaitHint;
if((dwCurrentState == SERVICE_RUNNING)
(dwCurrentState == SERVICE_STOPPED))
ss.dwCheckPoint = 0;
else
ss.dwCheckPoint = dwCheckPoint++;
SetServiceStatus(ssHandle, &ss);
}
При заполнении структуры SERVICE_STATUS эта функция проверяет содержимое поля dwCurrentState. Если сервис находится в состоянии ожидания запуска, в поле допустимых команд dwControlsAccepted записывается нулевое значение. В противном случае функция записывает туда значение SERVICE_ACCEPT_STOP, в результате чего сервису может быть передана команда остановки. Далее функция заполняет поля dwCurrentState, dwWin32ExitCode и dwWaitHint значениями, полученными через параметры.
В том случае, когда сервис выполняет команды запуска или остановки, функция увеличивает значение счетчика шагов длительных операций dwCheckPoint. Текущее значение счетчика хранится в статической переменной dwCheckPoint, определенной в нашей функции.
После подготовки структуры SERVICE_STATUS ее адрес передается функции установки состояния сервиса SetServiceStatus.
Для определения текущего состояния сервиса вы можете использовать функцию QueryServiceStatus, прототип которой приведен ниже:
BOOL QueryServiceStatus(
SC_HANDLE schService, // идентификатор сервиса
LPSERVICE_STATUS lpssServiceStatus); // адрес структуры
// SERVICE_STATUS
Идентификатор сервиса вы можете получить от функций OpenService или CreateService, которые будут описаны ниже.