Смекни!
smekni.com

Розробка служби Win32 (стр. 2 из 3)

2. Перетворювати попередню функцію точки входу main() в ServiceMain(), яка буде реєструвати обробник керування служби і інформувати SCM про його стан. Другий код зберігається практично без змін.

3. створити обробник керування служби, який буде відповідати на команди SCM.

Описавши ці три етапи, ми будемо по мірі необхідності давати додаткову інформацію по створенню, запуску служб і керуванню ними. Далі ці дії описанні краще.

Функція main().

Задача нової функції main(), яку викликає SCM, полягає в реєструванні служби і в запуску диспетчера керування службою. Для цього потрібно визвати функцію StartServiceCtrlDispatcher з ім’ям (іменами) і точкою (точками) входу однієї або декількох логічних служб.

BOOLStartServiceCtrlDispatcher (

LPSERVICE_TABLE_ENTRYlpServiceStartTable)

Єдиний параметр lpServiceStartTable є адресою масиву SERVICE_TABLE_ENTRY, кожен елемент якого містить ім’я і точку входу логічної служби. Кінець масиву позначається парою елементів з значеннями NULL.

Виклик StartServiceCtrlDispatcher в головному потоці процесу служби приводить до приєднання потоку до SCM в формі потоку диспетчера керування службою SCMне повертає керування, поки всі служби не будуть закінчені. Але потрібно замітити, що в цей момент логічні служби фактично ще не запускаються.

В прикладі 1 показана проста головна програма служби, яка містить одну логічну службу.

Приклад 1. Програма main: головна точка входу служби.

#include “EvryThng.h”

void WINAPI ServiceMain (DWORD argc, LPTSTR argv []);

static LPTSTR ServiceName = _T (“SocketCommandLineService”);

/* головна процедура, яка запускає диспетчер керуваня службою. */

VOID _tmain (int argc, LPTSTR argv [])

{

SERVICE_TABLE_ENTRY DispatchTable [] =

{

{ ServiceName, SeviceMain },

{ NULL, NULL }

};

if (!StartServiceCtrlDispatcher (DispatchTable))

ReportErrjr (_T

(“Не вдалося запустити диспетчер керування службою.”),

1, TRUE);

/*Servoce<aom () не буде працювати, поки її не запустить SCM.*/

/*Повернення назад відбувається тільки після завершення всіх служб.*/

return;

Функції ServiceMain()

Ці функції визначені в таблиці служб, як показано в прикладі 1, і представляють логічні служби. Це, по суті, вдосконалені версії основної програми, яка перетворюється в службу, причому кожну логічну службу SCM викликає в окремому потоці. В свою чергу, логічна служба може запускати додаткові потоки, наприклад потоки-робочі сервера, які застосовувалися в програмах serverSKі serverNP. Часто в службі NT знаходиться тільки одна логічна служба. Можливо реалізувати в складі одної служби NT логічні служби як на базі сокетів, так і на базі іменованих каналів, якщо задати дві головні функції служби.

Також тут є додатковий код для реєстрації обробника керування служби – функції, яка SCM викликає для керування службами.

Реєстрація обробника керування служби

Обробник керування служби, який викликається диспетчером SCM, повинен бути здатним керувати відповідною логічною службою. Обробник керування консолі в програмі serverSK, який встановлює глобальний прапор закінчення, в обмеженій формі демонструє, що повинен представляти собою обробник. Але кожна логічна служба повина зареєструвати обробник з використанням функції Register-ServiceCtrlHandler.

SERVICE_STATUS_HANDLE

RegisterServiceCtrlHandler (

LPCTSCR lpServiceName,

LPHANDLER_FUNCTION lp&HandlerProc)

Параметри

lpServiceName – вказане користувачем ім’я служби, яке представлене в елементі таблиці, який відповідає цій логічній службі.

lpHandlerProc – адрес функції обробника, яка буде описана нижче.

Функція повертає дескриптор об’єкта SERVICE_STATUS_HANDLE, рівний нулю, якщо вийшла помилка; для аналізу помилок можна застосовувати звичайні методи.

Встановлення стану служби

Коли обробник зареєстрований, то наступна задача полягає в тому, щоб встановити стан служби; в даний момент це SERVICE_STAER_PENDING. Функція, яка для цього застосовується, SetServiceStatus також використовується в деяких інших місцях і повина викликати періодично, щоб інформувати SCM про стан служби (інтервал вказується в полі параметра-структури стану).

Структура service_status

Структура SERVICE_STATUS визначена слідуючим чином:

typedef struct _SERVICE_STATUS {

DWORD dwServiceType;

DWORD dwCurrentState;

WORD dwControlsAccepted;

DWORD dwWin32ExitCode;

DWORD dwServiceSpecificExitCode;

DWORD dwCheckPoint;

DWORD dwWaitHint;

} SERVICE_STATUS, *LPBERVICE_STATUS;

Параметри

dwWin32ExitCode – звичайний код закінчення потоку логічної служби, яка повинна присвоїти йому значення NO_ERROR в ході виконання і при нормальному завершені.

dwServiceSpecificExitCode можна ви користувати для вказання помилок вводу запуску або закінчення служби, але це значення буде проігнорованим, якщо dwWin32ExitCode не присвоєне значення ERROR_SERVICE_SPECIFIC_ERROR.

dwCheckPoint – контрольний показчик проходження службою всіх її значень разом з ініціалізацією і закінченням, які служба повинна періодично зчитувати. Це значення не використовується, якщо служба не має затримки при закінченні, зупинці і відновлені.

dwWaitHint – інтервал в мілісекундах між викликами SetServiceSpecificExitCode і збільшеним значенням dwCheckPoint або зміненим значенням dwCurrentState. SCM може припустити, що виникла помилка, якщо інтервал протікає без такого виклику SetServiceStatus.

Тип служби

Елемент dwServiceTypeповинен мати одне із значень, які приведені в таблиці 3.

Для наших цілей тип служби майже завжди буде мати значення SERVICE_WIN32_OWN_PROCESS, хоча різні значення свідчать про те, що служби можуть грати різні ролі.

Таблиця 3. Типи служб.

Значення Тип служби
SERVICE_WIN32_OWN_PROCESS Служба Win32, яка працює в окремому процесі зі своїми власними ресурсами. Використовується в таблиці 2.
SERVICE_WIN_SHARE_PROCESS Служба Win32, яка використовує процес спільно з іншими службами, так що декілька служб може спільно користуватися ресурсами, змінними і т.д.
SERVICE_KERNEL_DRIVER Драйвер пристрою Windows NT
SERVICE_FILE_SYSTEM_DRIVER Драйвер файлової системи Windows NT
SERVICE_INTERACTIVE_PROCESS Процес служби Win32, який може взаємодіяти з користувачем через робочий стіл

Стан служби

Параметр dwCurrentStateвизначає поточний стан служби. Можливі значення цього параметра приведені в табл.4.

Таблиця 4. Значення стану служби.

Значення Стан
SERVICE_STOPPED Служба зовсім не працює, так як не була запущена
SERVICE_START_PENDING Служба знаходиться в процесі запуску, але ще не готова відповідати не запити. Наприклад, потоки-робочі ще не запущені
SERVICE_STOP_PENDING Служба зупиняється, але ще не закінчилася до кінця. Наприклад, глобальний прапор закінчення може бути встановленим, але потоки-робочі ще не прореагували
SERVICE_RUNNING Служба працює
SERVICE_CONTINUE_PENDING Виконується відновлення служби після зупинки
SERVICE_PAUSE_PENDING Служба переходить в стан зупинка, але перехід ще не закінчений
SERVICE_PAYSED Служба призупинена

Коди керування, що сприймаються

Параметр dwControlsAccepted визначає коди керування, які служба сприймає і обробляє в своєму обробнику керування. Можливі значення перераховані в таблиці 5; декілька значень можуть об’єднуватися порозрядним „або”. Приведена нижче версія serverSK для служби сприймає всі три значення. Додаткові значення описані в документації MSDN.

Таблиця 5. Коди керування, які сприймаються службою

Значення Дія
SERVICE_ACCEPT_STOP Дозволяється SERVICE_CONTROL_STOP
SERVICE_ACCEPT_PAUSR_CONTINUE Дозволяється SERVICE_CONTROL_PAUSE і SERVICE_CONTROL_CONTINUE
SERVICE_ACCEPT_SHUTDOWN (функція ControlService не може видавати цей код керування) Служба повідомляє, коли система закінчує роботу. Таким чином, система може послати службі значенняSERVICE_CONTROL_ SHUTDOWN

Код служби

Коли обробник зареєстрований і встановлений стан служби SERVICE_START_PENDING, служба може ініціалізувати себе і встановити стан знову. В прикладі перетворення serverSK після того, як сокети ініціалізовані і сервер буде готовий до прийому клієнтів, слідує встановити стан SERVICE_RUNNING.

Обробник керування служби

Обробник керування служби, який вказаний в функції RegisterServiceCtrlHandler, має наступну форму:

VOID WINAPI ServerCtrlHandler (DWORD fdwControl)

Єдиний параметр цієї функції, fdwControl, містить сигнал керування, який передається диспетчером SCM для обробки. Таким чином, обробник керування є узагальненою формою обробника керування консолі.

Сигнали керування можуть мати слідуючи значення:

SERVICE_CONTROL_STOP

SERVICE_CONTROL_PAUSE

SERVICE_CONTROL_CONTINUE

SERVICE_CONTROL_INTERROGATE

SERVICE_CONTROL_SHUTDOWN

Також допускаються значення користувача в діапазоні 128-255, але тут вони не розглядаються.

Обробник викликається диспетчером SCMв тому ж потоці, що і головна програма, і звичайно будується на базі оператора switch.

Керування службами WindowsNT

Наступна задача після написання коду – передача служби під керуванням SCM, щоб її можна було запускати, закінчувати і виконувати інші керуючі операції.

Для цього потрібно виконати декілька дій з відкриття SCM, створення служби під керуванням SCM і потім з її запуску. Ці дії не керують службою безпосередньо; вони представляють собою команди для SCM, який працює з вказаною службою.

Відкриття SCM

Для створення служби необхідний окремий процес, який грає роль „адміністратора”. Перша дія – відкриття SCMі отримання дескриптору, який потім дозволяє створити службу.

SC_HANDLE OpenSCManager (

LPCTSTR lpMachineName,

LPCTSTR lpDatabaseName,

DWORD dwDesiredAddress)

Параметри

lpMachineName рівний NULL, якщо SCM знаходиться в локальній системі, хоча можна звертатися до SCM і на інших машинах в мережі.

lpDatabaseName – також звичайно NULL.

dwDesiredAddress – звичайно SC_MANAGER_ALL_ACCESS, але можна задати більш обмежені права доступу, як описано в вбудованій документації.

Створення і видалення служби

Нові служби фіксуються в слідую чому розділі системного реєстру: