Создание службы для Windows
Последнее обновление: 16.10.2019
Одним из важнейших компонентов ОС Windows являются службы. Фактически это отдельные приложения, которые не имеют графического интерфейса и
которые выполняют различные задачи в фоновом режиме. Службы могут быть запущены при старте операционной системы, так и в любой другой момент работы пользователя.
Распространенным примером служб являются различные веб-серверы, которые в фоновом режиме прослушивают
определенный порт на наличие подключений, и если подключения имеются, то взаимодействуют с ними. Это могут быть также различные вспомогательные сервисы
обновлений для других установленных программ, которые обращаются к серверу, чтобы узнать, есть ли новая версия приложения. В общем то мы можем
открыть панель служб и сами увидеть все установленные и запущенные службы:
Рассмотрим, как создавать свои службы в C#. В качестве реализуемой задачи выберем наблюдение за изменениями в определенной папке в файловой системе.
Теперь создадим для ее выполнения службу.
Вначале создадим новый проект, который будет иметь тип Windows Service. Назовем проект FileWatcherService:
После этого Visual Studio генерирует проект, который имеет все необходимое. Хотя в принципе нам необязательно выбирать именно этот тип проекта,
можно было бы создать проект библиотеки классов, и затем в нем определить все необходимые классы.
Итак, новый проект выглядит следующим образом:
Здесь также есть файл Program.cs и есть собственно узел службы Service1.cs.
Служба представляет обычное приложение, но она не запускаетс сама по себе. Все вызовы и обращения к ней проходят через менеджер управления службами
(Service Control Manager или SCM). Когда служба запускается автоматически при старте системы или вручную, то SCM обращается к методу Main в классе Program:
static class Program { static void Main() { ServiceBase[] ServicesToRun; ServicesToRun = new ServiceBase[] { new Service1() }; ServiceBase.Run(ServicesToRun); } }
Метод Main по умолчанию определен таким образом, чтобы запускать сразу несколько служб, которые определены в массиве ServicesToRun. Однако по умолчанию
проект содержит только одну службу Service1. Сам запуск производится с помощью метода Run: ServiceBase.Run(ServicesToRun)
.
Сама запускаемая служба представлена узлом Service1.cs. Однако на самом деле
это не простой файл кода. Если мы откроем этот узел, то увидим в нем файл дизайнера службы Service1.Designer.cs и класс Service1.
Класс Service1 собственно представляет службу. По умолчанию он имеет следующий код:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Diagnostics; using System.Linq; using System.ServiceProcess; using System.Text; using System.Threading.Tasks; namespace FileWatcherService { public partial class Service1 : ServiceBase { public Service1() { InitializeComponent(); } protected override void OnStart(string[] args) { } protected override void OnStop() { } } }
Класс службы должен наследоваться от базового класса ServiceBase. Этот класс определяет ряд методов, важнейшие из которых
метод OnStart(), который запускает действия, выпоняемые службой, и метод OnStop(), останавливающий
службу.
После того, как SCM вызовет метод Main и зарегистрирует службу, происходит непосредственный ее вызов через запуск метода OnStart.
Когда в консоли служб или через командную строку мы посылаем команду на остановку службы, то SCM обращается к методу OnStop для ее остановки.
Кроме этих двух методов в классе службы можно переопределить еще несколько методов базового класса ServiceBase:
-
OnPause: вызывается при приостановке службы
-
OnContinue: вызывается при возобновлении работы службы после ее приостановки
-
OnShutdown: вызывается при завершении работы Windows
-
OnPowerEvent: вызывается при изменении режима электропитания
-
OnCustomCommand: вызывается при получении службой пользовательской команды от Менеджера Управления Службами (Service Control Manager / SCM)
В конструкторе класса Service1 вызывается метод InitializeComponent()
, который определен в файле дизайнера Service1.Designer.cs:
namespace FileWatcherService { partial class Service1 { private System.ComponentModel.IContainer components = null; protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } private void InitializeComponent() { components = new System.ComponentModel.Container(); this.ServiceName = "Service1"; } } }
Единственное, что надо в нем отметить, это установка названия службы (свойство ServiceName):
this.ServiceName = "Service1";
Это то название, которое будет отображаться в консоли служб после установки данной службы. Мы можем его изменить, а можем и оставить как есть.
Теперь изменим код службы следующим образом:
using System; using System.ServiceProcess; using System.IO; using System.Threading; namespace FileWatcherService { public partial class Service1 : ServiceBase { Logger logger; public Service1() { InitializeComponent(); this.CanStop = true; this.CanPauseAndContinue = true; this.AutoLog = true; } protected override void OnStart(string[] args) { logger = new Logger(); Thread loggerThread = new Thread(new ThreadStart(logger.Start)); loggerThread.Start(); } protected override void OnStop() { logger.Stop(); Thread.Sleep(1000); } } class Logger { FileSystemWatcher watcher; object obj = new object(); bool enabled = true; public Logger() { watcher = new FileSystemWatcher("D:\\Temp"); watcher.Deleted += Watcher_Deleted; watcher.Created += Watcher_Created; watcher.Changed += Watcher_Changed; watcher.Renamed += Watcher_Renamed; } public void Start() { watcher.EnableRaisingEvents = true; while(enabled) { Thread.Sleep(1000); } } public void Stop() { watcher.EnableRaisingEvents = false; enabled = false; } // переименование файлов private void Watcher_Renamed(object sender, RenamedEventArgs e) { string fileEvent = "переименован в " + e.FullPath; string filePath = e.OldFullPath; RecordEntry(fileEvent, filePath); } // изменение файлов private void Watcher_Changed(object sender, FileSystemEventArgs e) { string fileEvent = "изменен"; string filePath = e.FullPath; RecordEntry(fileEvent, filePath); } // создание файлов private void Watcher_Created(object sender, FileSystemEventArgs e) { string fileEvent = "создан"; string filePath = e.FullPath; RecordEntry(fileEvent, filePath); } // удаление файлов private void Watcher_Deleted(object sender, FileSystemEventArgs e) { string fileEvent = "удален"; string filePath = e.FullPath; RecordEntry(fileEvent, filePath); } private void RecordEntry(string fileEvent, string filePath) { lock (obj) { using (StreamWriter writer = new StreamWriter("D:\\templog.txt", true)) { writer.WriteLine(String.Format("{0} файл {1} был {2}", DateTime.Now.ToString("dd/MM/yyyy hh:mm:ss"), filePath, fileEvent)); writer.Flush(); } } } } }
Ключевым классом, который инкапсулирует всю функциональность, является класс Logger. С помощью объекта FileSystemWatcher он будет вести мониторинг изменений в папке D://Temp.
В методе Start()
устанавливается, что мы будем отслеживать изменения через объект FileSystemWatcher. И вся работа будет идти, пока булевая переменная enabled равна true
. А метод Stop()
позволит
завершить работу класса.
События FileSystemWatcher позволяют отслеживать все изменения в наблюдаемой папке.
При этом будет вестись запись изменений в файл templog.txt. Чтобы не было гонки ресурсов
за файл templog.txt, в который вносятся записи об изменениях, процедура записи блокируется заглушкой lock(obj)
.
В итоге после создания, изменения, переименования и удаления файл лога будет содержать что-то наподобие:
30.07.2015 12:15:40 файл D:\Temp\Новый текстовый документ.txt был создан 30.07.2015 12:15:46 файл D:\Temp\Новый текстовый документ.txt был переименован в D:\Temp\hello.txt 30.07.2015 12:15:55 файл D:\Temp\hello.txt был изменен 30.07.2015 12:15:55 файл D:\Temp\hello.txt был изменен 30.07.2015 12:16:01 файл D:\Temp\hello.txt был удален
В самом классе службы Service1 в конструкторе устанавливается ряд опций:
this.CanStop = true; // службу можно остановить this.CanPauseAndContinue = true; // службу можно приостановить и затем продолжить this.AutoLog = true; // служба может вести запись в лог
В методе OnStart()
для запуска объекта Logger вызывется новый поток:
protected override void OnStart(string[] args) { logger = new Logger(); Thread loggerThread = new Thread(new ThreadStart(logger.Start)); loggerThread.Start(); }
Новый поток нужен, так как текущий поток обрабатывает только команды SCM и должен возвращаться из метода OnStart как можно быстрее.
Когда от менеджера SCM поступает команда на остановку службы, срабатывает метод OnStop, который вызывает метод logger.Stop()
. Дополнительная задержка
позволит потоку логгера остановиться:
protected override void OnStop() { logger.Stop(); Thread.Sleep(1000); }
Однако самого класса службы еще недостаточно. Нам необходимо еще создать устанощик службы.
Хочу описать процесс создание и тестирования службы Windows на C#. В качестве среды разработки буду использовать Visual Studio 2015. В качестве примера создам службу, которая пишет в файл данные о событиях. Исходный код проекта доступен на GitHub.
Для создания службы я создаю решение с тремя проектами:
Приложение Windows Forms
Создание новой службы начинается, с создания windows приложения (шаблон проекта «Приложение Windows Forms»). Оно нам понадобится для тестирования службы. Приложение будет состоять из одной формы:
Разработка логики
На втором этапе создается проект «Библиотека классов». Именно здесь создаются классы описывающие логику работы будущей службы. Выделение логики в библиотеку классов позволяют нам пока не думать о реализации службы, а вести разработка стандартной windows программы. Плюсом такого подхода является: простота тестирования, легкость повторного использования кода, возможность создания автоматических тестов. В данном примере в библиотеке классов мы создадим только один класс:
public class LogFile : System.IDisposable
{
StreamWriter file;
public string FileName { get; private set; }
public LogFile()
{
CreateFile();
}
~LogFile()
{
Dispose();
}
public void Dispose()
{
if (file != null)
{
file.Close();
}
}
private void CreateFile()
{
FileName = Path.GetTempFileName();
file = File.CreateText(FileName);
}
public void Write(string text)
{
file.WriteLine(text);
}
}
Для тестирования — используем приложение созданное на первом этапе.
Создание службы
Когда логика новой службы написана и оттестирована, приступаем к созданию службы. В качестве шаблона используем стандартный шаблон: «Служба Windows». Реализация службы должна выглядеть примерно так:
public partial class MainService : ServiceBase
{
Logical.LogFile file;
public MainService()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
file = new LogFile();
file.Write("OnStart");
}
protected override void OnStop()
{
file.Write("OnStop");
file.Dispose();
}
protected override void OnPause()
{
file.Write("OnPause");
}
protected override void OnContinue()
{
file.Write("OnContinue");
}
}
Не забываем вставлять ссылку на библиотеку классов. Настраиваем установку службы.
Тестирование службы
Для финального тестирования возвращаемся к приложению Windows Form. Добавляем в него ссылку на созданную службу и создаем новый класс производный от класса описывающего созданную службу:
class ServiceTest : MainService
{
public void TestStart(string[] args)
{
OnStart(args);
}
public void TestStop()
{
OnStop();
}
public void TestPause()
{
OnPause();
}
public void TestContinue()
{
OnContinue();
}
}
Создание этого класса позволяет нам вызывать методы службы из диалогового окна нашего приложения. Для примера — эмуляция старта службы:
private void btStart_Click(object sender, EventArgs e)
{
service = new ServiceTest();
service.TestStart(new string[0]);
btStart.Enabled = false;
btStop.Enabled = true;
btPause.Enabled = true;
btContinue.Enabled = false;
}
После тестирования — можно разворачивать нашу службу.
Особенности служб Windows
Написав свою первую службу по работе с БД, столкнулся с загадочной ошибкой. При тестировании все работало замечательно, но периодически, при перезагрузке компьютера, служба не стартовала. Оказалось, что если сервер БД не успевал запуститься к моменту старта моей службы — возникала ошибка. Поэтому, когда пишите службу, необходимо понимать, какие приложения вы используете и будут ли они доступны в момент запуска службы
Ссылки
Полный текст решения.
Пример создания службы в msdn.
This article introduces Windows Services in .NET and how to create a Windows Service in C# and .NET using Visual Studio.
What is a Windows Service?
Windows Services are non-UI software applications that run in the background. Windows services usually start when an operating system boots and is scheduled to run in the background to execute some tasks. Windows services can also be started automatically or manually. You can also manually pause, stop and restart Windows services.
Windows service is a computer program that runs in the background to execute some tasks. Some examples of Windows services are auto-update of Windows, check emails, print documents, SQL Server Agent, file and folder scanning and indexing, etc. If you open your Task Manager and click on the Services tab, you will see hundreds of services running on your machine. You can also see the statuses of these services. Some services are running, some have paused, and some have stopped. You can start, stop, and pause a service from here by right click on the service.
You may also find all services running on your machine in the following ways:
- Go to Control Panel and select «Services» inside «Administrative Tools.»
- Next, open the Run window (Window + R), type services.msc, and press ENTER.
How to create a Windows service in C#?
Let’s create a Windows Service in C# using Visual Studio.
Step 1
Open Visual Studio, click File > New, and select a project. Next, select a new project from the Dialog box, select «Window Service,» and click the OK button.
Step 2
Go to Visual C# ->» Windows Desktop» ->» Windows Service,» give an appropriate name and then click OK.
Once you click the OK button, the below screen will appear, which is your service.
Step 3
Right-click on the blank area and select «Add Installer.»
How to Add an Installer to a Windows Service
Before you can run a Windows Service, you need to install the Installer, which registers it with the Service Control Manager.
After Adding Installer, ProjectInstaller will add to your project, and ProjectInstakker.cs file will be open. Don’t forget to save everything (by pressing the ctrl + shift + s key)
Solution Explore looks like this:
Step 4
Right-click on the blank area and select «View Code»
Step 5
It has Constructor, which contains the InitializeComponent method:
The InitializeComponent method contains the logic which creates and initializes the user interface objects dragged on the forming surface and provides the Property Grid of Form Designer.
Very important: Don’t ever try to call any method before the call of the InitializeComponent process.
Step 6
Select the InitializeComponent method and press the F12 key to go definition.
Step 7
Now add the below line:
this.serviceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.LocalSystem;
You also can add a description and display the service name (optionally).
this.serviceInstaller1.Description = "My First Service demo";
this.serviceInstaller1.DisplayName = "MyFirstService.Demo";
Step 8
In this step, we will implement a timer and code to call the service at a given time. Then, we will create a text file and write the current time in the text file using the service.
Service1.cs class
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
namespace MyFirstService {
public partial class Service1: ServiceBase {
Timer timer = new Timer(); // name space(using System.Timers;)
public Service1() {
InitializeComponent();
}
protected override void OnStart(string[] args) {
WriteToFile("Service is started at " + DateTime.Now);
timer.Elapsed += new ElapsedEventHandler(OnElapsedTime);
timer.Interval = 5000; //number in milisecinds
timer.Enabled = true;
}
protected override void OnStop() {
WriteToFile("Service is stopped at " + DateTime.Now);
}
private void OnElapsedTime(object source, ElapsedEventArgs e) {
WriteToFile("Service is recall at " + DateTime.Now);
}
public void WriteToFile(string Message) {
string path = AppDomain.CurrentDomain.BaseDirectory + "\\Logs";
if (!Directory.Exists(path)) {
Directory.CreateDirectory(path);
}
string filepath = AppDomain.CurrentDomain.BaseDirectory + "\\Logs\\ServiceLog_" + DateTime.Now.Date.ToShortDateString().Replace('/', '_') + ".txt";
if (!File.Exists(filepath)) {
// Create a file to write to.
using(StreamWriter sw = File.CreateText(filepath)) {
sw.WriteLine(Message);
}
} else {
using(StreamWriter sw = File.AppendText(filepath)) {
sw.WriteLine(Message);
}
}
}
}
}
Code explanation — the above code will call service every 5 seconds, create a folder if none exists, and write our message.
Step 9. Rebuild your application.
Right-click on your project or solution and select Rebuild.
Step 10
Search «Command Prompt» and run as administrator.
Step 11
Fire the below command in the command prompt and press ENTER.
cd C:\Windows\Microsoft.NET\Framework\v4.0.30319
Step 12
Now Go to your project source folder > bin > Debug and copy the full path of your Windows Service exe file.
Installing a Windows Service
Open the command prompt and fire the below command and press ENTER.
Syntax
InstallUtil.exe + Your copied path + \your service name + .exe
Our path
InstallUtil.exe C:\Users\Faisal-Pathan\source\repos\MyFirstService\MyFirstService\bin\Debug\MyFirstService.exe
Check the status of a Windows Service.
Open services by following the below steps:
- Press the Window key + R.
- Type services.msc
- Find your Service.
You may notice that the Windows service is running.
Check Windows Service Output
The service will create a text file with the following text in it.
The log folder will be created in your bin folder.
Uninstalling a Windows Service
If you want to uninstall your service, fire the below command.
- Syntax InstallUtil.exe -u + Your copied path + \your service name + .exe
- Our path InstallUtil.exe -u C:\Users\Faisal-Pathan\source\repos\MyFirstService\MyFirstService\bin\Debug\MyFirstService.exe
Summary
This article taught us how to create a Windows Service and install/Uninstall it using InstallUtil.exe from the command prompt.
I hope you found this tutorial easy to follow and understand.
I also uploaded this project on GitHub; here is the URL https://github.com/faisal5170/WindowsService.
Служба Windows – это специальная служебная программа, которая запускается операционной системой автоматически при загрузке вне зависимости от статуса пользователя или вручную (в зависимости от настроек).
Службы работают в фоновом режиме и, как правило, выполняют различные технологические задачи.
Также, в отличие от «обычных» программ, службы не имеют графического интерфейса и управляются при помощи специального диспетчера.
Мы не станем вдаваться в подробности работы служб Windows как таковой потому, что она достаточно подробно описана в литературе по работе с Windows.
В этой статье мы рассмотрим пример создания службы Windows с использованием языка программирования C#.
Создание службы
Для того, чтобы создать проект службы необходимо в окне создания нового проекта выбрать пункт «Служба Windows».
После этого Visual Studio создаст новый проект с «каркасом» службы.
К сожалению, в отличие, например, от Windows Forms или консольного приложения данный «каркас» не самодостаточен. То есть, установить в систему и запустить в работу такую «пустую» службу нельзя. Необходимо дописать рабочий функционал и логику установки в систему.
В качестве примера создадим службу, которая будет каждую секунду записывать в текстовый файл текущее время.
Во избежание возможных проблем в будущем, настоятельно рекомендуется рабочий функционал выносить в отдельный класс.
Ниже представлен такой класс для нашей службы.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
class TimeLogger { Timer timer; // Запуск процесса записи public void Start() { timer = new Timer(1000); timer.Elapsed += this.Log; timer.Start(); } // Завершение записи public void Stop() { timer.Stop(); } // Собственно запись private void Log(Object source, ElapsedEventArgs e) { using (System.IO.StreamWriter file = new System.IO.StreamWriter(@»C:\Test.txt», true)) { file.WriteLine(DateTime.Now.ToString()); } } } |
Теперь, с одной, стороны, у нас есть всё необходимое для работы, но, в то же, время мы пока не можем это никак использовать. Нам необходимо реализовать запуск службы в работу и её остановку.
Для этого в шаблоне проекта предусмотрен специальный файл, который по умолчанию называется Service1.cs. В нём расположен класс, который является наследником класса ServiceBase представляющего основу служб Windows. По сути, этот файл собственно и является будущей службой.
В нём нам требуется описать объект описанного ранее класса в качестве поля и обработать два события. OnStart (запуск службы) и OnStop (её остановка).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public partial class ServiceTest : ServiceBase { private TimeLogger logger; public ServiceTest() { InitializeComponent(); } protected override void OnStart(string[] args) { logger= new TimeLogger(); Thread loggerThread = new Thread(new ThreadStart(logger.Start)); loggerThread.Start(); } protected override void OnStop() { logger.Stop(); } } |
Наша служба уже готова к работе, но прежде чем её запустить необходимо установить её в систему.
Visual Studio не предоставляет готового установщика поэтому его нам придётся написать самостоятельно.
Вначале добавим в наш проект класс установщика.
Весь процесс установки описывается в конструкторе класса установщика.
При помощи объектов классов ServiceInstaller и ServiceProcessInstaller мы задаём для службы учётную запись, режим запуска и имена, как это показано ниже:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
[RunInstaller(true)] public partial class InstallerTestService : System.Configuration.Install.Installer { ServiceInstaller serviceInstaller; ServiceProcessInstaller processInstaller; public Installer1() { InitializeComponent(); serviceInstaller = new ServiceInstaller(); processInstaller = new ServiceProcessInstaller(); // Учётная запись для службы processInstaller.Account = ServiceAccount.LocalSystem; // Режим запуска (в данном случае Manual (вручную)). // Если служба не является драйвером устройства допустимы только значения Manual, Authomatic (автоматический запуск при загрузке системы) и Disabled (отключено). serviceInstaller.StartType = ServiceStartMode.Manual; // Имя службы (должно совпадать с именем класса службы). serviceInstaller.ServiceName = «ServiceTest»; // Отображаемое имя службы. Под ним служба будет отображаться в различных утилитах для работы со службами Windows. // Это необязательные параметр. При его отсутствии будет отображаться ServiceName. serviceInstaller.DisplayName = «Служба ServiceTest»; Installers.Add(processInstaller); Installers.Add(serviceInstaller); } } |
После написания установщика разработка нашей службы завершена и мы можем её установить и запустить в работу.
Установка и удаление службы
К сожалению, Visual Studio не позволяет установить и запустить службу непосредственно из IDE. Вместо этого нужно воспользоваться специальной консольной утилитой InstallUtil.exe.
Эта утилита располагается в следующей папке:
- Для 32-разрядных систем:
C:\Windows\Microsoft.NET\Framework64\v4.0.30319 - Для 64-разрядных систем:
C:\Windows\Microsoft.NET\Framework\v4.0.30319
Для установки службы нужно передать InstallUtil.exe полный путь к её исполняемому файлу. В качестве примера файл размещён в папке C:\ServiceTest.
«C:\Windows\Microsoft.NET\Framework64\v4.0.30319\ InstallUtil.exe» «C:\ServiceTest\ServiceTest.exe» |
После выполнения данной команды в консоли служба будет установлена в системе и её можно будет запустить в работу.
Если требуется деинстраллировать службу, это можно сделать при помощи той же команды с флагом u.
«C:\Windows\Microsoft.NET\Framework64\v4.0.30319\ InstallUtil.exe» «C:\ServiceTest\ServiceTest.exe» /u |
Table of Contents
- Introduction
- Create the Windows Service Project
- Create an Installer
- Configure the Installer
- Create a sample recurring task
- Installing the Service
- Alternative install using SC CREATE
- Testing the Service
- Removing the Service
- Troubleshooting
- Conclusion
In this post I’ll briefly explain how to create and configure a custom Windows Service in C# using Visual Studio 2019. Before we start, it could be wise to quickly summarize what a Windows Service actually is and how it differs from a standard console program or desktop application.
NOTE: the most recent version of the source code explained in this post is now fully available on GitHub: if you like it, be sure to give it a star!
Introduction
In Windows-based operating systems, the term Windows Service refers to a computer program without a user-interface that completely operates in the background. If you know Unix-based environments, you could think of it as a Windows version of a typical Unix daemon. A Windows service must conform to the interface rules and protocols of the Service Control Manager, the kernel component responsible for managing Windows services.
Once installed, the Windows Service can be configured using the Service Control Manager (services.exe) to start when the operating system is started and run in the background as long as Windows is running; alternatively, they can be started manually or by an event. It’s also possible to manually pause, stop and restart the service.
Each service can also be configured to run within a context of a specific user or system account: for example, most Windows services that come shipped with the OS are pre-configured to run within the context of three system accounts: System, Network Service and Local Service. Such behaviours allow them to operate even when the machine is unmanaged and/or a standard user is not logged on.
To get a glimpse of all services running on a Windows machine you can either:
- Open Control Panel, then navigate to the Administrative Tools and click on the Services icon.
- Press Window + R to open the Run window, then type services.msc and press ENTER.
Create the Windows Service Project
Open Visual Studio 2019 and select Create a new project from the initial dashboard. Type Windows Service into the filter textbox near the top to quickly find out the project template we’re looking for: select the Windows Service (.NET Framework) project template and click Next.
Give the project a name and create it. After a few seconds, you should be prompted with your newly-created project and a blank page showing the Service1.cs file in Design Mode.
Create an Installer
The next thing we have to do is to create an Installer, which is the component that will register our Service with the Service Control Manager.
To do that, right-click on the blank area in the Design View of the Service1.cs file and select Add Installer from the contextual menu, as shown in the screenshot below:
Once you do that, a new ProjectInstaller.cs file will be added to the project. Save everything (CTRL + SHIFT + S), then open the ProjectInstaller.Designer.cs file and take a look at the InitializeComponent() method, which defines some important settings of our service: its name and display name, the account context used to run it, and so on.
Configure the Installer
Locate the lines defining the Username and Password (both set to null by default) and add the following line right above them to make our service use the LocalSystem account context:
this.serviceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.LocalSystem; |
Right after that, locate the line of code defining the ServiceName and change it accordingly:
this.serviceInstaller1.ServiceName = «WindowsService.NET»; |
You can also specify a Display Name and a Description by adding the relevant source code lines, in the following way:
this.serviceInstaller1.DisplayName = «WindowsService.NET»; this.serviceInstaller1.Description = «A sample Windows Service boilerplate written in C# using NET Framework and VS2019»; |
Here’s how your InitializeComponent() method should look like after the changes:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
private void InitializeComponent() { this.serviceProcessInstaller1 = new System.ServiceProcess.ServiceProcessInstaller(); this.serviceInstaller1 = new System.ServiceProcess.ServiceInstaller(); // // serviceProcessInstaller1 // this.serviceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.LocalSystem; this.serviceProcessInstaller1.Password = null; this.serviceProcessInstaller1.Username = null; // // serviceInstaller1 // this.serviceInstaller1.ServiceName = «WindowsService.NET»; this.serviceInstaller1.DisplayName = «WindowsService.NET»; this.serviceInstaller1.Description = «A sample Windows Service boilerplate written in C# using NET Framework and VS2019»; // // ProjectInstaller // this.Installers.AddRange(new System.Configuration.Install.Installer[] { this.serviceProcessInstaller1, this.serviceInstaller1}); } |
That’s it for the installer: now let’s switch to the Windows Service code itself.
Create a sample recurring task
Open the Service1.cs file and switch to Code View. As we can see, there are three main methods here:
- the Service1() method, aka the constructor, which internally calls the InitializeComponents() method that we modified in the previous paragraph.
- The OnStart() method, which is called once when the service is started.
- The OnStop() method, which will be called once when the service is stopped.
For our sample boilerplate we’ll just add a basic timelapse logging feature to the default code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
public partial class Service1 : ServiceBase { Timer Timer = new Timer(); int Interval = 10000; // 10000 ms = 10 second public Service1() { InitializeComponent(); this.ServiceName = «WindowsService.NET»; } protected override void OnStart(string[] args) { WriteLog(«Service has been started»); Timer.Elapsed += new ElapsedEventHandler(OnElapsedTime); Timer.Interval = Interval; Timer.Enabled = true; } private void OnElapsedTime(object source, ElapsedEventArgs e) { WriteLog(«{0} ms elapsed.»); } protected override void OnStop() { Timer.Stop(); WriteLog(«Service has been stopped.»); } private void WriteLog(string logMessage, bool addTimeStamp = true) { var path = AppDomain.CurrentDomain.BaseDirectory; if (!Directory.Exists(path)) Directory.CreateDirectory(path); var filePath = String.Format(«{0}\\{1}_{2}.txt», path, ServiceName, DateTime.Now.ToString(«yyyyMMdd», CultureInfo.CurrentCulture) ); if (addTimeStamp) logMessage = String.Format(«[{0}] — {1}», DateTime.Now.ToString(«HH:mm:ss», CultureInfo.CurrentCulture), logMessage); File.AppendAllText(filePath, logMessage); } } |
As we can see, we added the following stuff:
- The Timer private variable, which host a standard timer (from System.Timers namespace).
- The Interval private variable, which globally defines the timer interval in milliseconds.
- The OnElapsedTime() private method, which will be called upon each timer interval cycle.
- The WriteLog() private method, which logs a custom log message to a daily log file persisted on disk: such file will be created within the folder that hosts the service executable.
- A minimal implementation in the existing OnStart() and OnStop() methods to put everything together.
While we were there, we also changed the service’s default name from Service1 to WindowsService.NET by adding the following line in the constructor, rightbelow the InitializeComponent() call:
this.ServiceName = «WindowsService.NET»; |
Alternatively, the ServiceName property can also be changed from within the Service1.Designer.cs file’s InitializeComponent() method.
IMPORTANT: be sure to use the same ServiceName specified in the Service1.Designer.cs class!
That’s basically it: our service is ready to be installed and tested!
Installing the Service
To install our service, build the project in Release mode: once done, copy the WindowsService.NET.exe file from /bin/Release/ to a more «handy» folder — such as C:\Temp\WindowsInstaller\.
Right after that, open a Command Prompt with administrative rights and type the following:
«C:\Windows\Microsoft.NET\Framework\v4.0.30319\installutil.exe» «C:\Temp\WindowsService.NET\WindowsService.NET.exe» |
You should receive a quick confirmation message saying that everything went good.
Alternative install using SC CREATE
If you don’t want to use installutil.exe, you can also install your service using the sc create command with the following syntax:
sc create «WindowsService.NET» binPath=«C:\Temp\WindowsService.NET\WindowsService.NET.exe» |
However, if you do that, you’ll have to manually specify the service name: also, the service Description won’t be shown in the service list UI window (see below).
Testing the Service
Use Control Panel > Administrative Tools > Services to open the service list and scroll down until you’ll find our new service:
That’s our boy! From here we can either manually start it or set it to automatic, so that it will be started whenever the system starts. However, I strongly suggest to not do that for now, since it would permanently drain your system resources if you forget to disable it — it’s just a sample service after all! Let’s just start it and take a look at the log file, which should be found in the executable folder:
C:\Temp\WindowsService.NET\ |
If everything went good, the log should be similar to this screenshot:
Removing the Service
To remove our service, open a Command Prompt with administrative rights and type the following command:
sc delete «WindowsService.NET»; |
Troubleshooting
It’s worth noting that, whenever the service encounters an unhandled exceptions while operating, it will crash without writing anything to the Windows event log. For that very reason, I strongly suggest to wrap your main execution methods within a try/catch block and log any possible exceptions on disk — or somewhere else — so you can better understand what’s going on. Such task can be very easy using the WriteLog() function within our sample code: don’t forget to do that!
Conclusion
That’s it: I hope that this guide will help other ASP.NET C# developers to create awesome Windows Services to fullfill their needs! If you like my project, feel free to give me a virtual hug by starring the GitHub project and/or putting a like on our Facebook/Twitter pages!