Джонсон Харт - Системное программирование в среде Windows
lpBinaryPathName — имя исполняемого файла службы; указывать расширение .exe не требуется.
Другие параметры используются для указания имени учетной записи и пароля, групп, объединяющих несколько служб, а также зависимостей, если существует несколько отдельных служб.
Конфигурационные параметры существующей службы можно изменить с помощью функции ChangeServiceConfig или, в случае NT5, ChangeService-Config2. Служба идентифицируется по своему дескриптору, и для большинства параметров вы можете указать новые значения. Например, можно предоставить новые значения параметров dwServiceType или dwStartType, но в случае параметра dwAccess это сделать невозможно.
Доступна также функция OpenService, которая позволяет получить дескриптор именованной службы. Для удаления службы из реестра используется функция DeleteService, а для закрытия дескрипторов SC_HANDLE — функция CloseServiceHandle.
Запуск службы
Созданная служба сразу не выполняется. Для этого необходимо вызвать функцию ServiceMain(), указав дескриптор, полученный при помощи функции CreateService, а также параметры командной строки argc и argv, ожидаемые основной функцией службы (то есть функцией, указанной в таблице диспетчеризации).
BOOL StartService(SC_HANDLE hService, DWORD argc, LPTSTR argv[])
Управление службой
Чтобы начать управление службой, вы должны сообщить SCM о необходимости активизации обработчика управляющих команд службы с указанным управляющим кодом.
BOOL ControlService(SC_HANDLE hService, DWORD dwControlCode, LPSERVICE_STATUS lpServStat)
Параметр dwControlCode, если доступ разрешен, может принимать одно из следующих значений:
SERVICE_CONTROL_STOP
SERVICE_CONTROL_PAUSE
SERVICE_CONTROL_CONTINUE
SERVICE_CONTROL_INTERROGATE
SERVICE_CONTROL_SHUTDOWN
или значение, определенное пользователем, лежащее в пределах диапазона 128–255. Эти значения совпадают с теми, которые использовались вместе с флагом dwControl в функции ServerCtrlHandler.
lpServStat — указатель на структуру SERVICE_STATUS, которая получает текущее состояние. Это та же структура, которая использовалась функцией SetServiceStatus.
Опрос состояния службы
Для получения структурой SERVICE_STATUS текущего состояния службы используется следующая функция:
BOOL QueryServiceStatus(SC_HANDLE hService, LPSERVICE_STATUS lpServiceStatus)
Резюме: функционирование и управление службой
На рис. 13.1 показано, каким образом SCM связан со службами и программой управления службами, подобной программе 13.3, которая рассматривается в следующем разделе. В частности, SCM должен зарегистрировать службу, и все команды, предназначенные для службы, должны пропускаться через SCM.
Рис. 13.1. Управление службами Windows через SCM
Пример:команднаяоболочкауправленияслужбами
Управление службами часто осуществляется посредством утилит, входящих в группу Administrative Tools, доступ к которым открывается через пиктограмму Services (Службы). Для управления пользовательскими службами можно также использовать оболочку ServiceShell (программа 13.3), представляющую собой видоизмененный вариант программы JobShell из главы 6 (программа 6.3).
Программа 13.3. ServiceShell: программа управления службами/* Глава 13. */
/* ServiceShell.с. Программа командной оболочки управления службами Windows.
Эта программа является видоизмененным вариантом программы управления задачами из главы 6, но только управляет службами, а не задачами. */
/* Поддерживаемые команды:
create — создание службы
delete – удаление службы
start – запуск службы
control – управление службой */
#include "EvryThng.h"
static SC_HANDLE hScm;
static BOOL Debug;
int _tmain(int argc, LPTSTR argv[]) {
BOOL Exit = FALSE;
TCHAR Command[MAX_COMMAND_LINE + 10], *pc;
DWORD i, LocArgc; /* Локальный параметр argc. */
TCHAR argstr[MAX_ARG][MAX_COMMAND_LINE];
LPTSTR pArgs[MAX_ARG];
/* Подготовить локальный массив "argv" в виде указателей на строки. */
for (i = 0; i < MAX_ARG; i++) pArgs[i] = argstr[i];
/* Открыть диспетчер управления службами на локальной машине. */
hScm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
/* Главный цикл обработки команд. */
_tprintf(_T("nУправление службами Windows Services"));
while (!Exit) {
_tprintf(_T ("nSM$"));
_fgetts(Command, MAX_COMMAND_LINE, stdin);
… Как для JobShell …
if (_tcscmp(argstr [0], _T("create")) == 0) {
Create(LocArgc, pArgs, Command);
}
… Аналогичным образом для всех команд …
}
CloseServiceHandle(hScm);
return 0;
}
int Create(int argc, LPTSTR argv[], LPTSTR Command) {
/* Создание новой службы в виде службы, запускаемой "по требованию":
argv[1]: имя службы
argv[2]: отображаемое имя службы
argv[3]: название исполняемого файла */
SC_HANDLE hSc;
TCHAR CurrentDir[MAX_PATH +1], Executable[MAX_PATH + 1];
hSc = CreateService(hScm, argv[1], argv[2], SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, Executable, NULL, NULL, NULL, NULL, NULL);
return 0;
}
/* Удаление службы – argv [1]: имя удаляемой службы. */
int Delete(int argc, LPTSTR argv[], LPTSTR Command) {
SC_HANDLE hSc;
hSc = OpenService(hScm, argv[1], DELETE);
DeleteService(hSc);
CloseServiceHandle(hSc);
return 0;
}
/* Запуск именованной службы - argv [1] : имя запускаемой службы. */
int Start(int argc, LPTSTR argv[], LPTSTR Command) {
SC_HANDLE hSc;
TCHAR WorkingDir[MAX_PATH + 1];
LPTSTR pWorkingDir = WorkingDir;
LPTSTR argvStart[] = {argv[1], WorkingDir};
GetCurrentDirectory(MAX_PATH + 1, WorkingDir);
hSc = OpenService(hScm, argv[1], SERVICE_ALL_ACCESS);
/* Запустить службу с одним аргументом — именем рабочего каталога. */
/* Примечание: по умолчанию имя службы совпадает с именем, */
/* связанным с дескриптором hSc посредством функции OpenService. */
/* Вместе с тем, функция ServiceMain это не проверяет. */
StartService(hSc, 2, argvStart);
CloseServiceHandle(hSc);
return 0;
}
/* Управление именованной службой.
argv[1]: имя управляемой службы.
argv[2]: управляющая команда: stop (остановка), pause (пауза), resume (возобновление), interrogate (опрос). */
static LPCTSTR Commands[] = {"stop," "pause," "resume," "interrogate," "user"};
static DWORD Controls[] = {
SERVICE_CONTROL_STOP, SERVICE_CONTROL_PAUSE,
SERVICE_CONTROL_CONTINUE, SERVICE_CONTROL_INTERROGATE, 128
};
int Control(int argc, LPTSTR argv[], LPTSTR Command) {
SC_HANDLE hSc;
SERVICE_STATUS ServiceStatus;
DWORD dwControl, i;
BOOL Found = FALSE;
for (i= 0; i < sizeof(Controls)/sizeof(DWORD) && !Found; i++) Found = (_tcscmp(Commands [i], argv[2]) == 0);
if (!Found) {
_tprintf(_T("nНесуществующая команда управления %s"), argv[1]);
return 1;
}
dwControl = Controls[i – 1];
hSc = OpenService(hScm, argv[1], SERVICE_INTERROGATE | SERVICE_PAUSE_CONTINUE | SERVICE_STOP | SERVICE_USER_DEFINED_CONTROL | SERVICE_QUERY_STATUS);
ControlService(hSc, dwControl, &ServiceStatus);
if (dwControl == SERVICE_CONTROL_INTERROGATE) {
QueryServiceStatus (hSc, &ServiceStatus);
printf(_T("Состояние, полученное при помощи QueryServiceStatusn"));
printf(_T("Состояние службыn"));
… Вывести всю остальную информацию о состоянии …
}
if (hSc != NULL) CloseServiceHandle(hSc);
return 0;
}
Совместное использование объектов ядра приложениями и службами
Возможны ситуации, в которых служба и приложения разделяют объект ядра. Например, служба может использовать именованный мьютекс для защиты разделяемой области памяти, используемой для обмена данными с приложениями. Более того, в нашем примере также будет применяться разделяемый объект ядра, которым в данном случае является отображение файла.
Существует одна трудность, связанная с тем, что контекст безопасности приложений отличается от контекста безопасности служб, выполняющихся от имени системной учетной записи. Даже если защита не требуется, было бы нелогично создавать и (или) открывать разделяемые объекты ядра с указателем атрибутов безопасности, установленным в NULL (см. глава 15). Вместо этого необходим, по крайней мере, нулевой список разграничительного контроля доступа (см. главу 15), то есть приложения и служба должны использовать ненулевую структуру атрибутов защиты. В общем случае вы захотите защитить объекты, и этот вопрос также будет рассмотрен в главе 15.
Следует обратить ваше внимание также на то, что если служба выполняется от имени системной учетной записи, то могут возникать трудности с доступом службы к таким ресурсам, как разделяемые файлы, находящиеся на других машинах.