Программирование служб Windows 7 с триггерами (часть 2)

В предыдущей статье о программировании служб Windows 7 с триггерами мы представили Windows 7 Trigger Services в качестве способа оптимизировать ваши службы для повышения производительности и безопасности. В этой статье мы рассмотрим, как превратить службу, настроенную на автоматический запуск, в службу, запускающуюся по триггеру, т.е. только после того, как в системе произошло определенное событие. Мы используем WPF-приложение (управляемый код), которое регистрирует и следит за службой (также созданной с использованием .NET). Для связи между миром .NET и Win32 API, о которой шла речь в прошлой статье, мы используем слой взаимодействия C++/CLI.

Приложение-пример состоит из 3 частей:

  • Слой взаимодействия C++/CLI, который обеспечивает обычный и простой интерфейс .NET API в приложении контроллера
  • Приложение контроллера WPF, которое позволяет вам регистрировать и запускать службу
  • Простая служба .NET, которая проверяет наличие запоминающего устройства USB (жесткого диска) и на нем ищет папку под названием «ToCopy» для копирования из нее файлов в локальную папку «C:\FromUSB».

Изображение иллюстрирует структуру решения:

image

Давайте начнем с рассмотрения реализации кода службы .NET. Это простая служба Windows, написанная на C#. Ее цель заключается в автоматическом копировании изображений с запоминающего устройства USB, подключенного к вашему компьютеру, на локальный жесткий диск – «c:\FromUSB».

Служба реализована в USBService.cs. Этот класс наследует базовый класс ServiceBase и переопределяет методы OnStart и OnStop. В этом классе имеется метод DoWork, который и выполняет все копирование с диска USB на локальный диск. Метод DoWork записывает данные в файл журнала, за которым мы будем следить.

Наиболее интересная часть службы заключается в методе OnStart. Он вызывается сразу же после запуска службы. Заметьте, что первая строка кода проверяет, настроена ли служба на запуск по триггеру. Если условие оператора if ложно, то создается новая копия таймера, которая производит опрос каждые 5 секунд. До появления Windows 7 это был единственный способ реализации подобной службы, то есть требовалось регулярное опрашивание системы на наличие USB-устройства. Кроме того, данной службе для опроса системы нужно быть запущенной постоянно (24х7). Это приводит к крайне неразумному расходованию ресурсов, не позволяет системе переходить в энергосберегательный режим и, наряду с некоторыми другими отрицательными факторами, увеличивает поверхность атаки приложения.

Но с Windows 7 можно настроить такую службу при помощи триггера подключения устройства USB. Это означает, что служба не будет работать до тех пор, пока не подключено устройство USB, а точнее универсальное дисковое устройство USB. Мы перейдем к этой части решения уже через секунду, но сейчас, если вы посмотрите на метод OnStart, то заметите, что мы проверяем, настроена ли служба как служба на запуск по триггеру; если это так, мы просто вызываем в другом потоке метод DoWork, как это показано в приведенном ниже фрагменте кода. Все это должно хорошо работать, поскольку служба НЕ включена и будет запущена только после срабатывания триггера. И, затем, для обработки очереди работы здесь используется пул потоков.

  protected override void OnStart(string[] args)
 {
   if (ServiceControl.IsServiceTriggerStart(ServiceName))
   {
      ThreadPool.QueueUserWorkItem(_ => DoWork());
   }
   else
   {
     _timer = new Timer(_ => DoWork());
     _timer.Change(0, 5000);
   }
 }

Пространство имен ServiceControl содержит слой взаимодействия C++/CLI. Этот слой использует C++/CLI в качестве связующего элемента между неуправляемым API и приложением WPF. Основной файл ServiceControlInterop.cpp содержит всю функциональность, которая необходима и которая используется приложением WPF. Например, используя приложение контроллера, мы можем использовать AddService(…) или RemoveService(…) для добавления или удаления службы соответственно. Мы также может настроить службу для запуска по триггеру либо при подключении устройства USB, либо при появлении IP-адреса, используя SetServiceTriggerStartOnUSBArrival или SetServiceTriggerStartOnIPAddressArrival соответственно. Рассмотрение реализации обеих функций показывает, что обе они действует одинаково. Они:

  • Сначала используют OpenSCManager, чтобы поместить дескриптор в Service Control Manager (SCM)
  • Затем используется дескриптор SCM OpenService, чтобы получить настоящий дескриптор службы, которую мы хотим настроить
  • Наконец, они вызывают ChangeServiceconfig2 для настройки соответствующего триггера

Обо всем этом уже говорилось подробно в предыдущей статье Программирование служб Windows 7 с триггерами (ч.1) .

Можно загрузить образец кода этого приложения. Обратите внимание, что вам потребуется запустить Visual Studio с правами администратора (см. изображение ниже), так как вам потребуется регистрировать, запускать и останавливать службы. Также потребуется Windows 7 SDK для компиляции той части решения, которая написана на С++.

image

После компиляции и запуска стандартного приложения (приложения WPF) вы увидите следующую картину.

image

Это основное окно приложения контроллера WPF. Отсюда вы можете создавать службы, нажимая на кнопку Create Manual.

Затем откройте окно служб, набрав «Services» в поле поиска меню «Пуск». Вы должны увидеть окно служб. Найдите USBCopyService; она должна выглядеть следующим образом:

image

Нажмите кнопку Run (Запуск) и затем кнопку Refresh (Обновить) в окне служб или просто нажмите F5. Вы не увидите больших изменений, но статус USBCopyService должен измениться с Manual на Started, как показано ниже:

image

Повторное обращение к приложению покажет активность службы в файле журнала. Как вы можете видеть на нижеприведенном изображении, служба проявляла активность каждые 5 секунд и опрашивала систему в поисках USB-устройства:

image

Нажмите кнопку Stop, чтобы остановить службу и затем нажмите Delete Service, чтобы удалить ее. Теперь нажмите кнопку Trigger Start, чтобы зарегистрировать и настроить службу на запуск по триггеру при подключении универсального дискового устройства USB. Если вы проверите окно служб, то увидите, что для службы USBCopyService указано «manual», в то время как на самом деле она настроена для запуска по событию триггера (просто для этого нет графического представления).

Если вы подключите USB-диск с папкой «ToCopy», служба запустится и скопирует файлы в c:\FromUSB. Не самая лучшая реализация, но это всего лишь демонстрация. На изображении, приведенном ниже, в файле журнала имеется всего одна строка, потому что служба была запущена лишь единожды; она выполнила метод DoWork и затем завершила свою работу. Она не работала постоянно и не опрашивала систему каждые 5 секунд и, следовательно, не растрачивала ресурсы и не была подвержена угрозам безопасности.

Заключение

Разработка службы в Windows 7 с запуском по триггеру может быть несколько сложнее, чем обычный автозапуск службы, которая постоянно работает от загрузки до выключения. Но практически все умещается в нескольких строках кода. В то же самое время эти несколько строк кода могут оказать очень большое влияние в плане сохранения ресурсов и безопасности. Так что в следующий раз, когда вы будете разрабатывать новую службу Windows, попытайтесь встроить в нее триггеры.

Вы можете узнать больше о Windows 7, обратившись к Windows 7 Training Kit for Developers или просмотрев видео о Windows 7 на Channel 9.

Вы также можете попрактиковаться в работе с Windows 7 Trigger Start Services, используя онлайн-тренинги Windows 7, являющиеся частью Channel 9 Learning Center.