Программирование служб Windows 7 с триггерами (ч.1)
Несколько недель назад мы рассмотрели
Что такое службы?
Служба - это внутренний механизм, встроенный в операционную систему Windows. Вы можете считать службы специальными приложениями, работающие вне зависимости от текущего пользовательского контекста. Службы отличаются от обычных приложений тем, что вы можете настроить службу на работу с момента включения (загрузки) системы и до ее выключения, не требуя присутствия пользователя, то есть, службы могут работать, даже если пользователь не выполнил вход в систему.
Мы предпочитаем считать службы запущенными задачами, работающими в фоновом режиме и не затрагивающими операции пользователя. Службы в Windows отвечают за все виды фоновой активности, начиная со службы Remote Procedure Call (RPC), Printer Spooler и вплоть до службы Network Location Awareness.
На протяжении многих лет Windows росла и вместе с ней росло число фоновых служб. Будем честны, фоновые службы в Windows ощущаются довольно болезненно - операционная система изначально поставляется с множеством служб. Помимо этого, независимые разработчики ПО (ISV) и их приложения добавляют еще больше служб. Назовем в пример хотя бы службы обновления программного обеспечения. Вместе с тем, некоторые службы критически важны и требуются в процессе загрузки, в то время как необходимость в других возникает позже, когда определенный пользователь выполняет вход в систему, а иные вовсе не нуждаются в запуске, пока не будут вызваны. Несмотря на это, когда вы смотрите на список запущенных в данный момент служб, вы видите множество служб, которым нет необходимости работать по схеме 24х7.
Что плохого в службах, работающих 24 часа в сутки 7 дней в неделю?
Есть несколько проблем, связанных с наличием служб, работающих по схеме 24х7. Во-первых, зачем что-то должно работать (пускай даже и в фоновом режиме), если в его работе нет нужды? Любой запущенный процесс (включая службы) использует драгоценную память и ресурсы ЦП, которые могли бы использоваться для других приложений и служб. Если вы подсчитаете все службы, запущенные в определенный момент, то они сложатся в значительный объем памяти, дескрипторов, потоков и использование ЦП. Все эти "растрачиваемые" ресурсы понижают общую производительность компьютера, его отзывчивость и заставляют пользователя думать, что его компьютеры вялые и медлительные. К тому же, поскольку множество работающих служб настроены на автоматический запуск (начинают работать со входом в систему), эти службы влияют на время загрузки компьютера.
Во-вторых, эти растрачиваемые ресурсы непосредственным образом сказываются на потреблении электроэнергии. Чем больше нагрузки мы даем на ЦП, тем больше электроэнергии компьютер потребляет. Это может быть критически важно для ноутбуков и может сократить время работы батареи на несколько часов.
В-третьих, постоянная работа непродуктивного программного обеспечения может привести к утечкам памяти и общей нестабильности системы. Это может привести к сбою в работе приложения и, в конце концов, компьютера.
Наконец, если служба работает по схеме 24х7, и если это хорошо известная служба (которая может оказаться у каждого популярного приложения - например, у PDF Reader), то это создает большую поверхность для атаки. Злоумышленник может воспользоваться сведениями о том, что определенное популярное приложение устанавливает службу, работающую в режиме 24х7, и попытаться взломать ее для получения доступа к компьютеру.
Учитывая все вышесказанное, вы можете удивиться, почему так много разработчиков настраивают свои службы на постоянную работу, если у них имеется другая возможность. Даже до Windows 7 было доступно несколько вариантов запуска служб:
- Disabled (Отключена) полностью отключает службу и предотвращает ее запуск и запуск зависимых служб - это означает, что пользователь должен включить службу вручную из Панели управления или командной строки
- Manual (Вручную) запускает службу по надобности (в связи с зависимостями других служб) или при вызове службы из приложения при помощи соответствующих API, как будет показано ниже
- Automatic (Автоматически) запускает службу при входе в систему
- Automatic Delayed (Автоматически (отложенный запуск) - более новый тип запуска, появившийся в Windows Vista, при помощи которого запуск службы происходит после завершения загрузки и выполнения первоначальных операций, что ускоряет запуск системы
К сожалению, многие ISV (включая саму Microsoft) продолжают настраивать свои службы на автоматический (Automated) или автоматический отложенный запуск (Automatic Delayed), поскольку для всех это представляется простейшим решением. Служба просто работает 24х7 и всегда доступна, устраняя любую необходимость проверки зависимостей или того, запущена ли служба.
Можно привести множество примеров существующих служб, которые могут расходовать куда меньше ресурсов и стать безопаснее, не работая 24х7. Например, подумайте о службе обновлений, которая проверяет наличие новых обновлений для приложения. Если компьютер не подключен к сети и не имеет IP-адреса, зачем работать этой службе? Она ничего не может сделать, так зачем оставлять работающей программу, которая ничего не делает? Подумайте о службе управления политиками, которая используется при изменении групповых политик или при подключении компьютера к домену или отключении от него, но сейчас, когда компьютер подключен к моей домашней сети, служба, опять же, работает впустую.
Появление служб с запуском по триггеру
Решение вышеуказанных проблем заключается в выведении службы из "состояния постоянной работы" в другие виды фоновой активности, такие как запланированные задачи или службы, запускаемые триггером. Эта статья посвящена Windows 7 Trigger Start Services. О Windows 7 Scheduled Tasks можно сказать очень много полезного, что и будет сделано в последующих статьях.
[url=http://msdn.microsoft.com/en-us/library/dd405513(VS.85).aspx]Службы, запускаемые по триггеру[/url] (англ. trigger-start service), впервые появились в Windows 7. По сути, это обычная служба, которую вы можете настроить на запуск (или остановку) в случае срабатывания триггера, то есть в определенном случае или состоянии, которые вы сами задаете (например, когда становится доступным IP-адрес или когда он исчезает). Ниже приведен список доступных триггеров, с помощью которых вы можете настроить режим запуска вашей службы:
- Подключение или отключение устройства
- Вход в домен или выход из него
- Открытие или закрытия порта брандмауэра
- Изменение в групповых политиках
- Доступность первого IP-адреса / исчезновение последнего IP-адреса
- Настраиваемое событие - Трассировка событий для Windows (ETW)
Последний пункт указывает на расширяемость. Будучи разработчиком, вы можете настроить любое событие ETW в качестве триггера для службы, что дает вам очень хороший инструмент для управления запуском и остановкой служб вашего приложения.
Так что же такое триггер?
Триггер состоит из:
- Типа события триггера
- Подтипа события триггера
- Действия, которое должно быть предпринято при совершении события триггера
- Одного или более элементов данных, связанных с триггером (для определенных типов событий триггера)
Подтип и связанные с триггером элементы данных вместе устанавливают состояние для уведомления службы о событии. Формат элемента данных зависит от типа события триггера; элемент данных может состоять из бинарных, строковых или многостроковых данных.
Работа с Trigger Start Services
К сожалению, в пользовательском интерфейсе консоли Windows 7 Services MMC нет графического представления Trigger Start Services. Однако у вас есть две возможности. Вы можете по-прежнему использовать старый добрый sc.exe (программа командной строки Service Configuration) или воспользоваться методом WIN32
Использование SC.exe для запроса данных триггера службы (Query Service Trigger Information)
Пора повеселиться. Начнем с получения сведений о конфигурации некоторых служб. Общая форма для использования конфигурации службы выглядит следующим образом:
sc <server> [command] [service name] <option1> <option2>...
Где server опционален, а по умолчанию вы работаете с локальным компьютером:
- command - это операция, которую вы хотите выполнить, например, запрос данных триггера
- service name - это имя службы, с которой вы хотите работать
- options - это различные значения (опции), которые вы можете выполнить для настройки службы
Начнем с запроса определенной службы о ее конфигурации триггера. Для этого нам понадобится запустить окно Windows Shell:
[ist]1. Откройте меню "Пуск".
2. Введите CMD в поле поиска.
3. Выберите cmd.exe.
4. Введите sc qtriggerinfo w32time и нажмите клавишу ввода.
Вот, как это должно выглядеть:
Как вы можете видеть, мы запросили данные триггера службы W32time, которая настроена на запуск при подключении компьютера к домену и остановку при отключении от домена.
Microsoft в Windows 7 обновила приложение командной строки sc.exe для поддержки конфигурации и получения сведений о поддерживаемых триггерах. Введите sc triggerinfo в окне Windows Shell и нажмите клавишу ввода. Результат будет похож на тот, что приведен ниже, и будет содержать все триггеры и сведения о том, как настроить службы на их использование.
ОПИСАНИЕ:
Изменяет параметры активации службы.
USAGE:
sc <сервер> triggerinfo [имя службы] <параметр1> <параметр2>...
ПАРАМЕТРЫ:
start/device/UUID/HwId1/... <Запуск службы после получения строки UUID указанного класса интерфейса устройства с одной или несколькими строками кода оборудования или совместимыми строками кода>
start/custom/UUID/data0/.. <Запуск службы после получения события от строки UUID указанного настраиваемого поставщика трассировки событий Windows с одним или несколькими двоичными элементами данных в формате шестнадцатеричной строки, например,r ABCDABCD, для задания 4 байтов данных>
stop/custom/UUID/data0/... <Остановка службы после получения события от строки UUID указанного настраиваемого поставщика трассировки событий Windows с одним или несколькими двоичными элементами данных в формате шестнадцатеричной строки, например, ABCDABCD, для задания 4 байтов данных>
start/strcustom/UUID/data0/.. <Запуск службы после получения события от строки UUID указанного настраиваемого поставщика трассировки событий Windows с одним или несколькими необязательными элементами данных>
stop/strcustom/UUID/data0/.. <Остановка службы после получения события от строки UUID указанного настраиваемого поставщика трассировки событий Windows с одним или несколькими необязательными элементами данных>
start/networkon <Запуск службы при первом IP-адресе>
stop/networkoff <Остановить службу при отсутствии IP-адресов>
start/domainjoin <Запуск службы при подключении к домену>
stop/domainleave <Остановка службы при отсоединении от домена>
start/portopen/параметр <Запуск службы при открытии сетевого порта. Параметр имеет следующую форму: номер_порта;имя_протокола; путь_к_образу;имя_службы>
stop/portclose/параметр <Остановка службы при закрытии сетевого порта. Параметр имеет следующую форму: номер_порта;имя_протокола; путь_к_образу;имя_службы>
start/machinepolicy <Запуск службы при изменении групповой политики компьютера или при ее наличии на момент загрузки>
start/userpolicy <Запуск службы при изменении групповой политики пользователя или при ее наличии на момент загрузки>
delete <Удаление текущих параметров активации>
<Остановка службы после получения события от строки UUID указанного настраиваемого поставщика трассировки событий Windows с одним или несколькими необязательными элементами данных>
start/networkon <Запуск службы при первом IP-адресе>
stop/networkoff <Остановить службу при отсутствии IP-адресов>
start/domainjoin <Запуск службы при подключении к домену>
stop/domainleave <Остановка службы при отсоединении от домена>
start/portopen/параметр <Запуск службы при открытии сетевого порта. Параметр имеет следующую форму: номер_порта;имя_протокола; путь_к_образу;имя_службы>
stop/portclose/параметр <Остановка службы при закрытии сетевого порта. Параметр имеет следующую форму: номер_порта;имя_протокола; путь_к_образу;имя_службы>
start/machinepolicy <Запуск службы при изменении групповой политики компьютера или при ее наличии на момент загрузки>
start/userpolicy <Запуск службы при изменении групповой политики пользователя или при ее наличии на момент загрузки>
delete <Удаление текущих параметров активации>[/code]
Так всё, что вам нужно для настройки службы на запуск при появлении IP-адреса, - это ввести sc triggerinfo [имя вашей службы] start/networkon, где "имя вашей службы" заменено на имя той службы, которую вы хотите настроить.
Программная настройка Trigger Start Services при помощи ChanceServiceConfig2
Более интересным с точки зрения разработчиков аспектом является создание служб, зависящих от триггера, и использование кода для конфигурации службы. В Windows 7 вы можете использовать функцию ChangeServiceConfig2 для настройки данных триггера службы и функцию QueryServiceConfig2 для их вызова.
Регистрация триггера службы производится вызовом ChangeServiceConfig2 использованием SERVICE_CONFIG_TRIGGER_INFO для параметра dwInfoLevel и представлением данных регистрации триггера в структуре SERVICE_TRIGGER_INFO посредством параметра lpInfo. К тому же, могут быть указаны дополнительные связанные с триггером данные. Ниже приведен пример функции установщика службы, который создает триггер USB-устройства для службы под названием MyService:
[code]define SERVICE_NAME L"MyService"
//set the device guid
static const GUID GUID_USBDevice = {
0x53f56307, 0xb6bf, 0x11d0,
{0x94, 0xf2, 0x00, 0xa0, 0xc9,
0x1e, 0xfb, 0x8b }};
BOOL _SetServiceToStartOnDeviceTrigger()
{
BOOL fResult = FALSE;
SC_HANDLE hScm = OpenSCManager(
NULL, //local machine
NULL, //active database
SC_MANAGER_CONNECT);
if(hScm != NULL)
{
SC_HANDLE hService = OpenService(
hScm,
SERVICE_NAME,
SERVICE_ALL_ACCESS);
If( hService != NULL)
{
LPCWSTR lpszDeviceString = L"USBSTOR\\GenDisk";
SERVICE_TRIGGER_SPECIFIC_DATA_ITEM deviceData = {0};
deviceData.dwDataType = SERVICE_TRIGGER_DATA_TYPE_STRING;
deviceData.cbData =
(wcslen(lpszDeviceString)+1) * sizeof(WCHAR);
deviceData.pData = (PBYTE)lpszDeviceString;
SERVICE_TRIGGER st;
st.dwTriggerType =
SERVICE_TRIGGER_TYPE_DEVICE_INTERFACE_ARRIVAL;
st.dwAction = SERVICE_TRIGGER_ACTION_SERVICE_START;
st.pTriggerSubtype = (GUID *) &GUID_USBDevice;
st.cDataItems = 1;
st.pDataItems = &deviceData;
SERVICE_TRIGGER_INFO sti;
sti.cTriggers = 1;
sti.pTriggers = &st;
sti.pReserved = 0;
fResult = ChangeServiceConfig2(
hService,
SERVICE_CONFIG_TRIGGER_INFO,
&sti);
}
CloseServiceHandle (hService);
}
CloseServiceHandle (hScm);
if(!fResult)
{
printf("Service trigger registration failed (%d)\n",
GetLastError());
}
return fResult;
}[/code]
Примечание: все службы контролируются Service Control Manager (SCM), который мы рассмотрим в другой статье.
Вы можете видеть, как в приведенном выше фрагменте кода мы сначала берем дескриптор (hScm) к SCM вызовом openSCManager. Далее мы вызываем openService и проходим дескриптор к SCM - hscm, и имя службы - SERVICE_NAME, к которой мы хотим получить доступ. Последний параметр, SERVICE_ALL_ACCESS, указывает, что у нас имеется полный доступ к службам. Полагая, что теперь у нас в службе имеется верный дескриптор, мы начинаем создавать отдельную структуру, которой воспользуемся вскоре для настройки службы.
SERVICE_TRIGGER_SPECIFIC_DATA_ITEM задает тип события триггера. Он содержит данные о событии триггера службы. В нашем случае, мы задаем строку, описывающую подключение USB-диска.
Затем мы задаем структуру SERVICE_TRIGGER, которая представляет события триггеру службы. Заметьте, что именно здесь мы задаем тип триггера (подключение устройства), действие (запуск службы), и подтип триггера (определенный род USB-дисков). Следом мы определяем конкретное устройство, которое будет вызывать службу. Заметьте, что вы можете задать список устройств и их GUID. Также следует отметить, что мы не хотим срабатывания триггера запуска службы при подключении любого USB-устройства, как то мышь или камера. Мы хотим, чтобы служба запускалась только при появлении USB-диска.
Наконец, мы задаем структуру SERVICE_TRIGGER_INFO, которая содержит данные события триггера службы. Эта структура просто указывает на структуру SERVICE_TRIGGER, которую мы задали ранее, и количество триггеров, число которых в данном случае равно одному.
Теперь мы можем вызвать функцию ChanceServiceConfig2 и пройти дескриптор к службе, которую мы хотим настроить, параметр SERVICE_CONFIG_TRIGGER_INFO, который указывает, что мы хотим настроить триггер службы, и Null.
Вот и все. Если вы все сделали правильно, то служба запуститься при подключении USB жесткого диска.
В следующей статье я рассмотрю, как написать простую реализацию службы .NET, которую мы настроим на запуск при подключении диска USB.
Вы можете узнать больше о Windows 7 при помощи
Вы также можете
Источник:
Перевод: Sibiryak
Комментарии
По теме
- Федеративный поиск в Windows 7
- Программируем панель задач Windows 7: панель миниатюр
- Использование сенсоров в приложении: реализация в managed-коде (ч.1)
- Использование сенсоров в приложении: реализация в native-коде (ч.1)
- Я тебя чувствую: использование платформы Sensor в Windows 7
- Доступны записи с Windows 7 PDC Boot Camp
- Fishbowl для Facebook с использованием новой панели задач Windows 7
- Программируем для Windows 7 в Visual Studio 2010
- Новая версия Windows API Code Pack
- Программирование служб Windows 7 с триггерами (ч.2)