Руководство. Создание приложения службы Windows

В этой статье показано, как создать приложение службы Windows для .NET Framework в Visual Studio. Служба просто записывает сообщения в журнал событий.

Замечание

Эта статья не относится к размещенным службам в .NET. Последние сведения об использовании Microsoft.Extensions.Hosting.BackgroundService служб Windows и шаблоне рабочей службы см. в следующих статье:

Создание сервиса

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

  1. В меню "Файл Visual Studio" выберите новый>проект (или нажмите клавиши CTRL SHIFT++N), чтобы открыть окно "Новый проект".

  2. Найдите и выберите шаблон проекта службы Windows (.NET Framework ).

    Замечание

    Если шаблон службы Windows не отображается, может потребоваться установить рабочую среду разработки настольных приложений .NET с помощью Visual Studio Installer.

  3. В поле "Имя проекта" введите MyNewService и нажмите кнопку "Создать".

    Откроется вкладка "Конструктор" (Service1.cs [конструктор] или Service1.vb [конструктор].

    Шаблон проекта включает класс компонента с именем Service1 , наследуемым от System.ServiceProcess.ServiceBase. Он включает большую часть базового кода службы, например код для запуска службы.

Переименование службы

Переименуйте службу из Service1 в MyNewService.

  1. В обозревателе решений выберите Service1.cs или Service1.vb и выберите "Переименовать " в контекстном меню. Переименуйте файл в MyNewService.cs или MyNewService.vb, а затем нажмите клавишу ВВОД.

    Появится всплывающее окно с запросом на переименование всех ссылок на элемент Code Service1.

  2. Во всплывающем окне нажмите кнопку "Да".

    Переименуйте запрос

  3. Выберите "Сохранить все" в меню "Файл ".

Добавление функций в службу

В этом разделе описано, как добавить настраиваемый журнал событий в службу Windows. Компонент EventLog является примером типа компонента, который можно добавить в службу Windows.

Добавление функций пользовательского журнала событий

  1. В окне Toolbox раскройте группу Components и перетащите компонент EventLog в дизайнера Service1.cs [Design] или Service1.vb [Design].

    Подсказка

    Если окно панели элементов не отображается, выберите "Вид>панели элементов".

  2. В обозревателе решений в контекстном меню для MyNewService.cs или MyNewService.vb выберите пункт "Просмотреть код".

  3. Определите пользовательский журнал событий.

    Для C# измените существующий MyNewService() конструктор, как показано в следующем фрагменте кода. Для Visual Basic добавьте New() конструктор, как показано в следующем фрагменте кода.

    public MyNewService()
    {
        InitializeComponent();
        eventLog1 = new EventLog();
        if (!EventLog.SourceExists("MySource"))
        {
            EventLog.CreateEventSource("MySource", "MyNewLog");
        }
        eventLog1.Source = "MySource";
        eventLog1.Log = "MyNewLog";
    }
    
    ' To access the constructor in Visual Basic, select New from the
    ' method name drop-down list. 
    Public Sub New()
        MyBase.New()
        InitializeComponent()
        Me.EventLog1 = New System.Diagnostics.EventLog
        If Not System.Diagnostics.EventLog.SourceExists("MySource") Then
            System.Diagnostics.EventLog.CreateEventSource("MySource",
            "MyNewLog")
        End If
        EventLog1.Source = "MySource"
        EventLog1.Log = "MyNewLog"
    End Sub
    
  4. Если он еще не существует, добавьте директиву using в MyNewService.cs или оператор Imports в MyNewService.vb для пространства имен System.Diagnostics:

    using System.Diagnostics;
    
    Imports System.Diagnostics
    
  5. Выберите "Сохранить все" в меню "Файл ".

Определение того, что происходит при запуске службы

В редакторе кода для MyNewService.cs или MyNewService.vb найдите OnStart метод. Visual Studio автоматически создал пустое определение метода при создании проекта. Добавьте код, который записывает запись в журнал событий при запуске службы:

protected override void OnStart(string[] args)
{
    eventLog1.WriteEntry("In OnStart.");
}
' To access the OnStart in Visual Basic, select OnStart from the
' method name drop-down list. 
Protected Overrides Sub OnStart(ByVal args() As String)
    EventLog1.WriteEntry("In OnStart")
End Sub

Голосование

Так как служебное приложение предназначено для длительного выполнения, обычно оно опрашивает или отслеживает систему, которую вы настраиваете в методе OnStart. Метод OnStart должен вернуться в операционную систему после начала операции службы, чтобы система не была заблокирована.

Чтобы настроить простой механизм опроса, используйте System.Timers.Timer компонент. Таймер инициирует событие Elapsed через регулярные интервалы, в тот момент ваша служба может выполнять мониторинг. Вы используете компонент Timer следующим образом:

  • Задайте свойства Timer компонента в методе MyNewService.OnStart .
  • Запустите таймер, вызвав Start метод.
Настройка механизма опроса
  1. using Добавьте директиву в MyNewService.cs или Imports инструкцию в MyNewService.vb для этого пространства имен:

    using System.Timers;
    
    Imports System.Timers
    
  2. Добавьте следующий код в MyNewService.OnStart событие, чтобы настроить механизм опроса:

    // Set up a timer that triggers every minute.
    Timer timer = new Timer
    {
        Interval = 60000 // 60 seconds
    };
    timer.Elapsed += new ElapsedEventHandler(this.OnTimer);
    timer.Start();
    
    ' Set up a timer that triggers every minute.
    Dim timer As Timer = New Timer()
    timer.Interval = 60000 ' 60 seconds
    AddHandler timer.Elapsed, AddressOf Me.OnTimer
    timer.Start()
    
  3. MyNewService В классе добавьте переменную-член. Он содержит идентификатор следующего события для записи в журнал событий:

    private int eventId = 1;
    
    Private eventId As Integer = 1
    
  4. MyNewService В классе добавьте OnTimer метод для обработки Timer.Elapsed события:

    public void OnTimer(object sender, ElapsedEventArgs args)
    {
        // TODO: Insert monitoring activities here.
        eventLog1.WriteEntry("Monitoring the System", EventLogEntryType.Information, eventId++);
    }
    
    Private Sub OnTimer(sender As Object, e As Timers.ElapsedEventArgs)
       ' TODO: Insert monitoring activities here.
       eventLog1.WriteEntry("Monitoring the System", EventLogEntryType.Information, eventId)
       eventId = eventId + 1
    End Sub
    

Вместо выполнения всей работы в основном потоке можно выполнять задачи с помощью фоновых рабочих потоков. Дополнительные сведения см. в разделе System.ComponentModel.BackgroundWorker.

Определение того, что происходит при остановке службы

Вставьте строку кода в метод, который добавляет запись в OnStop журнал событий при остановке службы:

protected override void OnStop()
{
    eventLog1.WriteEntry("In OnStop.");
}
Protected Overrides Sub OnStop()
    EventLog1.WriteEntry("In OnStop.")
End Sub

Определите другие действия для службы

Можно переопределить методы OnPause, OnContinue и OnShutdown, чтобы определить дополнительную обработку для вашего компонента.

В следующем коде показано, как переопределить OnContinue метод в MyNewService классе:

protected override void OnContinue()
{
    eventLog1.WriteEntry("In OnContinue.");
}
Protected Overrides Sub OnContinue()
    EventLog1.WriteEntry("In OnContinue.")
End Sub

Настройка состояния службы

Службы сообщают о состоянии диспетчера управления службами , чтобы пользователь смог определить, работает ли служба правильно. По умолчанию служба, наследующая от ServiceBase, сообщает ограниченный набор параметров состояния, включая SERVICE_STOPPED, SERVICE_PAUSED и SERVICE_RUNNING. Если служба занимает некоторое время для запуска, полезно сообщить SERVICE_START_PENDING о состоянии.

Вы можете установить SERVICE_START_PENDING и SERVICE_STOP_PENDING настройки статуса, добавив код, вызывающий функцию Windows SetServiceStatus.

  1. using Добавьте директиву в MyNewService.cs или Imports инструкцию в MyNewService.vb для этого пространства имен:

    using System.Runtime.InteropServices;
    
    Imports System.Runtime.InteropServices
    
  2. Добавьте следующее перечисление и структуру в MyNewService.cs или MyNewService.vb, чтобы объявить значения и добавить структуру для статуса, которую вы будете использовать в вызове функции платформы.

    public enum ServiceState
    {
        SERVICE_STOPPED = 0x00000001,
        SERVICE_START_PENDING = 0x00000002,
        SERVICE_STOP_PENDING = 0x00000003,
        SERVICE_RUNNING = 0x00000004,
        SERVICE_CONTINUE_PENDING = 0x00000005,
        SERVICE_PAUSE_PENDING = 0x00000006,
        SERVICE_PAUSED = 0x00000007,
    }
    
    [StructLayout(LayoutKind.Sequential)]
    public struct ServiceStatus
    {
        public int dwServiceType;
        public ServiceState dwCurrentState;
        public int dwControlsAccepted;
        public int dwWin32ExitCode;
        public int dwServiceSpecificExitCode;
        public int dwCheckPoint;
        public int dwWaitHint;
    };
    
    Public Enum ServiceState
        SERVICE_STOPPED = 1
        SERVICE_START_PENDING = 2
        SERVICE_STOP_PENDING = 3
        SERVICE_RUNNING = 4
        SERVICE_CONTINUE_PENDING = 5
        SERVICE_PAUSE_PENDING = 6
        SERVICE_PAUSED = 7
    End Enum
    
    <StructLayout(LayoutKind.Sequential)>
    Public Structure ServiceStatus
        Public dwServiceType As Long
        Public dwCurrentState As ServiceState
        Public dwControlsAccepted As Long
        Public dwWin32ExitCode As Long
        Public dwServiceSpecificExitCode As Long
        Public dwCheckPoint As Long
        Public dwWaitHint As Long
    End Structure
    

    Замечание

    Диспетчер управления службами использует dwWaitHint и dwCheckpoint члены структуры SERVICE_STATUS , чтобы определить время ожидания запуска или завершения работы службы Windows. Если ваши методы OnStart и OnStop выполняются долго, служба может выделить больше времени, вызывая SetServiceStatus еще раз с увеличенным значением dwCheckPoint.

  3. В классе MyNewService объявите функцию SetServiceStatus, используя Platform Invoke:

    [DllImport("advapi32.dll", SetLastError = true)]
    private static extern bool SetServiceStatus(System.IntPtr handle, ref ServiceStatus serviceStatus);
    
    Declare Auto Function SetServiceStatus Lib "advapi32.dll" (ByVal handle As IntPtr, ByRef serviceStatus As ServiceStatus) As Boolean
    
  4. Чтобы реализовать SERVICE_START_PENDING состояние, добавьте следующий код в начало OnStart метода:

    // Update the service state to Start Pending.
    ServiceStatus serviceStatus = new ServiceStatus
    {
        dwCurrentState = ServiceState.SERVICE_START_PENDING,
        dwWaitHint = 100000
    };
    SetServiceStatus(this.ServiceHandle, ref serviceStatus);
    
    ' Update the service state to Start Pending.
    Dim serviceStatus As ServiceStatus = New ServiceStatus()
    serviceStatus.dwCurrentState = ServiceState.SERVICE_START_PENDING
    serviceStatus.dwWaitHint = 100000
    SetServiceStatus(Me.ServiceHandle, serviceStatus)
    
  5. Добавьте код в конец OnStart метода, чтобы задать состояние SERVICE_RUNNINGследующим образом:

    // Update the service state to Running.
    serviceStatus.dwCurrentState = ServiceState.SERVICE_RUNNING;
    SetServiceStatus(this.ServiceHandle, ref serviceStatus);
    
    ' Update the service state to Running.
    serviceStatus.dwCurrentState = ServiceState.SERVICE_RUNNING
    SetServiceStatus(Me.ServiceHandle, serviceStatus)
    
  6. (Необязательно) Если OnStop это длительный метод, повторите эту процедуру в методе OnStop . Реализуйте статус SERVICE_STOP_PENDING и верните статус SERVICE_STOPPED перед завершением метода OnStop.

    Рассмотрим пример.

    // Update the service state to Stop Pending.
    ServiceStatus serviceStatus = new ServiceStatus
    {
        dwCurrentState = ServiceState.SERVICE_STOP_PENDING,
        dwWaitHint = 100000
    };
    SetServiceStatus(this.ServiceHandle, ref serviceStatus);
    
    // Update the service state to Stopped.
    serviceStatus.dwCurrentState = ServiceState.SERVICE_STOPPED;
    SetServiceStatus(this.ServiceHandle, ref serviceStatus);
    
    ' Update the service state to Stop Pending.
    Dim serviceStatus As ServiceStatus = New ServiceStatus()
    serviceStatus.dwCurrentState = ServiceState.SERVICE_STOP_PENDING
    serviceStatus.dwWaitHint = 100000
    SetServiceStatus(Me.ServiceHandle, serviceStatus)
    
    ' Update the service state to Stopped.
    serviceStatus.dwCurrentState = ServiceState.SERVICE_STOPPED
    SetServiceStatus(Me.ServiceHandle, serviceStatus)
    

Добавление установщиков в службу

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

  1. В обозревателе решений в контекстном меню для MyNewService.cs или MyNewService.vb выберите конструктор представлений.

  2. В режиме конструктора выберите фоновую область, а затем выберите "Добавить установщик " в контекстном меню.

    По умолчанию Visual Studio добавляет класс компонента с именем ProjectInstaller, который содержит два установщика в проект. Эти установочные программы предназначены для вашей службы и связанного с ней процесса.

  3. В представлении конструктора для ProjectInstaller выберите serviceInstaller1 для проекта C# или ServiceInstaller1 для проекта Visual Basic, а затем выберите "Свойства" в контекстном меню.

  4. В окне свойств установите свойство ServiceName на значение MyNewService.

  5. Добавьте текст в Description свойство, например пример службы.

    Этот текст отображается в столбце "Описание " окна "Службы " и описывает службу пользователю.

    Описание службы в окне

  6. Добавьте текст в DisplayName свойство. Например, отображаемое имя MyNewService.

    Этот текст отображается в столбце Имя для отображения окна Службы. Это имя может отличаться от ServiceName свойства, которое является именем, которое использует система (например, имя, используемое для команды для net start запуска службы).

  7. StartType Задайте для свойства Automatic значение из раскрывающегося списка.

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

    Свойства установщика для службы Windows

  9. В представлении конструктора для ProjectInstaller выберите serviceProcessInstaller1 для проекта C# или ServiceProcessInstaller1 для проекта Visual Basic, а затем выберите "Свойства" в контекстном меню. Account Задайте для свойства LocalSystem значение из раскрывающегося списка.

    Этот параметр устанавливает службу и запускает ее с помощью локальной системной учетной записи.

    Это важно

    У LocalSystem учетной записи есть широкие разрешения, включая возможность записи в журнал событий. Используйте эту учетную запись с осторожностью, так как это может увеличить риск атак со стороны вредоносного программного обеспечения. Для других задач рекомендуется использовать LocalService учетную запись, которая выступает в качестве не привилегированного пользователя на локальном компьютере и предоставляет анонимные учетные данные любому удаленному серверу. Однако этот пример завершается ошибкой, если вы пытаетесь использовать учетную запись LocalService, поскольку учетной записи требуется разрешение на запись в журнал событий.

Дополнительные сведения о установщиках см. в статье "Практическое руководство. Добавление установщиков в приложение службы".

(Необязательно) Настройка параметров запуска

Замечание

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

Служба Windows может принимать аргументы командной строки, также известные как параметры запуска. При добавлении кода для обработки параметров запуска пользователь может запустить службу с собственными настраиваемыми параметрами запуска в окне свойств службы. Однако эти параметры запуска не сохраняются при следующем запуске службы.

Чтобы задать параметры запуска безвозвратно, задайте их в реестре. У каждой службы Windows имеется запись реестра в подразделе HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services. В подразделе каждой службы используйте подраздел Параметры для хранения сведений, к которым может получить доступ ваша служба. Файлы конфигурации приложений можно использовать для службы Windows так же, как и для других типов программ. Пример кода см. в разделе ConfigurationManager.AppSettings.

Добавление параметров запуска

  1. В MyNewService.cs или MyNewService.vb измените MyNewService конструктор, чтобы принять и обработать входной параметр:

    public MyNewService(string[] args)
    {
        InitializeComponent();
    
        string eventSourceName = "MySource";
        string logName = "MyNewLog";
    
        if (args.Length > 0)
        {
            eventSourceName = args[0];
        }
    
        if (args.Length > 1)
        {
            logName = args[1];
        }
    
        eventLog1 = new EventLog();
    
        if (!EventLog.SourceExists(eventSourceName))
        {
            EventLog.CreateEventSource(eventSourceName, logName);
        }
    
        eventLog1.Source = eventSourceName;
        eventLog1.Log = logName;
    }
    
    Public Sub New(ByVal cmdArgs() As String)
        InitializeComponent()
        Dim eventSourceName As String = "MySource"
        Dim logName As String = "MyNewLog"
        If (cmdArgs.Count() > 0) Then
            eventSourceName = cmdArgs(0)
        End If
        If (cmdArgs.Count() > 1) Then
            logName = cmdArgs(1)
        End If
        eventLog1 = New EventLog()
        If (Not EventLog.SourceExists(eventSourceName)) Then
            EventLog.CreateEventSource(eventSourceName, logName)
        End If
        eventLog1.Source = eventSourceName
        eventLog1.Log = logName
    End Sub
    

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

  2. Выберите Program.cs или MyNewService.Designer.vb, а затем выберите "Просмотреть код " в контекстном меню. В методе Main измените код, чтобы добавить входной параметр и передать его конструктору службы:

    static void Main(string[] args)
    {
        ServiceBase[] ServicesToRun;
        ServicesToRun = new ServiceBase[]
        {
            new MyNewService(args)
        };
        ServiceBase.Run(ServicesToRun);
    }
    
    Shared Sub Main(ByVal cmdArgs() As String)
        Dim ServicesToRun() As System.ServiceProcess.ServiceBase = New System.ServiceProcess.ServiceBase() {New MyNewService(cmdArgs)}
        System.ServiceProcess.ServiceBase.Run(ServicesToRun)
    End Sub
    
  3. Чтобы указать аргументы командной строки, добавьте следующий код в ProjectInstaller класс в ProjectInstaller.cs или ProjectInstaller.vb:

    protected override void OnBeforeInstall(IDictionary savedState)
    {
        string parameter = "MySource1\" \"MyLogFile1";
        Context.Parameters["assemblypath"] = "\"" + Context.Parameters["assemblypath"] + "\" \"" + parameter + "\"";
        base.OnBeforeInstall(savedState);
    }
    
    Protected Overrides Sub OnBeforeInstall(ByVal savedState As IDictionary)
        Dim parameter As String = "MySource1"" ""MyLogFile1"
        Context.Parameters("assemblypath") = """" + Context.Parameters("assemblypath") + """ """ + parameter + """"
        MyBase.OnBeforeInstall(savedState)
    End Sub
    

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

Создание службы

  1. В обозревателе решений выберите "Свойства " в контекстном меню проекта MyNewService .

  2. На вкладке "Приложение" в списке объектов startup выберите MyNewService.Program (или Sub Main для проектов Visual Basic).

  3. Чтобы создать проект, в обозревателе решений выберите "Сборка" из контекстного меню проекта (или нажмите клавиши CTRL SHIFT++B).

Установка службы

Теперь, когда вы создали службу Windows, ее можно установить. Чтобы установить службу Windows, необходимо иметь учетные данные администратора на компьютере, на котором он установлен.

  1. Откройте командную строку разработчика для Visual Studio с учетными данными администратора.

  2. В командной строке разработчика для Visual Studio перейдите в папку, содержащую выходные данные проекта (по умолчанию — подкаталог \bin\Debug проекта).

  3. Введите следующую команду:

    installutil MyNewService.exe
    

    Если служба установлена успешно, команда сообщает об этом.

    Если система не может найти installutil.exe, убедитесь, что она существует на компьютере. Это средство устанавливается вместе с .NET Framework в папку %windir%\Microsoft.NET\Framework[64]\<версия framework>.

    Если процесс installutil.exe завершается ошибкой, проверьте журнал установки, чтобы узнать, почему. По умолчанию журнал находится в той же папке, что и исполняемый файл службы. Установка может завершиться ошибкой, если:

    • Класс RunInstallerAttribute отсутствует в ProjectInstaller классе.
    • Атрибут не задан значением true.
    • Класс ProjectInstaller не определен как public.
    • Вы не открыли командную строку разработчика для VS от имени администратора.

Дополнительные сведения см. в разделе "Практическое руководство. Установка и удаление служб".

Запуск и работа службы

  1. В Windows откройте классическое приложение "Службы" : нажмите клавишу Windows+R , чтобы открыть поле запуска , введите services.msc и нажмите клавишу ВВОД или нажмите кнопку "ОК".

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

    MyNewService в окне

  2. Чтобы запустить службу, выберите "Пуск " из контекстного меню службы.

  3. Чтобы остановить службу, выберите "Остановить " в контекстном меню службы.

  4. (Необязательно) В командной строке используйте имя< службы net start > и имя< службы net stop>, чтобы запустить и остановить службу.

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

  1. В Windows откройте классическое приложение "Средство просмотра событий": введите "Средство просмотра событий" в строке поиска Windows и выберите "Средство просмотра событий" в результатах поиска.

    Подсказка

    В Visual Studio вы можете получить доступ к журналам событий, открыв обозреватель серверов в меню "Вид " (или нажав клавиши CTRL+ALT+S) и расширив узел журналов событий для локального компьютера.

  2. В средстве просмотра событий разверните журналы приложений и служб.

  3. Найдите список для MyNewLog (или MyLogFile1 , если вы выполнили процедуру для добавления аргументов командной строки) и разверните его. Вы увидите записи для двух действий (запуска и остановки), выполненных службой.

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

Очистите ресурсы

Если вам больше не нужен приложение службы Windows, его можно удалить.

  1. Откройте командную строку разработчика для Visual Studio с учетными данными администратора.

  2. В командной строке разработчика для Visual Studio перейдите в папку, содержащую исполняемый файл проекта.

  3. Введите следующую команду:

    installutil.exe /u MyNewService.exe
    

    Если служба успешно удаляется, команда сообщает, что служба успешно удалена. Дополнительные сведения см. в разделе "Практическое руководство. Установка и удаление служб".

Дальнейшие шаги

Теперь, когда вы создали службу, вы можете:

  • Создайте автономную программу установки, которую могли бы использовать другие пользователи для установки службы Windows. Используйте набор инструментов WiX для создания установщика для службы Windows. Другие идеи см. в разделе "Создание пакета установщика".

  • Изучите ServiceController компонент, который позволяет отправлять команды в установленную вами службу.

  • Вместо создания журнала событий при запуске приложения используйте установщик для создания журнала событий при установке приложения. Журнал событий удаляется установщиком при удалении приложения. Дополнительные сведения см. в разделе EventLogInstaller.

См. также