Системное программирование в среде Windows
Шрифт:
Функция возвращает значение TRUE, если регистрация службы прошла успешно. Если служба уже выполняется или возникают проблемы с обновлением записей реестра (HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services), функция завершается с ошибками, обработка которых может осуществляться обычным путем.
Основной поток процесса службы, которая вызывает функцию StartService-ControlDispatcher, связывает поток с SCM. SCM регистрирует службу с вызывающим потоком в качестве потока диспетчера службы. SCM не осуществляет возврата в вызывающий поток до тех пор, пока не завершат выполнение все службы. Заметьте, однако, что фактического запуска логических служб в этот момент не происходит;
Типичная основная программа службы, соответствующая случаю единственной логической службы, представлена в программе 13.1.
Функции ServiceMain
Эти функции, которые указываются в таблице диспетчеризации, фигурирующей в программе 13.1, представляют логические службы. По сути, эти функции являются усовершенствованными версиями основной программы, преобразуемой в службу, и каждая логическая служба будет активизироваться в ее собственном потоке SCM. В свою очередь, логическая служба может запускать дополнительные потоки, например, рабочие потоки сервера, которые использовались в программах serverSK и serverNP. Часто внутри службы существует только одна логическая служба. Логическая служба в программе 13.2 получена путем соответствующей адаптации основного сервера из программы 12.2. В то же время, логические службы на основе сокетов и именованных каналов могут выполняться в рамках одной и той же службы Windows, что потребует предоставления основных функций обеих служб.
Несмотря на то что функция ServiceMain является адаптированным вариантом функции main с ее параметрами, представляющими количество аргументов и содержащую их строку, между ними имеется одно незначительное отличие: функция службы должна быть объявлена с типом void, а не иметь возвращаемое значение типа int, как в случае обычной функции main.
Для регистрации обработчика управляющих команд службы, который представляет собой функцию, вызываемую SCM для осуществления управления службой, требуется дополнительный код.
Регистрация управляющей программы службы
Обработчик управляющих команд
lpServiceName — определяемое пользователем имя службы, которое предоставляется в соответствующем поле таблицы диспетчеризации, отведенном для данной логической функции.
lpHandlerProc — адрес функции расширенного обработчика, которая описывается в следующем разделе. Расширенный обработчик был добавлен в NT5, причем функция RegisterServiceCtrlHandlerEx заменяет функцию Register-ServiceCtrlHandler. Следующий параметр также был введен в NT5.
lpContext — определяемые пользователем данные, передаваемые обработчику. Благодаря этому обработчик может различать ассоциированные с ним службы, которых может быть несколько.
В случае ошибки возвращаемое функцией значение, которым является объект SEPARARE_STATUS_HANDLE, равно 0, а для анализа ошибок могут быть использованы обычные методы.
Настройка состояния службы
Теперь, когда управляющая программа зарегистрирована, необходимо сразу же перевести службу в состояние SERVICE_START_PENDING, воспользовавшись для этого функцией SetServiceStatus. Функция SetServiceStatus будет применяться еще в других местах для установки различных значений параметра состояния, информируя SCM о текущем состоянии службы. Описания других возможных состояний службы, характеризуемых значениями параметра состояния, отличными от SERVICE_STATUS_PENDING, приведены в табл. 13.3.
Обработчик службы должен устанавливать состояние службы при каждом вызове, даже если ее состояние не менялось.
Далее, любой из потоков службы может в любой момент вызвать функцию SetServiceStatus, чтобы сообщить данные, характеризующие степень выполнения задачи, а также предоставить информацию об ошибках или иную информацию, причем для периодического обновления состояния многие службы часто выделяют отдельный поток. Длительность временного промежутка между вызовами обновления состояния указывается в одном из полей структуры данных, выступающей в качестве параметра. Если в пределах указанного промежутка времени состояние не обновлялось, то SCM может предположить, что произошла ошибка.
hServiceStatus — дескриптор типа SERVICE_STATUS_HANDLE, возвращенный функцией RegisterCtrlHandlerEx. Поэтому вызову функции SetServiceStatus должен предшествовать вызов функции RegisterCtrlHandlerEx.
lpServiceStatus — указатель на структуру SERVICE_STATUS, содержащую описание свойств, состояния и возможностей службы.