Мониторинг служб Windows

31

Для мониторинга и управления службами Windows можно применять доступную в консоли ММС оснастку Services (Службы), которая находится в узле Computer Management (Управление компьютером). Кроме того, в состав любой системы Windows входит утилита командной строки net.ехе, которая тоже позволяет управлять службами, и более функциональная утилита sc.exe. Управлять службами также можно и прямо из окна Server Explorer в Visual Studio.

В этой статье будет показано, как с помощью класса System.ServiceProcess.ServiceController создать свое небольшое приложение Windows специально для осуществления мониторинга и управления службами.

Оснастка Services консоли ММС

С помощью оснастки Services (Службы), которая предлагается в консоли ММС (Microsoft Management Console — консоль управления Microsoft), можно просматривать информацию о состоянии всех служб:

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

Двойной щелчок на службе QuoteService в окне этой оснастки приводит к открытию диалогового окна Properties (Свойства). В этом диалоговом окне можно будет увидеть имя службы, ее описание, путь к ее исполняемой программе, режим запуска и состояние. В настоящий момент служба находится в запущенном состоянии. На вкладке Log On (Вход в систему) этого окна при желании можно изменить учетную запись, от имени которой должно осуществляться управление процессом службы:

Свойства службы

Утилита net.ехе

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

Например, команда net start позволяет просмотреть список запущенных служб, команда net start <имя_службы> — запустить любую необходимую службу, а команда net stop <имя_службы> — отправить запрос на останов указанной службы. Приостанавливать и возобновлять работу интересующей службы можно с помощью, соответственно, команд net pause и net continue (разумеется, данная служба поддерживает такое поведение).

Утилита sc.eхе

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

Окно Server Explorer в Visual Studio

Управлять службами можно также с помощью предлагаемого в Visual Studio окна Server Explorer, выбрав в нем элемент Services (Службы). Этот элемент находится внутри элемента с именем текущего компьютера, который, в свою очередь, расположен в дереве под элементом Servers (Серверы). Выделив внутри этого элемента желаемую службу и открыв контекстное меню, можно запускать и останавливать службу. Также с помощью этого контекстного меню можно добавлять в проект класс ServiceController.

Чтобы добавить в приложение класс ServiceController для конкретной службы, необходимо перетащить интересующую службу из окна Server Explorer в область конструктора; экземпляр этого класса будет автоматически добавлен в приложение. Свойства этого экземпляра автоматически настраиваются так, чтобы открыть доступ к выбранной службе. В код также добавляется ссылка на сборку System.ServiceProcess. После этого данный экземпляр можно использовать для управления службой так, как описано в следующем разделе.

Создание специального класса ServiceController

В настоящем разделе рассматривается пример создания небольшого приложения Windows, в котором используется класс ServiceController для мониторинга и управления службами Windows. Создаваемое приложение представляет собой приложение WPF, пользовательский интерфейс которого имеет вид, показанный на рисунке:

Интерфейс программы для мониторинга служб

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

XAML-разметка данного окна выглядит следующим образом:

<Window x:Class="Wrox.ProCSharp.WinServices.ServiceControlWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Service Control" Height="300" Width="300">
    <Window.Resources>
        <DataTemplate x:Key="listTemplate">
            <Label Content="{Binding Path=DisplayName}"/>
        </DataTemplate>
    </Window.Resources>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <Grid Margin="6,11,14,7" MinHeight="50" MinWidth="50" Name="grid1" Grid.Column="1"  >
            <Grid.RowDefinitions>
                <RowDefinition />
                <RowDefinition />
                <RowDefinition />
                <RowDefinition />
                <RowDefinition />
                <RowDefinition />
                <RowDefinition />
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>
            <TextBlock Margin="4" VerticalAlignment="Center"  Grid.Row="0" Grid.ColumnSpan="2" Name="textDisplayName"  
                       Text="{Binding Path=DisplayName, Mode=OneTime}" />
            <TextBlock Margin="4" VerticalAlignment="Center" Grid.Row="1" Grid.ColumnSpan="2" Name="textStatus" 
                       Text="{Binding Path=ServiceStatusName, Mode=OneTime}" />
            <TextBlock Margin="4" VerticalAlignment="Center"   Grid.Row="2" Grid.ColumnSpan="2" Name="textType" 
                       Text="{Binding Path=ServiceTypeName, Mode=OneTime}" />
            <TextBlock Margin="4" VerticalAlignment="Center"  Grid.Row="3" Grid.ColumnSpan="2" Name="textName" 
                       Text="{Binding Path=ServiceName, Mode=OneTime}" />
            <Button Click="OnServiceCommand" Margin="3"  Grid.Row="4" Grid.Column="0" Name="buttonStart" 
                    Content="Start" IsEnabled="{Binding Path=EnableStart, Mode=OneTime}" />
            <Button Click="OnServiceCommand" Margin="3"  Grid.Row="4" Grid.Column="1" Name="buttonStop" 
                    Content="Stop" IsEnabled="{Binding Path=EnableStop, Mode=OneTime}" />
            <Button Click="OnServiceCommand" Margin="3" Grid.Row="5" Grid.Column="0" Name="buttonPause" 
                    Content="Pause" IsEnabled="{Binding Path=EnablePause, Mode=OneTime}" />
            <Button Click="OnServiceCommand" Margin="3"  Grid.Row="5" Grid.Column="1" Name="buttonContinue" 
                    Content="Continue" IsEnabled="{Binding Path=EnableContinue, Mode=OneTime}" />
            <Button Margin="3"  Grid.Row="6" Grid.Column="0" Name="buttonRefresh" Content="Refresh" Click="OnRefresh" />
            <Button Margin="3"  Grid.Row="6" Grid.Column="1" Name="buttonExit" Content="Exit" Click="OnExit"  />
        </Grid>
        <ListBox Grid.Row="0" Grid.Column="0" HorizontalAlignment="Left" Name="listBoxServices" 
                 VerticalAlignment="Top" IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding}" ItemTemplate="{StaticResource listTemplate}">
        </ListBox>
    </Grid>
</Window>

С помощью класса ServiceController можно получать подробную информацию о каждой службе. В таблице приведено краткое описание свойств этого класса:

Свойство Описание
CanPauseAndContinue Возвращает true, если службе могут отправляться запросы на приостановку и возобновление работы.
CanShutdown Возвращает true, если служба имеет обработчик для завершения работы системы.
CanStop Возвращает true, если служба может быть остановлена.
DependentServices Возвращает коллекцию зависимых служб. В случае останова службы сначала останавливаются все ее зависимые службы.
ServicesDependentOn Возвращает коллекцию служб, от которых зависит данная.
DisplayName Специфицирует имя, которое должно отображаться для данной службы.
MachineName Специфицирует имя машины, на которой выполняется служба.
ServiceName Специфицирует имя службы.
ServiceType Специфицирует тип службы. Служба может запускаться как внутри разделяемого процесса, который могут использовать несколько служб (вроде Win32SharedProcess), так и внутри отдельного процесса, который может использовать только одна служба (наподобие Win320wnProcess). Если служба способна взаимодействовать с рабочим столом, тогда ее тип будет interactiveProcess.
Status Отражает состояние службы. Состояние может принимать следующие значения: работает (running), остановлена (stopped), временно приостановлена (paused), находится в некотором промежуточном состоянии — в процессе запуска (start pending), в процессе останова (stop pending) и т.п. Все возможные значения состояния определены в перечислении ServiceControllerStatus.

В создаваемом здесь приложении для отображения информации о службах используются свойства DisplayName, ServiceName, ServiceType и Status, а для включения и отключения доступа к кнопкам Pause (Пауза), Continue (Продолжить) и Stop (Остановить) — свойства CanPauseAndContinue и CanStop.

Для отображения текущей информации о службе в классе ServiceControllerInfo предусмотрены доступные только для чтения свойства DisplayName, ServiceName, ServiceTypeName и ServiceStatusName. В реализации свойств DisplayName и ServiceName просто производится доступ к свойствам DisplayName и ServiceName лежащего в основе класса ServiceController. В реализации свойств ServiceTypeName и ServiceStatusName выполняется немного больше работы: информация о состоянии и типе службы не может быть возвращена столь же просто, поскольку вместо чисел, которые возвращает класс ServiceController, должны отображаться строки.

Свойство ServiceTypeName возвращает строку, отражающую тип службы. Значение ServiceType, получаемое из свойства ServiceController.ServiceType, представляет собой набор флагов, которые могут объединяться с помощью битовой операции "ИЛИ". Бит InteractiveProcess может устанавливаться вместе с Win320wnProcess и Win32ShareProcess. Поэтому перед проверкой остальных значений проверяется, был ли установлен бит InteractiveProcess. В случае служб возвращаемые строки выглядят как "Win32 Service Process" или "Win32 Shared Process":

public class ServiceControllerInfo
    {
        private readonly ServiceController controller;

        public ServiceControllerInfo(ServiceController controller)
        {
            this.controller = controller;
        }

        public ServiceController Controller
        {
            get { return controller; }
        }

        public string ServiceTypeName
        {
            get
            {
                ServiceType type = controller.ServiceType;
                string serviceTypeName = String.Empty;
                if ((type & ServiceType.InteractiveProcess) != 0)
                {
                    serviceTypeName = "Interactive ";
                    type -= ServiceType.InteractiveProcess;
                }
                switch (type)
                {
                    case ServiceType.Adapter:
                        serviceTypeName += "Adapter";
                        break;

                    case ServiceType.FileSystemDriver:
                    case ServiceType.KernelDriver:
                    case ServiceType.RecognizerDriver:
                        serviceTypeName += "Driver";
                        break;

                    case ServiceType.Win32OwnProcess:
                        serviceTypeName += "Win32 Service Process";
                        break;

                    case ServiceType.Win32ShareProcess:
                        serviceTypeName += "Win32 Shared Process";
                        break;

                    default:
                        serviceTypeName += "unknown type " + type.ToString();
                        break;
                }
                return serviceTypeName;
            }
        }

        public string ServiceStatusName
        {
            get
            {
                switch (controller.Status)
                {
                    case ServiceControllerStatus.ContinuePending:
                        return "Continue Pending";
                    case ServiceControllerStatus.Paused:
                        return "Paused";
                    case ServiceControllerStatus.PausePending:
                        return "Pause Pending";
                    case ServiceControllerStatus.StartPending:
                        return "Start Pending";
                    case ServiceControllerStatus.Running:
                        return "Running";
                    case ServiceControllerStatus.Stopped:
                        return "Stopped";
                    case ServiceControllerStatus.StopPending:
                        return "Stop Pending";
                    default:
                        return "Unknown status";
                }
            }
        }

        public string DisplayName
        {
            get { return controller.DisplayName; }
        }

        public string ServiceName
        {
            get { return controller.ServiceName; }
        }

        public bool EnableStart
        {
            get
            {
                return controller.Status == ServiceControllerStatus.Stopped;
            }
        }

        public bool EnableStop
        {
            get
            {
                return controller.Status == ServiceControllerStatus.Running;
            }
        }

        public bool EnablePause
        {
            get
            {
                return controller.Status == ServiceControllerStatus.Running &&
                      controller.CanPauseAndContinue;
            }
        }

        public bool EnableContinue
        {
            get
            {
                return controller.Status == ServiceControllerStatus.Paused;
            }
        }

    }

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

Пройди тесты
Лучший чат для C# программистов