Поделиться через


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

Предупреждение

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

Из этой статьи вы узнаете, как создать в Visual Studio приложение службы 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, а затем нажмите клавишу ВВОД.

    Появится всплывающее окно, предлагающее переименовать все ссылки на элемент кода Service1.

  2. Выберите Да.

    Запрос на переименование

  3. На вкладке Проект выберите в контекстном меню пункт Свойства. В окне Свойства измените значение ServiceName на MyNewService.

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

  4. В меню Файл выберите команду Сохранить все.

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

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

Добавление возможности работы с настраиваемым журналом событий

  1. В Обозреватель решений в контекстном меню myNewService.cs или MyNewService.vb выберите Просмотреть Designer.

  2. На панели элементов разверните узел Компоненты, а затем перетащите компонент EventLog на вкладку Service1.cs [Конструктор] или Service1.vb [Конструктор] .

  3. В Обозреватель решений в контекстном меню myNewService.cs или MyNewService.vb выберите Просмотреть код.

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

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

    public MyNewService()
    {
        InitializeComponent();
        eventLog1 = new System.Diagnostics.EventLog();
        if (!System.Diagnostics.EventLog.SourceExists("MySource"))
        {
            System.Diagnostics.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
    
  5. using Добавьте оператор в MyNewService.cs (если он еще не существует) или Imports в MyNewService.vb для System.Diagnostics пространства имен:

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

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

В редакторе кода для 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 для пространства имен System.Timers.

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

    // Set up a timer that triggers every minute.
    Timer timer = new Timer();
    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.

Состояния ERVICE_START_PENDING и SERVICE_STOP_PENDING можно реализовать путем добавления кода, вызывающего функцию Windows SetServiceStatus.

Реализация состояния ожидания службы

  1. Добавьте оператор using в файл MyNewService.cs или оператор Imports в файл MyNewService.vb для пространства имен System.Runtime.InteropServices.

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

    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 с помощью вызова неуправляемого кода:

    [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();
    serviceStatus.dwCurrentState = ServiceState.SERVICE_START_PENDING;
    serviceStatus.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. Для установки состояния SERVICE_RUNNING добавьте код в конце метода OnStart:

    // 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();
    serviceStatus.dwCurrentState = ServiceState.SERVICE_STOP_PENDING;
    serviceStatus.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 для проекта Visual C# или ServiceInstaller1 для проекта Visual Basic. Затем в контекстном меню выберите пункт Свойства.

  4. Убедитесь в том, что в окне Свойства свойство ServiceName имеет значение MyNewService.

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

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

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

  6. Введите текст для свойства DisplayName, например Отображаемое имя MyNewService.

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

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

  8. В итоге окно Свойства должно выглядеть так:

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

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

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

    Важно!

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

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

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

Примечание

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

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

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

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

  1. Выберите файл 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
    
  2. В файле MyNewService.cs или MyNewService.vb измените конструктор MyNewService для обработки входного параметра следующим образом:

    using System.Diagnostics;
    
    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;
    }
    
    Imports System.Diagnostics
    
    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
    

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

  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. Для правильного запуска службы пользователь должен заключить путь и каждый параметр в кавычки. Чтобы изменить параметры запуска службы Windows, пользователь может настроить параметры в разделе реестра ImagePath. Однако лучше изменять их программными средствами и создать для пользователей удобный интерфейс для этой возможности, например в виде программы управления или настройки.

Сборка службы

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

    Отобразятся страницы свойств для проекта.

  2. На вкладке Приложение в списке Автоматически запускаемый объект выберите 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 version>. Например, для 64-разрядной версии по умолчанию используется путь %windir%\Microsoft.NET\Framework64\v4.0.30319\InstallUtil.exe.

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

    • Класс RunInstallerAttribute отсутствует в классе ProjectInstaller.
    • Значение атрибута отличается от true.
    • Класс ProjectInstaller не определен как public.

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

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

  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. Создать установщик для службы Windows можно с помощью набора инструментов WiX. Другие идеи можно почерпнуть в статье о создании пакета установщика.

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

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

См. также