[Назад] [Содеожание] [Дальше]

Программирование для Windows NT

© Александр Фролов, Григорий Фролов
Том 27, часть 2, М.: Диалог-МИФИ, 1996, 272 стр.

5 Сервисные процессы

  • Создание сервисного процесса
  • Управление сервисами
  • Приложение SRVCTRL
  • Помимо обычных процессов, в операционной системе Microsoft Windows NT создаются так называемые сервисные процессы или сервисы (services). Эти процессы могут стартовать автоматически при загрузке операционной системы, по запросу приложений или других сервисов, а также в ручном режиме.

    Функции, выполняемые сервисами, могут быть самыми разнообразными: от обслуживания аппаратуры и программных интерфейсов до серверов приложений, таких, например, как серверы баз данных или серверы World Wide Web (WWW).

    Информация о всех серверах, установленных в системе, хранится в регистрационной базе данных. Ниже мы привели путь к этой информации:

    
    HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Servicies
    

    Для просмотра и редактирования регистрационной базы данных вы можете воспользоваться приложением regedt32.exe, которое находится в каталоге winnt\system32. Однако в программном интерфейсе WIN32 имеется набор функций, специально предназначенных для работы с записями регистрационной базы данных, имеющих отношение к сервисным процессам.

    Чтобы просмотреть список установленных сервисов, вы можете запустить приложение Services из папки Control Panel. В диалоговой панели Services (рис. 5.1) отображается список установленных сервисов, их текущее состояние и режим запуска (ручной или автоматический).

    Рис. 5.1. Диалоговая панель приложения Services, предназначенная для управления сервисными процессами

    Если сделать двойной щелчок левой клавишей мыши по названию сервиса, на экране появится диалоговая панель, с помощью которой можно настроить параметры сервиса (рис. 5.2).

    Рис. 5.2. Диалоговая панель, предназначенная для настройки параметров сервиса

    С помощью группы переключателей Startup Type вы можете выбрать один из трех способов запуска сервиса.

    Если включен переключатель Automatic, сервис будет запускаться автоматически при загрузке операционной системы. Этот режим удобен для тех сервисов, которые нужны постоянно, например для сервера базы данных. При этом сервер базы данных будет запускаться автоматически без участия оператора, что очень удобно.

    При включении переключателя Manual сервис будет запускаться в ручном режиме. Пользователь может запустить сервис, нажав кнопку Start в диалоговой панели Services, показанной на рис. 5.1. Другое приложение или другой сервис также может запустить этот сервис при помощи специальной функции программного интерфейса WIN32. Эту функцию мы рассмотрим позже.

    И, наконец, если включить переключатель Disabled, работа сервиса будет заблокирована.

    Сервис может работать с привилегиями выбранных пользователей или с привилегиями системы LocalSystem. Для выбора имени пользователя в ы должны включить переключатель This Account и выбрать из списка, расположенного справа от этого переключателя, имя пользователя. Дополнительно в полях Password и Confirm Password необходимо ввести пароль пользователя.

    Если включить переключатель System Account, сервис будет работать с привилегиями системы. Если сервис будет взаимодействовать с программным интерфейсом рабочего стола Desktop, следует включить переключатель Allow Service to Interact with Desktop.

    Сервисы могут быть двух типов: стандартные сервисы и сервисы, соответствующие протоколам драйверов устройств Microsoft Windows NT. Последние описаны в документации DDK и не рассмотрены в нашей книге. Нажав в диалоговой панели Services, показанной на рис. 5.1, кнопку HW Profiles, вы можете выбрать один из установленных файлов конфигурации аппаратуры, которая обслуживается данным сервисом (рис. 5.3), разрешить или запретить использование выбранного файла конфигурации.

    Рис. 5.3. Выбор файла конфигурации аппаратуры

    При запуске операционной системы Microsoft Windows NT автоматически стартует специальный процесс, который называется процессом управления сервисами (Service Control Manager). В программном интерфейсе WIN32 имеются функции, с помощью которых приложения и сервисы могут управлять работой сервисов, обращаясь к процессу управления сервисами. Некоторые из этих функций будут рассмотрены в нашей книге.

    Создание сервисного процесса

    Для того чтобы создать загрузочный модуль сервиса, вы должны подготовить исходные тексты обычного консольного приложения, имеющего функцию main (не WinMain, а именно main).

    Функция main сервисного процесса

    В простейшем случае функция main вызывает функцию StartServiceCtrlDispatcher, что необходимо для подключения главной задачи сервисного процесса к процессу управления сервисами. Ниже мы привели пример функции main сервисного процесса:

    
    #define MYServiceName "Sample of simple service"
    void main(int agrc, char *argv[])
    {
      SERVICE_TABLE_ENTRY DispatcherTable[] =
      {
        {
          MYServiceName, (LPSERVICE_MAIN_FUNCTION)ServiceMain
        },
        {
          NULL, NULL
        }
      };
      if(!StartServiceCtrlDispatcher(DispatcherTable))
      {
        fprintf(stdout,"StartServiceCtrlDispatcher: Error %ld\n",
          GetLastError());
        getch();
        return;
      }
    }
    

    Функции StartServiceCtrlDispatcher передается указатель на массив структур типа SERVICE_TABLE_ENTRY. В этом массиве описываются точки входа всех сервисов, определенных в данном файле. Таким образом, в одном файле можно определить сразу несколько сервисов. Последняя строка таблицы всегда должна содержать значения NULL - это признак конца таблицы.

    Тип SERVICE_TABLE_ENTRY и соответствующий указатель определены следующим образом:

    
    typedef struct _SERVICE_TABLE_ENTRY
    {
      LPTSTR lpServiceName;
      LPSERVICE_MAIN_FUNCTION lpServiceProc;
    } SERVICE_TABLE_ENTRY, *LPSERVICE_TABLE_ENTRY;
    

    В поле lpServiceName записывается указатель на текстовую строку имени сервиса, а в поле lpServiceProc - указатель на точку входа сервиса.

    Заметим, что функция main должна вызвать функцию StartServiceCtrlDispatcher достаточно быстро - не позднее чем через 30 секунд после запуска.

    Получив управление, функция StartServiceCtrlDispatcher не возвращает его до тех пор, пока все сервисы, запущенные в рамках данного процесса, не завершат свою работу.

    При успешном завершении функция StartServiceCtrlDispatcher возвращает значение TRUE. Если же произойдет ошибка, возвращается значение FALSE. Код ошибки можно определить при помощи функции GetLastError.

    Точка входа сервиса

    Точка входа сервиса - это функция, адрес которой записывается в поле lpServiceProc массива структур SERVICE_TABLE_ENTRY. Имя функции может быть любым, а прототип должен быть таким, как показанный ниже:

    
    void WINAPI ServiceMain(DWORD dwArgc, LPSTR *lpszArgv);
    

    Точка входа сервиса вызывается при запуске сервиса функцией StartService (эту функцию мы рассмотрим позже). Через параметр dwArgc передается счетчик аргументов, а через параметр lpszArgv - указатель на массив строк параметров. В качестве первого параметра всегда передается имя сервиса. Остальные параметры можно задать при запуске сервиса функцией StartService.

    Функция точки входа сервиса должна зарегистрировать функцию обработки команд и выполнить инициализацию сервиса.

    Первая задача решается с помощью функции RegisterServiceCtrlHandler, прототип которой приведен ниже:

    
    SERVICE_STATUS_HANDLE RegisterServiceCtrlHandler(
      LPCTSTR lpszServiceName,            // имя сервиса
      LPHANDLER_FUNCTION lpHandlerProc);  // адрес функции
                                          // обработки команд
    

    Через первый параметр этой функции необходимо передать адрес текстовой строки имени сервиса, а через второй - адрес функции обработки команд (функция обработки команд будет рассмотрена ниже).

    Вот пример использования функции RegisterServiceCtrlHandler:

    
    SERVICE_STATUS_HANDLE ssHandle;
    ssHandle =
      RegisterServiceCtrlHandler(MYServiceName, ServiceControl);
    

    Функция RegisterServiceCtrlHandler в случае успешного завершения возвращает идентификатор состояния сервиса. При ошибке возвращается нулевое значение.

    Заметим, что регистрация функции обработки команд должна быть выполнена немедленно в самом начале работы функции точки входа сервиса.

    Теперь перейдем к решению второй задачи - инициализации сервиса.

    В процессе инициализации функция точки входа сервиса выполняет действия, которые зависят от назначения сервиса. Необходимо, однако, помнить, что без принятия дополнительных мер инициализация должна выполняться не дольше одной секунды.

    А что делать, если инициализация сервиса представляет собой достаточно длительный процесс?

    В этом случае перед началом инициализации функция точки входа сервиса должна сообщить процессу управления сервисами, что данный сервис находится в состоянии ожидания запуска. Это можно сделать с помощью функции SetServiceStatus, которая будет описана позже. Перед началом инициализации вы должны сообщить процессу управления сервисами, что сервис находится в состоянии SERVICE_START_PENDING.

    После завершения инициализации функция точки входа сервиса должна указать процессу управления сервисами, что процесс запущен и находится в состоянии SERVICE_RUNNING.

    Функция обработки команд

    Как следует из названия, функция обработки команд, зарегистрированная функцией RegisterServiceCtrlHandler, обрабатывает команды, передаваемые сервису операционной системой, другими сервисами или приложениями. Эта функция может иметь любое имя и выглядит следующим образом:

    
    void WINAPI ServiceControl(DWORD dwControlCode)
    {
      switch(dwControlCode)
      {
        case SERVICE_CONTROL_STOP:
        {
          ss.dwCurrentState = SERVICE_STOP_PENDING;
          ReportStatus(ss.dwCurrentState, NOERROR, 0);
    
          // Выполняем остановку сервиса, вызывая функцию,
          // которая выполняет все необходимые для этого действия
          // ServiceStop();
    
          ReportStatus(SERVICE_STOPPED, NOERROR, 0);
          break;
        }
        case SERVICE_CONTROL_INTERROGATE:
        {
          ReportStatus(ss.dwCurrentState, NOERROR, 0);
          break;
        }
        default:
        {
          ReportStatus(ss.dwCurrentState, NOERROR, 0);
          break;
        }
      }
    }
    

    В приведенном выше фрагменте кода для сообщения процессу управления сервисами текущего состояния сервиса мы вызываем созданную нами функцию ReportStatus. Эта функция будет описана в следующем разделе.

    Через единственный параметр функция обработки команд получает код команды, который может принимать одно из перечисленных ниже значений.

     Значение

     Описание

     SERVICE_CONTROL_STOP

     Остановка сервиса

     SERVICE_CONTROL_PAUSE

     Временная остановка сервиса

     SERVICE_CONTROL_CONTINUE

     Продолжение работы сервиса после временной остановки

     SERVICE_CONTROL_INTERROGATE

     Когда поступает эта команда, сервис должен немедленно сообщить процессу управления сервисами свое состояние

     SERVICE_CONTROL_SHUTDOWN

     Сервис должен прекратить работу в течении 20 секунд, так как завершается работа операционной системы

    Состояние сервиса

    Как мы уже говорили, сервис может сообщить процессу управления сервисами свое состояние, для чего он должен вызвать функцию 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

    Значение в поле dwWin32ExitCode определяет код ошибки WIN32, который используется для сообщения о возникновении ошибочной ситуации при запуске и остановки сервиса. Если в этом поле указать значение ERROR_SERVICE_SPECIFIC_ERROR, то будет использован специфический для данного сервиса код ошибки, указанной в поле dwServiceSpecificExitCode структуры SERVICE_STATUS. Если ошибки нет, в поле dwWin32ExitCode необходимо записать значение NO_ERROR.

    Поле 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, которые будут описаны ниже.

    Управление сервисами

    Вы можете создать приложение или сервис, управляющее сервисами. В этом разделе мы рассмотрим основные функции программного интерфейса WIN32, предназначенные для управления сервисами. Более подробную информацию вы найдете в документации SDK.

    Получение идентификатора системы управления сервисами

    Идентификатор системы управления сервисами нужен для выполнения различных операций над сервисами, таких например, как установка сервиса. Вы можете получить этот идентификатор с помощью функции OpenSCManager:

    
    SC_HANDLE OpenSCManager(
      LPCTSTR lpszMachineName,   // адрес имени рабочей станции
      LPCTSTR lpszDatabaseName,  // адрес имени базы данных
      DWORD   fdwDesiredAccess); // нужный тип доступа
    

    Задавая имя рабочей станции через параметр lpszMachineName, вы можете получить идентификатор системы управления сервисами на любом компьютере сети. Для локального компьютера необходимо указать значение NULL.

    Для наших примеров параметр lpszDatabaseName, определяющий имя базы данных системы управления сервисами, нужно указать как NULL. При этом по умолчанию будет использована база данных активных сервисов ServicesActive.

    Через параметр fdwDesiredAccess нужно задать требуемый тип доступа. Здесь можно использовать следующие константы:

     Константа

     Разрешенный тип доступа

     SC_MANAGER_ALL_ACCESS

     Полный доступ

     SC_MANAGER_CONNECT

     Подключение к системе управления сервисами

     SC_MANAGER_CREATE_SERVICE

     Создание новых сервисов и добавление их к регистрационной базе данных

     SC_MANAGER_ENUMERATE_SERVICE

     Просмотр списка всех установленных сервисов при попщи функции EnumServicesStatus (в нашей книге эта функция и следующие две функции не описаны)

     SC_MANAGER_LOCK

     Блокирование базыв данных функцией LockServiceDatabase

     SC_MANAGER_QUERY_LOCK_STATUS

     Определение состояние блокировки базы данных функцией QueryServiceLockStatus

    Ниже мы привели пример вызова функции OpenSCManager:

    
    schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    

    После использования вы должны закрыть идентификатор, полученный от функции OpenSCManager. Для этого необходимо вызвать функцию CloseServiceHandle:

    CloseServiceHandle(schSCManager);

    Установка сервиса

    Для установки сервиса в систему вы должны использовать функцию CreateService, которая вносит все необходимые дополнения в регистрационную базу данных.

    Прототип функции CreateService мы привели ниже:

    
    SC_HANDLE CreateService(
      SC_HANDLE hSCManager,    // идентификатор базы данных системы
                               // управления сервисами
      LPCTSTR lpServiceName,   // имя сервиса, которое будет использовано
                               // для запуска
      LPCTSTR lpDisplayName,   // имя сервиса для отображения
      DWORD   dwDesiredAccess, // тип доступа к сервису
      DWORD   dwServiceType,   // тип сервиса
      DWORD   dwStartType,     // способ запуска сервиса
      DWORD   dwErrorControl,  // действия при ошибках в момент запуска
      LPCTSTR lpBinaryPathName,   // путь к загрузочному файлу сервиса
      LPCTSTR lpLoadOrderGroup,   // имя группы порядка загрузки
      LPDWORD lpdwTagId,          // адрес переменной для сохранения
                                  // идентификатора тега
      LPCTSTR lpDependencies,     // адрес массива имен взаимосвязей
      LPCTSTR lpServiceStartName, // адрес имени пользователя, права
                        // которого будут применены для работы сервиса
      LPCTSTR lpPassword );       // адрес пароля пользователя
    

    Через параметр hSCManager вы должны передать функции CreateService идентификатор базы данных системы управления сервисами, полученный от функции OpenSCManager, описанной выше.

    Через параметры lpServiceName и lpDisplayName задаются, соответственно, имя сервиса, которое будет использовано для запуска и имя сервиса для отображения в списке установленных сервисов.

    С помощью параметра dwDesiredAccess вы должны указать тип доступа, разрешенный при обращении к данному сервису. Здесь вы можете указать следующие значения:

     Значение

     Разрешенный тип доступа

     SERVICE_ALL_ACCESS

     Полный доступ

     SERVICE_CHANGE_CONFIG

     Изменение конфигурации сервиса функцией ChangeServiceConfig

     SERVICE_ENUMERATE_DEPENDENTS

     Просмотр сервиса в списке сервисов, созданных на базе данного сервиса функцией EnumDependentServices

     SERVICE_INTERROGATE

     Выдача сервису команды немедленного определения текущего состояния сервиса при помощи функции ControlService (эта функция будет описана ниже)

     SERVICE_PAUSE_CONTINUE

     Временная остановка сервиса или продолжение работы после временной остановки

     SERVICE_QUERY_CONFIG

     Определение текущей конфигурации функцией QueryServiceConfig

     SERVICE_QUERY_STATUS

     Определение текущего состояния сервиса функцией QueryServiceStatus

     SERVICE_START

     Запуск сервиса функцией StartService

     SERVICE_STOP

     Остановка сервиса выдачей соответствующей команды функцией ControlService

     SERVICE_USER_DEFINE_CONTROL

     Возможность передачи сервису команды, определенной пользователем, с помощью функции ControlService

    Через параметр dwServiceType необходимо передать тип сервиса. Здесь вы можете указывать те же самые флаги, что и в поле dwServiceType структуры SERVICE_STATUS, описанной выше:

     Флаг

     Описание

     SERVICE_WIN32_OWN_PROCESS

     Сервис работает как отдельный процесс

     SERVICE_WIN32_SHARE_PROCESS

     Сервис работает вместе с другими сервисами в рамках одного и того же процесса

     SERVICE_KERNEL_DRIVER

     Сервис представляет собой драйвер операционной системы Microsoft Windows NT

     SERVICE_FILE_SYSTEM_DRIVER

     Сервис является драйвером файловой системы

     SERVICE_INTERACTIVE_PROCESS

     Сервисный процесс может взаимодействовать с программным интерфейсом рабочего стола Desktop

    В параметре dwStartType указывается один из следующих способов запуска сервиса:

     Константа

     Способ запуска

     SERVICE_BOOT_START

     Используется только для сервисов типа SERVICE_KERNEL_DRIVER или SERVICE_FILE_SYSTEM_DRIVER (драйверы). Указывает, что драйвер должен загружаться при загрузке операционной системы

     SERVICE_SYSTEM_START XE

     Аналогично предыдущему, но драйвер запускается при помощи функции IoInitSystem, не описанной в нашей книге

     SERVICE_AUTO_START

     Драйвер или обычный сервис, который запускается при загрузке операционной системы

     SERVICE_DEMAND_START

     Драйвер или обычный сервис, который запускается функцией StartService

     SERVICE_DISABLED

     Отключение возможности запуска драйвера или обычного сервиса

    Параметр dwErrorControl задает действия, выполняемые при обнаружении ошибки в момент загрузки сервиса. Здесь можно указывать одно из следующих значений:

     Значение

     Реакция на ошибку

     SERVICE_ERROR_IGNORE

     Протоколирование ошибки в системном журнале и продолжение процедуры запуска сервиса

     SERVICE_ERROR_NORMAL

     Протоколирование ошибки в системном журнале без продолжения процедуры запуска сервиса

     SERVICE_ERROR_SEVERE

     Протоколирование ошибки в системном журнале. Если это возможно, используется конфигурация, с которой сервис успешно был запущен в прошлый раз. В противном случае система перезапускается с использованием работоспособной конфигурации

     SERVICE_ERROR_CRITICAL

     Криичная ошибка. Сообщение при возможности записывается в системный журнал. Операция запуска отменяется, система перезапускается с с использованием работоспособной конфигурации

    В параметре lpBinaryPathName вы должны указать полный путь к загрузочному файлу сервиса.

    Через параметр lpLoadOrderGroup передается указатель на имя группы порядка загрузки сервиса. Сделав сервис членом одной из групп порядка загрузки, вы можете определить последовательность загрузки вашего сервиса относительно других сервисов. Список групп порядка загрузки находится в регистрационной базе данных:

    
    HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\ServiceGroupOrder
    

    Если относительный порядок загрузки не имеет значения, укажите для параметра lpLoadOrderGroup значение NULL.

    Параметр lpdwTagId используется только в том случае, если значение параметра lpLoadOrderGroup не равно NULL.

    Параметр lpDependencies должен содержать указатель на массив строк имен сервисов или групп порядка загрузки, которые должны быть запущены перед запуском данного сервиса. Последняя строка такого массива должна быть закрыта двумя двоичными нулями. Если зависимостей от других сервисов нет, для параметра lpDependencies можно указать значение NULL.

    Последние два параметра функции lpServiceStartName и lpPassword указывают, соответственно, имя и пароль пользователя, с правами которого данный сервис будет работать в системе (имя указывается в форме “ИмяДомена\имяПользователя”). Если параметр lpServiceStartName указан как NULL, сервис подключится к системе как пользователь LocalSystem. При этом параметр lpPassword должен быть указан как NULL.

    Ниже мы привели фрагмент исходного текста приложения, в котором выполняется установка сервиса из каталога c:\ntbk2\src\service\small\debug:

    
    schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    schService = CreateService(
      schSCManager, MYServiceName, MYServiceName,
      SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
      SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
      "c:\\ntbk2\\src\\service\\small\\debug\\small.exe",
      NULL, NULL, "", NULL, NULL);
    CloseServiceHandle(schSCManager);
    

    Получение идентификатора сервиса

    Для выполнения операций с сервисом вы должны получить его идентификатор. Это нетрудно сделать с помощью функции OpenService, прототип которой мы привели ниже:

    
    SC_HANDLE OpenService(
      SC_HANDLE schSCManager,      // идентификатор базы данных системы
                                   // управления сервисами
      LPCTSTR   lpszServiceName,   // имя сервиса
      DWORD     fdwDesiredAccess); // тип доступа к сервису
    

    Через параметр schSCManager вы должны передать функции OpenService идентификатор базы данных системы управления сервисами, полученный от функции OpenSCManager.

    Параметр lpszServiceName определяет имя сервиса, а параметр fdwDesiredAccess - желаемый тип доступа к сервису.

    Выдача команд сервису

    Приложение или сервис может выдать команду сервису, вызвав функцию ControlService:

    
    BOOL ControlService(
      SC_HANDLE hService,                // идентификатор сервиса
      DWORD     dwControl,               // код команды
      LPSERVICE_STATUS lpServiceStatus); // адрес структуры состояния
                                         // сервиса SERVICE_STATUS
    

    В качестве кода команды вы можете указать один из следующих стандартных кодов:

     Код

     Команда

     SERVICE_CONTROL_STOP

     Остановка сервиса

     SERVICE_CONTROL_PAUSE

     Временная остановка сервиса

     SERVICE_CONTROL_CONTINUE

     Продолжение работы сервиса после временной установки

     SERVICE_CONTROL_INTERROGATE

     Запрос текущего состояния сервиса

    Дополнительно вы можете указывать коды команд, определенные вами. Они должны находиться в интервале значений от 128 до 255.

    Удаление сервиса из системы

    Для удаления сервиса из системы используется функция DeleteService. В качетсве единственного параметра этой функции необходимо передать идентификатор сервиса, полученный от функции OpenService.

    Ниже мы привели фрагмент приложения, удаляющий сервис с именем MYServiceName из системы:

    
    schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    schService = OpenService(
      schSCManager, MYServiceName, SERVICE_ALL_ACCESS);
    ControlService(schService, SERVICE_CONTROL_STOP, &ss);
    DeleteService(schService);
    CloseServiceHandle(schSCManager);
    

    Заметим, что перед удалением мы останавливаем сервис.

    Запуск сервиса

    Для запуска сервиса вы должны использовать функцию StartService:

    
    BOOL StartService(
      SC_HANDLE schService,        // идентификатор сервиса
      DWORD     dwNumServiceArgs,  // количество аргументов
      LPCTSTR   *lpszServiceArgs); // адрес массива аргументов
    

    Через параметр schService вы должны передать функции StartService идентификатор сервиса, полученный от функции OpenService.

    Параметры dwNumServiceArgs и lpszServiceArgs определяют, соответственно, количество аргументов и адрес массива аргументов, которые получит функция точки входа сервиса. Эти параметры могут использоваться в процессе инициализации.

    Ниже мы привели фрагмент исходного текста приложения, выполняющий запуск сервиса:

    
    schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    schService = OpenService(
      schSCManager, MYServiceName, SERVICE_ALL_ACCESS);
    StartService(schService, 0, NULL);
    CloseServiceHandle(schSCManager);
    

    Остановка сервиса

    Остановка сервиса выполняется посылкой сервису команды SERVICE_CONTROL_STOP, для чего эта комнда передается функции ControlService XE "ControlService" :

    
    ControlService(schService, SERVICE_CONTROL_STOP, &ss);
    

    Определение конфигурации сервиса

    Приложение или сервис может определить конфигурацию заданного сервиса, вызвав для этого функцию QueryServiceConfig XE "QueryServiceConfig" :

    
    BOOL QueryServiceConfig(
      SC_HANDLE schService,  // идентификатор сервиса
      LPQUERY_SERVICE_CONFIG lpqscServConfig, // адрес структуры
                             // QUERY_SERVICE_CONFIG, в которую будет
                             // записана конфигурация сервиса
      DWORD   cbBufSize,     // размер буфера для записи конфигурации
      LPDWORD lpcbBytesNeeded); // адрес переменной, в котоую будет
                            // записан размер буфера, необходимый для
                            // сохранения всей информации о конфигурации
    

    Формат структуры QUERY_SERVICE_CONFIG приведен ниже:

    
    typedef struct _QUERY_SERVICE_CONFIG
    {
      DWORD  dwServiceType;
      DWORD  dwStartType;
      DWORD  dwErrorControl;
      LPTSTR lpBinaryPathName;
      LPTSTR lpLoadOrderGroup;
      DWORD  dwTagId;
      LPTSTR lpDependencies;
      LPTSTR lpServiceStartName;
      LPTSTR lpDisplayName;
    } QUERY_SERVICE_CONFIG, LPQUERY_SERVICE_CONFIG;
    

    Содержимое полей этой структуры соответствует содержимому параметров функции CreateService, описанной ранее.

    Ниже расположен фрагмент кода, в котором определяется текущая конфигурация сервиса:

    
    schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    schService = OpenService(
      schSCManager, MYServiceName, SERVICE_ALL_ACCESS);
    lpBuf = (LPQUERY_SERVICE_CONFIG)malloc(4096);
    if(lpBuf != NULL)
    {
      QueryServiceConfig(schService, lpBuf, 4096, &dwBytesNeeded);
      . . .
      free(lpBuf);
    }
    

    Приложение SRVCTRL

    В этом разделе мы приведем исходные тексты простейшего сервиса Simple и приложения SRVCTRL, с помощью которого можно установить данный сервис, запустить, остановить или удалить его, а также определить текущую конфигурацию.

    Главное меню приложения SRVCTRL и временное меню Service показано на рис. 5.4.

    Рис. 5.4. Главное меню приложения SRVCTRL

    С помощью строки Install вы можете установить сервис. Не забудьте перед этим записать загрузочный файл сервиса в каталог c:\ntbk2\src\service\small\debug, так как приложение SRVCTRL может установить сервис только из этого каталога.

    После установки имя сервиса появится в списке сервисов, который можно просмотреть при помощи приложения Services из папки Control Panel (рис. 5.5).

    Рис. 5.5. В списке сервисов появился новый сервис Sample of simple service

    Если теперь из меню Service нашего приложения выбрать строку Get configuration, на экране появится диалоговая панель, в которой будут отображены некоторые поля структуры конфигрурации сервиса (рис. 5.6).

    Рис. 5.6. Просмотр конфигурации сервиса

    Исходный текст сервиса

    Исходный текст сервиса представлен в листинге 5.1. Так как ранее мы уже подробно описывали структуру этого сервиса, то мы оставим вам этот листинг и листинг приложения SRVCTRL на самостоятельное изучение.

    Листинг 5.1. Файл service/small/small.c

    
    // ==================================================
    // Сервис "Sample of simple service"
    // Шаблон простейшего сервиса Windows NT
    //
    // (С) Фролов А.В., 1996
    // Email: frolov@glas.apc.org
    // ==================================================
    
    #define STRICT
    #include <windows.h>
    #include <windowsx.h>
    #include <stdio.h>
    #include <conio.h>
    #include <stdlib.h>
    
    #include "small.h"
    
    // -----------------------------------------------------
    // Глобальные переменные
    // -----------------------------------------------------
    
    // Код ошибки
    DWORD dwErrCode;
    
    // Текущее состояние сервиса
    SERVICE_STATUS ss;
    
    // Идентификатор сервиса
    SERVICE_STATUS_HANDLE ssHandle;
    
    // -----------------------------------------------------
    // Функция main
    // Точка входа процесса
    // -----------------------------------------------------
    void main(int agrc, char *argv[])
    {
      // Таблица точек входа
      SERVICE_TABLE_ENTRY DispatcherTable[] =
      {
        {
          // Имя сервиса
          MYServiceName,
    
          // Функция main сервиса
          (LPSERVICE_MAIN_FUNCTION)ServiceMain
        },
        {
          NULL,
          NULL
        }
    
      };
    
      printf("Sample of simple service\n"
        "(C) A. Frolov, 1996, Email: frolov@glas.apc.org\n");
    
      // Запуск диспетчера
      if(!StartServiceCtrlDispatcher(DispatcherTable))
      {
        fprintf(stdout,
          "StartServiceCtrlDispatcher: Error %ld\n",
          GetLastError());
        getch();
        return;
      }
    }
    
    // -----------------------------------------------------
    // Функция ServiceMain
    // Точка входа сервиса
    // -----------------------------------------------------
    void WINAPI ServiceMain(DWORD argc, LPSTR *argv)
    {
      // Регистрируем управляющую функцию сервиса
      ssHandle =
        RegisterServiceCtrlHandler(MYServiceName, ServiceControl);
    
      if(!ssHandle)
        return;
    
      // Устанавливаем состояние сервиса
    
      // Сервис работает как отдельный процесс
      ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
    
      // Код ошибки при инициализации и завершения сервиса
      // не используется
      ss.dwServiceSpecificExitCode = 0;
    
      // Начинаем запуск сервиса.
      // Прежде всего устанавливаем состояние ожидания
      // запуска сервиса
      ReportStatus(SERVICE_START_PENDING, NO_ERROR, 4000);
    
      // Вызываем функцию, которая выполняет все
      // необходимые инициализирующие действия
      //  ServiceStart(argc, argv);
    
      // После завершения инициализации устанавливаем
      // состояние работающего сервиса
      ReportStatus(SERVICE_RUNNING, NOERROR, 0);
    
      return;
    }
    
    // -----------------------------------------------------
    // Функция ServiceControl
    // Точка входа функции обработки команд
    // -----------------------------------------------------
    void WINAPI ServiceControl(DWORD dwControlCode)
    {
      // Анализируем код команды и выполняем эту команду
      switch(dwControlCode)
      {
        // Команда остановки сервиса
        case SERVICE_CONTROL_STOP:
        {
          // Устанавливаем состояние ожидания остановки
          ss.dwCurrentState = SERVICE_STOP_PENDING;
          ReportStatus(ss.dwCurrentState, NOERROR, 0);
    
          // Выполняем остановку сервиса, вызывая функцию,
          // которая выполняет все необходимые для этого действия
          // ServiceStop();
    
          // Отмечаем состояние как остановленный сервис
          ReportStatus(SERVICE_STOPPED, NOERROR, 0);
          break;
        }
    
        // Определение текущего состояния сервиса
        case SERVICE_CONTROL_INTERROGATE:
        {
          // Возвращаем текущее состояние сервиса
          ReportStatus(ss.dwCurrentState, NOERROR, 0);
          break;
        }
    
        // В ответ на другие команды просто возвращаем
        // текущее состояние сервиса
        default:
        {
          ReportStatus(ss.dwCurrentState, NOERROR, 0);
          break;
        }
      }
    }
    
    // -----------------------------------------------------
    // Функция 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);
    }
    

    В файле small.h (листинг 5.2) определено имя сервиса MYServiceName и прототипы функций.

    Листинг 5.2. Файл service/small/small.h

    
    #define MYServiceName "Sample of simple service"
    
    void WINAPI ServiceMain(DWORD dwArgc, LPSTR *lpszArv);
    void WINAPI ServiceControl(DWORD dwControlCode);
    void ReportStatus(DWORD dwCurrentState,
            DWORD dwWin32ExitCode, DWORD dwWaitHint);
    

    Исходные тексты приложения SRVCTRL

    Главный файл исходных текстов приложения SRVCTRL, предназначенного для управления сервисом Sample of simple service приведен в листинге 5.3.

    Листинг 5.3. Файл service/srvctrl.c

    
    // ==================================================
    // Приложение SRVCTRL
    // Работа с сервисом "Sample of simple service"
    //
    // (С) Фролов А.В., 1996
    // Email: frolov@glas.apc.org
    // ==================================================
    
    #define STRICT
    #include <windows.h>
    #include <windowsx.h>
    #include "resource.h"
    #include "afxres.h"
    
    #include "srvctrl.h"
    
    HINSTANCE hInst;
    char szAppName[]  = "ServiceCtlApp";
    char szAppTitle[] = "Simple Service Control";
    
    // Состояние сервиса
    SERVICE_STATUS ss;
    
    // -----------------------------------------------------
    // Функция WinMain
    // -----------------------------------------------------
    int APIENTRY
    WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
            LPSTR lpCmdLine, int nCmdShow)
    {
      WNDCLASSEX wc;
      HWND hWnd;
      MSG msg;
    
      // Сохраняем идентификатор приложения
      hInst = hInstance;
    
      // Преверяем, не было ли это приложение запущено ранее
      hWnd = FindWindow(szAppName, NULL);
      if(hWnd)
      {
        // Если было, выдвигаем окно приложения на
        // передний план
        if(IsIconic(hWnd))
          ShowWindow(hWnd, SW_RESTORE);
        SetForegroundWindow(hWnd);
        return FALSE;
      }
    
      // Регистрируем класс окна
      memset(&wc, 0, sizeof(wc));
      wc.cbSize = sizeof(WNDCLASSEX);
      wc.hIconSm = LoadImage(hInst,
        MAKEINTRESOURCE(IDI_APPICONSM),
        IMAGE_ICON, 16, 16, 0);
      wc.style = 0;
      wc.lpfnWndProc = (WNDPROC)WndProc;
      wc.cbClsExtra  = 0;
      wc.cbWndExtra  = 0;
      wc.hInstance = hInst;
      wc.hIcon = LoadImage(hInst,
        MAKEINTRESOURCE(IDI_APPICON),
        IMAGE_ICON, 32, 32, 0);
      wc.hCursor = LoadCursor(NULL, IDC_ARROW);
      wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
      wc.lpszMenuName = MAKEINTRESOURCE(IDR_APPMENU);
      wc.lpszClassName = szAppName;
      if(!RegisterClassEx(&wc))
        if(!RegisterClass((LPWNDCLASS)&wc.style))
              return FALSE;
    
      // Создаем главное окно приложения
      hWnd = CreateWindow(szAppName, szAppTitle,
         WS_OVERLAPPEDWINDOW,
         CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
         NULL, NULL, hInst, NULL);
      if(!hWnd) return(FALSE);
    
      // Отображаем окно и запускаем цикл
      // обработки сообщений
      ShowWindow(hWnd, nCmdShow);
      UpdateWindow(hWnd);
      while(GetMessage(&msg, NULL, 0, 0))
      {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
      }
    
      return msg.wParam;
    }
    
    // -----------------------------------------------------
    // Функция WndProc
    // -----------------------------------------------------
    LRESULT WINAPI
    WndProc(HWND hWnd, UINT msg, WPARAM wParam,
            LPARAM lParam)
    {
      switch(msg)
      {
        HANDLE_MSG(hWnd, WM_COMMAND, WndProc_OnCommand);
        HANDLE_MSG(hWnd, WM_DESTROY, WndProc_OnDestroy);
    
        default:
          return(DefWindowProc(hWnd, msg, wParam, lParam));
      }
    }
    
    // -----------------------------------------------------
    // Функция WndProc_OnDestroy
    // -----------------------------------------------------
    #pragma warning(disable: 4098)
    void WndProc_OnDestroy(HWND hWnd)
    {
      PostQuitMessage(0);
      return 0L;
    }
    
    // -----------------------------------------------------
    // Функция WndProc_OnCommand
    // -----------------------------------------------------
    #pragma warning(disable: 4098)
    void WndProc_OnCommand(HWND hWnd, int id,
      HWND hwndCtl, UINT codeNotify)
    {
      // Идентификатор сервиса
      SC_HANDLE schService;
    
      // Идентификатор системы управления сервисами
      SC_HANDLE schSCManager;
    
      LPQUERY_SERVICE_CONFIG  lpBuf;
      DWORD dwBytesNeeded;
      char szBuf[1024];
    
      switch (id)
      {
        case ID_FILE_EXIT:
        {
          // Завершаем работу приложения
          PostQuitMessage(0);
          return 0L;
          break;
        }
    
        // Установка сервиса в систему
        case ID_SERVICE_INSTALL:
        {
          // Открываем систему управления сервисами
          schSCManager = OpenSCManager(NULL, NULL,
            SC_MANAGER_ALL_ACCESS);
    
          if(!schSCManager)
            break;
    
          // Создаем сервис с именем MYServiceName
          schService = CreateService(
            schSCManager,
            MYServiceName,
            MYServiceName,
            SERVICE_ALL_ACCESS,
            SERVICE_WIN32_OWN_PROCESS,
            SERVICE_DEMAND_START,
            SERVICE_ERROR_NORMAL,
            "c:\\ntbk2\\src\\service\\small\\debug\\small.exe",
            NULL,
            NULL,
            "",
            NULL,
            NULL);
    
          // Закрываем идентификатор системы управления
          // сервисами
          CloseServiceHandle(schSCManager);
          break;
        }
    
        // Удаление сервиса из системы
        case ID_SERVICE_REMOVE:
        {
          // Открываем систему управления сервисами
          schSCManager = OpenSCManager(NULL, NULL,
            SC_MANAGER_ALL_ACCESS);
    
          if(!schSCManager)
            break;
    
          // Открываем сервис с именем MYServiceName
          schService = OpenService(
            schSCManager, MYServiceName,
            SERVICE_ALL_ACCESS);
    
          if(!schService)
            break;
    
          // Останавливаем сервис
          ControlService(schService,
            SERVICE_CONTROL_STOP, &ss);
    
          // Вызываем функцию удаления сервиса из системы
          DeleteService(schService);
    
          // Закрываем идентификатор системы управления
          // сервисами
          CloseServiceHandle(schSCManager);
          break;
        }
    
        case ID_SERVICE_START:
        {
          // Открываем систему управления сервисами
          schSCManager = OpenSCManager(NULL, NULL,
            SC_MANAGER_ALL_ACCESS);
    
          if(!schSCManager)
            break;
    
          // Открываем сервис с именем MYServiceName
          schService = OpenService(
            schSCManager, MYServiceName,
            SERVICE_ALL_ACCESS);
    
          if(!schService)
            break;
    
          // Запускаем сервис
          StartService(schService, 0, NULL);
    
          // Закрываем идентификатор системы управления
          // сервисами
          CloseServiceHandle(schSCManager);
          break;
        }
    
        case ID_SERVICE_STOP:
        {
          // Открываем систему управления сервисами
          schSCManager = OpenSCManager(NULL, NULL,
            SC_MANAGER_ALL_ACCESS);
    
          if(!schSCManager)
            break;
    
          // Открываем сервис с именем MYServiceName
          schService = OpenService(
            schSCManager, MYServiceName,
            SERVICE_ALL_ACCESS);
    
          // Останавливаем сервис
          ControlService(schService,
            SERVICE_CONTROL_STOP, &ss);
    
          // Закрываем идентификатор системы управления
          // сервисами
          CloseServiceHandle(schSCManager);
          break;
        }
    
        case ID_SERVICE_GETCONFIGURATION:
        {
          // Открываем систему управления сервисами
          schSCManager = OpenSCManager(NULL, NULL,
            SC_MANAGER_ALL_ACCESS);
    
          if(!schSCManager)
            break;
    
          // Открываем сервис с именем MYServiceName
          schService = OpenService(
            schSCManager, MYServiceName,
            SERVICE_ALL_ACCESS);
    
          if(!schService)
            break;
    
          // Получаем буфер для сохранения конфигурации
          lpBuf = (LPQUERY_SERVICE_CONFIG)malloc(4096);
    
          if(lpBuf != NULL)
          {
            // Сохраняем конфигурацию в буфере
            QueryServiceConfig(schService,
               lpBuf, 4096, &dwBytesNeeded);
    
            // Отображаем некоторые поля конфигурации
            wsprintf(szBuf, "Binary path: %s\n"
              "Start Name: %s\n"
              "Display Name: %s\n",
              lpBuf->lpBinaryPathName,
              lpBuf->lpServiceStartName,
              lpBuf->lpDisplayName);
    
            MessageBox(hWnd, szBuf, szAppTitle,
              MB_OK | MB_ICONINFORMATION);
    
            // Освобождаем буфер
            free(lpBuf);
          }
    
          // Закрываем идентификатор системы управления
          // сервисами
          CloseServiceHandle(schSCManager);
          break;
        }
    
        case ID_HELP_ABOUT:
        {
          MessageBox(hWnd,
            "Simple Service Control\n"
            "(C) Alexandr Frolov, 1996\n"
            "Email: frolov@glas.apc.org",
            szAppTitle, MB_OK | MB_ICONINFORMATION);
          return 0L;
          break;
        }
    
            default:
            break;
      }
      return FORWARD_WM_COMMAND(hWnd, id, hwndCtl, codeNotify,
        DefWindowProc);
    }
    
    

    В файле srvctrl.h (листинг 5.4) определено имя сервиса и прототипы функций.

    Листинг 5.4. Файл service/srvctrl.h

    
    // Имя сервиса
    #define MYServiceName "Sample of simple service"
    
    // -----------------------------------------------------
    // Описание функций
    // -----------------------------------------------------
    LRESULT WINAPI
    WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
    
    void WndProc_OnCommand(HWND hWnd, int id,
      HWND hwndCtl, UINT codeNotify);
    
    void WndProc_OnDestroy(HWND hWnd);
    
    LRESULT WINAPI
    DlgProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam);
    
    BOOL DlgProc_OnInitDialog(HWND hdlg, HWND hwndFocus,
                              LPARAM lParam);
    void DlgProc_OnCommand(HWND hdlg, int id,
      HWND hwndCtl, UINT codeNotify);
    

    Файл resource.h (листинг 5.5) содержит описания констант, которые используются в файле определения ресурсов приложения.

    Листинг 5.5. Файл service/resource.h

    
    //{{NO_DEPENDENCIES}}
    // Microsoft Developer Studio generated include file.
    // Used by srvctrl.rc
    //
    #define IDR_MENU1                       101
    #define IDR_APPMENU                     101
    #define IDI_APPICON                     102
    #define IDI_APPICONSM                   103
    #define ID_FILE_EXIT                    40001
    #define ID_HELP_ABOUT                   40002
    #define ID_SERVICE_INSTALL              40010
    #define ID_SERVICE_REMOVE               40011
    #define ID_SERVICE_START                40012
    #define ID_SERVICE_STOP                 40013
    #define ID_SERVICE_GETCONFIGURATION     40014
    
    // Next default values for new objects
    //
    #ifdef APSTUDIO_INVOKED
    #ifndef APSTUDIO_READONLY_SYMBOLS
    #define _APS_NEXT_RESOURCE_VALUE        105
    #define _APS_NEXT_COMMAND_VALUE         40015
    #define _APS_NEXT_CONTROL_VALUE         1006
    #define _APS_NEXT_SYMED_VALUE           101
    #endif
    #endif
    

    Файл определения ресурсов приложения приведен в листинге 5.6.

    Листинг 5.6. Файл service/srvctrl.rc

    
    //Microsoft Developer Studio generated resource script.
    //
    #include "resource.h"
    
    #define APSTUDIO_READONLY_SYMBOLS
    //////////////////////////////////////////////////////////////
    //
    // Generated from the TEXTINCLUDE 2 resource.
    //
    #include "afxres.h"
    
    //////////////////////////////////////////////////////////////
    #undef APSTUDIO_READONLY_SYMBOLS
    
    //////////////////////////////////////////////////////////////
    // Russian resources
    
    #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_RUS)
    #ifdef _WIN32
    LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT
    #pragma code_page(1251)
    #endif //_WIN32
    
    //////////////////////////////////////////////////////////////
    //
    // Menu
    //
    
    IDR_APPMENU MENU DISCARDABLE
    BEGIN
        POPUP "&File"
        BEGIN
            MENUITEM "E&xit",       ID_FILE_EXIT
        END
        POPUP "&Service"
        BEGIN
            MENUITEM "&Install",    ID_SERVICE_INSTALL
            MENUITEM "&Remove",     ID_SERVICE_REMOVE
            MENUITEM SEPARATOR
            MENUITEM "&Start",      ID_SERVICE_START
            MENUITEM "Sto&p",       ID_SERVICE_STOP
            MENUITEM SEPARATOR
            MENUITEM "&Get configuration",
                     ID_SERVICE_GETCONFIGURATION
        END
        POPUP "&Help"
        BEGIN
            MENUITEM "&About...",    ID_HELP_ABOUT
        END
    END
    
    #ifdef APSTUDIO_INVOKED
    //////////////////////////////////////////////////////////////
    //
    // TEXTINCLUDE
    //
    
    1 TEXTINCLUDE DISCARDABLE
    BEGIN
        "resource.h\0"
    END
    
    2 TEXTINCLUDE DISCARDABLE
    BEGIN
        "#include ""afxres.h""\r\n"
        "\0"
    END
    
    3 TEXTINCLUDE DISCARDABLE
    BEGIN
        "\r\n"
        "\0"
    END
    
    #endif    // APSTUDIO_INVOKED
    
    //////////////////////////////////////////////////////////////
    //
    // Icon
    //
    
    // Icon with lowest ID value placed first to ensure
    // application icon
    // remains consistent on all systems.
    IDI_APPICON             ICON    DISCARDABLE     "srvctrl.ico"
    IDI_APPICONSM           ICON    DISCARDABLE     "srvctrsm.ico"
    #endif    // Russian resources
    //////////////////////////////////////////////////////////////
    #ifndef APSTUDIO_INVOKED
    //////////////////////////////////////////////////////////////
    //
    // Generated from the TEXTINCLUDE 3 resource.
    //
    //////////////////////////////////////////////////////////////
    #endif    // not APSTUDIO_INVOKED
    
    
    [Назад] [Содеожание] [Дальше]


    Hosted by uCoz