Приложение, управляющее сервисом Код, описанный в этой статье, работает только в Windows NT / 2000 / XP, поскольку Windows 98 не поддерживает работу с сервисами.
Создание сервиса
Как правило сервис представляет собой консольное приложение, поэтому программа должна содержать функцию main().
Функция StartServiceCtrlDispatcher() связывает главный поток сервиса с менеджером сервисов (service control manager, SCM).
Когда SCM запускает сервис, он ожидает выполнения сервисом функции StartServiceCtrlDispatcher(). Главный поток сервиса должен вызвать эту функцию как можно быстрее. Если функция выполняется успешно, она связывает вызывающий ее поток с SCM и не завершается, пока не будет остановлен сервис. SCM использует это соединение, чтобы посылать сервису управляющие запросы.
Если сервис запускается как отдельный процесс (как, например, в нашем случае), он должен вызвать функцию StartServiceCtrlDispatcher() немедленно. Поэтому всю инициализацию следует делать позже, в функции ServiceMain(). Если в рамках одного процесса запускается несколько сервисов, главный поток должен вызвать функцию StartServiceCtrlDispatcher() не позднее, чем через 30 секунд.
Функция ServiceMain()
Функция ServiceMain() определяется процессом в качестве точки входа для данного сервиса. Эта функция может носить любое имя, заданное приложением.
Аргументы функции ServiceMain() (аналогичны аргументам функции main()): dwArgc - количество аргументов, lpszArgv - указатель на массив, который содержит указатели на строковые аргументы функции.
Функция RegisterServiceCtrlHandler() регистрирует функцию, которая будет обрабатывать управляющие запросы к сервису (такие, например, как остановка сервиса). В случае успешного завершения функция возвращает дескриптор статуса сервиса (service status handle). При неудачном завершении функция возвращает нулевое значение.
CODE NOW!
//заполняем структуру, определяющую состояние сервиса:
//сервис выполняется как отдельный процесс
ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
//устанавливаем состояние ожидания запуска сервиса
SetSomeServiceStatus(SERVICE_START_PENDING, NO_ERROR, 4000);
//инициализация для SomeService
InitSomeServiceData(argc, argv);
Поле dwServiceType структуры ss устанавливаем в SERVICE_WIN32_OWN_PROCESS. Это означает, что сервис будет выполняться как отдельный процесс, т.е. будет иметь собственное адресное пространство.
Затем устанавливаем состояние ожидания запуска сервиса c помощью созданной нами вспомогательной функции SetSomeServiceStatus(). Эта функция изменяет содержимое структуры ss, которое использует SCM для получения информации о сервисе.
Далее можно выполнить всю необходимую инициализацию для сервиса (определяемая пользователем функция InitSomeServiceData()).
Когда инициализация выполнена, вызываем функцию SetSomeServiceStatus() с параметром SERVICE_RUNNING, чтобы установить состояние работающего сервиса.
После изменения состояния сервиса выполняется основной код программы.
Функция ServiceControl()
Эта функция является управляющей функцией сервиса и может носить любое имя, определенное приложением.
CODE NOW!
void WINAPI ServiceControl(DWORD dwControlCode) {
//анализируем код команды и выполняем ее
switch(dwControlCode) {
//команда остановки сервиса
case SERVICE_CONTROL_STOP: {
//устанавливаем состояние ожидания остановки
SetSomeServiceStatus(SERVICE_STOP_PENDING, NO_ERROR, 0);
С помощью функции SetSomeServiceStatus() мы устанавливаем состояние ожидания остановки сервиса, после чего можно вызвать определяемую пользователем функцию StopSomeService(), которая выполнит все необходимые действия перед остановкой сервиса. Содержимое этой функции зависит от конкретного сервиса.
Далее с помощью функции SetSomeServiceStatus() сообщаем SCM, что сервис остановлен.
Когда сервис получает команду SERVICE_CONTROL_INTERROGATE, это означает, что он должен немедленно обновить информацию о статусе сервиса, используемую SCM.
Функция SetSomeServiceStatus()
Эта функция изменяет содержимое структуры ss, которое использует SCM для получения информации о статусе сервиса.
//если сервис не находится в процессе запуска, его можно остановить
if(dwCurrentState == SERVICE_START_PENDING)
ss.dwControlsAccepted = 0;
else
ss.dwControlsAccepted = SERVICE_ACCEPT_STOP;
//если сервис не работает и не остановлен, увеличиваем значение счетчика
//шагов длительных операций
if(dwCurrentState == SERVICE_RUNNING || dwCurrentState == SERVICE_STOPPED)
ss.dwCheckPoint = 0;
else
ss.dwCheckPoint = dwCheckPoint++;
//обновить информацию о сервисе
SetServiceStatus(ssHandle, &ss);
}
Значение поля dwCheckPoint следует периодически изменять во время длительного запуска или остановки. Например, если при запуске сервиса инициализация выполняется в несколько этапов, то нужно увеличивать значение dwCheckPoint после каждого этапа. При этом программа, которая запросила услуги сервиса, может узнать, какой объем работы уже выполнен сервисом. Поле dwCheckPoint должно быть равно нулю, когда сервис не находится в состоянии запуска, остановки или выполнения длительной операции.
Поле dwControlsAccepted определяет управляющий код, который может принимать и обрабатывать данный сервис. По умолчанию все сервисы могут принимать команду SERVICE_CONTROL_INTERROGATE. Если это поле имеет значение SERVICE_ACCEPT_STOP, то сервис может быть остановлен с помощью команды SERVICE_CONTROL_STOP.
Функции InitSomeServiceData() и StopSomeService()
CODE NOW!
BOOL InitSomeServiceData() {
//...
return TRUE;
}
BOOL StopSomeService() {
//...
return TRUE;
}
С помощью этих функций проводится инициализация, необходимая для данного сервиса, а также действия, которые необходимо выполнить перед остановкой сервиса. Содержимое этих функций зависит от конкретного сервиса.
Приложение, управляющее сервисом
Приложение SomeSrvcApp может инсталлировать сервис в систему, запускать и останавливать его, получать информацию о сервисе и удалять сервис из системы.
Здесь не будут описываться функции WinMain() и InitApp(), поскольку они не обладают никакими особенностями, заслуживающими внимания. Функция WinMain() создает главное окно с шестью кнопками: "Install Service", "Start Service", "Get Service Configuration", "Stop Service", "Remove Service" и "Exit".
Функция WndProc()
В функции WndProc() используются макросы для обработки сообщений WM_COMMAND и WM_DESTROY:
Функция WndProcOnCommand()
Функция WndProcOnCommand() вызывается функцией WndProc(), если окно получает сообщение WM_COMMAND. Функция WndProcOnCommand() содержит код, выполняющийся при нажатии на кнопки главного окна приложения.
Приведем полностью код этой функции:
CODE NOW!
void
WndProcOnCommand(HWND hWnd, int id, HWND hwndCtl, UINT codeNotify) {
SC_HANDLE hService; //дескриптор сервиса
SC_HANDLE hSCManager; //дескриптор Service Control Manager
LPQUERY_SERVICE_CONFIG lpBufConfig;
DWORD dwBytesNeeded;
char szBufConfig[4096];
Здесь мы отрываем базу данных Service Control Manager (SCM) с помощью функции OpenSCManager() с уровнем доступа SC_MANAGER_CREATE_SERVICE, который позволяет создавать сервисы.
Для создания сервиса используется функция CreateService(), которая возвращает дескриптор созданного сервиса или NULL в случае неудачного выполнения. Одним из параметров функции является путь к сервису, который должен соответствовать расположению сервиса в Вашем компьютере. Проследите, чтобы путь был указан правильно, иначе создать сервис не получится.
В качестве типа сервиса мы указали SERVICE_WIN32_OWN_PROCESS. Это означает, что сервис будет выполняться как отдельный процесс. При успешном завершении функция CreateService() добавляет сервис в базу данных SCM. Вы можете в этом убедиться, открыв системную панель управления сервисами (Панель управления -> Администрирование -> Службы).
Для открытия сервиса мы используем функцию OpenService(). Третьим параметром этой функции является тип доступа к сервису. Указывая тип доступа SERVICE_START, мы получаем возможность запускать сервис.
Запуск сервиса осуществляется с помощью функции StartService(), которая возвращает нулевое значение, если запустить сервис не удалось.
На этот раз, открывая сервис, мы указываем тип доступа SERVICE_QUERY_CONFIG, чтобы получить информацию о конфигурации сервиса. Эту информацию мы помещаем в структуру типа QUERY_SERVICE_CONFIG (через указатель на эту структуру lpBufConfig, используя динамически выделяемую память) с помощью функции QueryServiceConfig(). Функция QueryServiceConfig() помещает в структуру типа QUERY_SERVICE_CONFIG информацию о сервисе, которая находится в реестре. Эта информация была помещена в реестр функцией CreateService().
Далее мы помещаем содержимое некоторых полей структуры в буфер szBufConfig и выводим его содержимое с помощью функции MessageBox().
Здесь мы открываем сервис, используя уровень доступа SERVICE_STOP.
Для остановки сервиса используется функция ControlService(). Эта функция посылает сервису управляющий код, который она получает в качестве второго параметра. В нашем случае управляющий код равен константе SERVICE_CONTROL_STOP. Третьим параметром функции является адрес структуры типа SERVICE_STATUS, в которой содержится информация о текущем статусе сервиса. Содержимое этой структуры использует SCM. Если сервис остановить не удалось функция ControlService() возвращает нулевое значение.
CODE NOW!
//удаление сервиса из системы
case IDB_REMOVE: {
hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if(!hSCManager) {
MessageBox(NULL, "Can't open Service Control Manager",
"Error", MB_OK | MB_ICONERROR);
break;
}
hService = OpenService(hSCManager, SomeServiceName,
SERVICE_STOP | DELETE);
if(!hService) {
GetSomeSvcError();
break;
}
if(ss.dwCurrentState != SERVICE_STOPPED)
ControlService(hService, SERVICE_CONTROL_STOP, &ss);
//удаляем сервис из системы
DeleteService(hService);
CloseServiceHandle(hService);
CloseServiceHandle(hSCManager);
break;
}
В этом случае, открывая сервис, мы используем комбинацию флагов SERVICE_STOP и DELETE, поскольку если сервис в данный момент работает, то прежде чем его удалять из системы, его необходимо остановить. Для удаления сервиса из системы используется функция DeleteService().
CODE NOW!
//завершаем работу приложения
case IDB_EXIT: {
PostQuitMessage(0);
return;
}
default: break;
}
return FORWARD_WM_COMMAND(hWnd, id, hwndCtl, codeNotify, DefWindowProc);
}
Функции WndProcOnDestroy() и GetSomeSvcError()
Приведем код функции WndProcOnDestroy() и функции GetSomeSvcError(), которая используется для вывода сообщений об ошибках:
void GetSomeSvcError() {
DWORD dwError = GetLastError();
char errMess[100];
switch(dwError) {
case ERROR_ACCESS_DENIED: {
sprintf(errMess, "The specified service control manager"\
"database handle does not have access to"\
"the service.");
break;
}
case ERROR_INVALID_HANDLE: {
sprintf(errMess, "The specified handle is invalid.");
break;
}
case ERROR_INVALID_NAME: {
sprintf(errMess, "The specified service name is invalid.");
break;
}
case ERROR_SERVICE_DOES_NOT_EXIST: {
sprintf(errMess, "The specified service does not exist.");
break;
}
default: {
sprintf(errMess, "Can't open service.\nError number not found.");
}
}
MessageBox(NULL, errMess, "Error", MB_OK | MB_ICONERROR);
return;
}
В функции GetSomeSvcError() анализируется код ошибки, полученный с помощью функции Win API GetLastError(). Полный список кодов ошибок и их значений можно посмотреть в файле WinError.h или в документации по Win API.
Комментарии
отсутствуют
Добавление комментария
:: Последние обновления ::
04.09.2011 Долгожданный релиз VB Decompiler. Масса улучшений декомпиляции Native Code. Значительно расширенна и обновлена справочная система на русском и английском языках.
20.12.2010 DotFix Software поздравляет наших клиентов и посетителей сайта с наступающим Новым Годом и рождеством! Желаем приятно провести праздники и успехов в новом году!
28.11.2010 Выпущена новая версия защиты DotFix NiceProtect. Основные изменения коснулись обфускатора Delphi программ. Теперь имеется полная поддержка Tab и Page контролов на формах, что обеспечивает максимальную совместимость обфускации с Delphi XE программами.
21.10.2010 Обновлен декомпилятор Visual Basic программ до версии 8.1. Декомпиляция P-Code программ становится все более идеальной, также проделана большая работа по улучшению анализа Native Code и .NET приложений.
16.09.2009 Полностью обновлен движок сайта! Теперь все ссылки имеют читаемый понятный вид, разного рода глюки на страницах убраны. И теперь сайт полноценно работает на второй версии нашего движка.