September 2019 update: This subject has been an ever-moving feast since this article was first published in the early days of .Net Core, but there are now four main approaches to this that are each discussed below.
Building an application to run as a Windows Service was pretty straightforward with the .Net Framework. You either implemented ServiceBase in a console application or used a helper library like TopShelf. Both approaches provided a programming model that covered the full life cycle of the service, from initial registration to running, pausing and stopping.
When .Net Core was first released it didn’t have any direct support for creating Windows Services in .Net Core. The ServiceBase class was left out of the initial implementation, presumably because it contained too much Windows implementation detail for what was supposed to be a cross-platform framework.
Since then several different approaches to creating a service have emerged:
- Using a service manager like NSSM to manage the registration and running of the service.
- Building a native service using the Windows Compatibility Pack for .Net Core
- Creating a service using Topshelf
- Using .Net Core’s generic host builder to run background services as a Windows Service
Using a service manager
The most direct way of creating a Windows Service in .Net Core is to create a simple console application and use an external service manager like NSSM to take care of the service aspects. You don’t even have to compile your .Net Core console application for Windows as NSSM can be configured to call a batch file.
To install a .Net Core application as a windows service just create a batch file called run.bat in your application root that contains the following command:
You can register this batch file as a windows service called “testservice” by dropping NSSM.EXE into your application directory and running the following:
This will display the installation dialog where you enter the full path to your run.bat file in the Path field.
The catch with using an external service manager is there is no direct support for handling aspects of the service lifecycle beyond registering and starting the service. Most importantly, there’s no equivalent of the OnStop event in ServiceBase that allows you to clean up resources gracefully when a service is stopped. There isn’t even a reliable event that you can hook into when the process ends or console window closes.
With an NSSM-based service the only way to hook into a service stop is to intercept the CTRL+C command – this is available in .Net Core using the Console object as shown below:
Console.CancelKeyPress += Console_CancelKeyPress; private static void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e) { // Put your clean-up code here }
When you register a service using NSSM make sure that it is configured to issue a CTRL+C command when shutting down the service. This is done by default but you can check it by looking at the ShutDown tab on the service registry dialog:
Note that although this allows you to clean up when a service is stopped, this approach can only support a rudimentary service that can be switched on and off. There’s still no support for other aspects of the service lifecycle including pausing, continuing and responding to power events.
Building a native service
The Windows Compatibility Pack contains the ServiceBase class, allowing you to build Windows Services in much the same way that you would with the .Net Framework.
Whether you’d want to take this approach is a different question altogether. The Windows Compatibility Pack was designed to support porting legacy .Net Framework code rather than acting as a foundation for new applications. This style of Windows Service project can also be awkward to debug, requiring you to manually attach a debugger to the process that the service is running on.
The code is simple enough. The example below shows a basic service class that overrides the main methods to start, stop and pause the service:
public class NativeServiceImpl : ServiceBase { protected override void OnStart(string[] args) { // The service has been started base.OnStart(args); } protected override void OnStop() { // The service has been stopped base.OnStop(); } protected override void OnPause() { // The service has been paused base.OnPause(); } }
The service can be set up in the application’s Program.Main() method:
static void Main(string[] args) { ServiceBase.Run(new NativeServiceImpl()); }
You’ll need to build the application for the Windows runtime using the -r flag:
dotnet publish -r win-x64 -c Release
The service can then be installed as normal using either sc.exe or installutil.exe.
Using Topshelf
Topshelf gained a lot of traction in the .Net Framework community as it simplified the task of building, configuring and debugging Windows Services. It was slow to offer a version that was compatible with .Net Core, though a .Net Standard version was finally released in September 2018.
A Topshelf implementation involves implementing an abstract ServiceControl class:
public class TopShelfServiceImpl : ServiceControl { public bool Start(HostControl hostControl) { return true; } public bool Stop(HostControl hostControl) { return true; } }
The service itself is run using the HostFactory class in the application’s Program.Main() method:
static void Main(string[] args) { HostFactory.Run(x => x.Service<TopShelfServiceImpl>()); }
The application needs to be built for the windows runtime in much the same way as a native service. This may seem much like building and deploying a native service, but the experience of installing and debugging a Topshelf service is very different – in a good way.
You can install and start by running the application directly with command line arguments, e.g.
TopShelfServer.exe install
TopShelfServer.exe start
There are a wide range of supported command line arguments and you’ll never need an external registration utility. For debugging Topshelf emulates starting the service in a console window, running the service in the background and automatically attaching the debugger. This means you can start a service from within the Visual Studio IDE and have it hit breakpoints much like any other application.
Using the .Net Core generic host to run background services
The generic host builder that was added in .Net Core 3.0 can also be used to build Windows Services. This takes care of the application lifecycle for you, including controlling startup and graceful shutdown, logging, dependency injection and configuration. You can also get it to run as a Windows Service.
With this approach, you create a service definition based on the BackgroundService base class. This class can also be used to execute long-running tasks in ASP.Net applications and it has a single method override as shown below:
public class BackgroundServiceImpl : BackgroundService { protected override async Task ExecuteAsync(CancellationToken stoppingToken) { // Do some work } }
The service definition is wired up to the host builder in Program.Main:
public static void Main(string[] args) { CreateHostBuilder(args).Build().Run(); } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureServices((hostContext, services) => { services.AddHostedService<BackgroundServiceImpl>(); });
To turn this into a Windows Service, you need to add the Microsoft.Extensions.Hosting.WindowsServices package which lets you add the UseWindowsService() extension method to the host builder:
public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureServices((hostContext, services) => { services.AddHostedService<BackgroundServiceImpl>(); }).UseWindowsService();
This gives you a vanilla .Net console app that can be debugged directly from Visual Studio. From here you need to build the project for the windows ruintime and install the application using sx.exe or installutil.exe.
This approach feels like a snug fit to the vanilla .Net Core ecosystem. It provides a flexible style of implementation that you can easily run in other contexts. The application can easily be run as a containerised service and the background services can also be run in an ASP.Net application. That said, it does lack the bells and whistles that Topshelf gives you, particularly when it comes to installing and configuring a service.
Filed under
DotNet.
Уровень сложностиСредний
Время на прочтение10 мин
Количество просмотров6.7K
Введение
После перехода Microsoft с .NET Framework на .NET Core программирование на C# стало более увлекательным, хотя некоторые аспекты изменились.
В частности, шаблон проекта Служба Windows (.NET Framework) создаёт класс, наследник от ServiceBase в пространстве имен System.ServiceProcess. Прогеру предлагается реализовать виртуальные методы базового класса OnStart и OnStop, которые задают действия, подлежащие выполнению при запуске (остановке) службы, что собственно и есть суть и назначение Службы Виндовз. Регистрация Службы в этом случае производится с помощью утилиты installUtil.exe, в .Net Core это делается утилитой SC.exe.
Реализовать службу на .NET Core (в моем случае .NET 9) не сложнее, но по другому, шаблон проекта теперь называется Worker Service (Microsoft), а рабочий класс наследуется от BackgroundService.
В этой статье я подробно опишу процесс создания, публикации и регистрации Службы в .Net 9 на примере службы для Telegram-бота (сокращенно — Телебот). Почему бот? Во-первых, писать Телебота на C# — это действительно приятно. Во-вторых, чтобы обеспечить его круглосуточную доступность на сервере под управлением Windows, логично использовать именно Службу Windows, которая будет поддерживать его работу в фоновом режиме и запускаться может сама при перезагрузке сервера.
В заключении рассмотрим как добавить логирование в стандартный виндовый EventLog и немного обсудим функционал самого Телебота.
1. Создание проекта
И так, поехали. В Visual Studio (у меня Community 2022, версия 17.12.1) выбираем шаблон проекта Worker Service (Microsoft). Жмем Далее, пишем имя проекта у меня Svc2, снова Далее и получаем пустой проект Рабочего сервиса.
В шаблонном проекте Рабочего сервиса нам уже добавили зависимость Microsoft.Extensions.Hosting и два файла Program.cs и Worker.cs. В первом дается подсказка как правильно внедрять зависимости (Dependency Injections), второй это сам Воркер, то место, где будем запускать Телебота. Для простоты восприятия я почищу файл класса Worker от лишних подробностей касательно логирования.
namespace Svc2;
public class Worker : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
var time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
Console.WriteLine($"Worker running at: {time}");
await Task.Delay(1000, stoppingToken);
}
}
}
Запустим, просто F5, проверим.
Все отлично, работает. Работает как обычное консольное приложение, теперь надо сделать из этого Службу.
2. Добавление пакета поддержки службы Windows
Класс Worker наследован от BackgroundService и наш проект запускается и работает как консольное приложение но это еще не служба. Что бы сделать из него Windows Service надо добавить в зависимости пакет Microsoft.Extensions.Hosting.WindowsServices, это сделает наш класс Worker способным реагировать на команды запуска и остановки сервиса через стандартную консоль.
В поиске менеджера пакетов NuGet, по запросу WindowsServices находим то что нам надо. Microsoft.Extensions.Hosting.WindowsServices. Хороший пакет, 44 миллиона скачиваний, солидно, заслуживает доверия.
Вот эти два пакета, это все что нам нужно для реализации Службы.
3. Редактирование кода
3.1 Programm.cs
После добавления пакета Microsoft.Extensions.Hosting.WindowsServices поле Services объекта builder было расширено методом AddWindowsService. Сделаем вызов этого метода указав имя нашей Службы, так как оно должно будет отображаться в общем списке Служб.
using Svc2;
var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddHostedService<Worker>();
builder.Services.AddWindowsService(options =>
options.ServiceName = "AM Telebot"
);
var host = builder.Build();
host.Run();
Это у нас так Microsoft реализует Внедрение Зависимости (Dependency Injection).
3.2 Worker.cs
Класс Worker изменим следующим образом, добавим кое что
namespace Svc2;
public class Worker : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
// Service started
}
public override async Task StopAsync(CancellationToken cancellationToken)
{
// Service stoped
await base.StopAsync(cancellationToken);
}
}
К имеющемуся изначально в шаблоне методу ExecuteAsync, добавим переопределение метода StopAsync. Именно эти два метода будут получать управление при запуске и остановке службы соответственно.
4. Публикация сервиса
Собственно код для сервиса у нас готов, теперь надо его выложить куда то, где он будет работать, то есть Опубликовать (Publish) наш проект.
В контекстном меню проекта выберем Опубликовать… в Папку и жмем далее.
Пропишем удобную для нас папку.
Ну и жмем Опубликовать.
Если публикация прошла успешно в папке D:\Projects\CoreService\_pub появится большая куча файлов. Что бы привести в порядок эту кучу добавим кое что в файл проекта. Вот эти несколько строк в тэге PropertyGroup сделают папку с публикацией более компактной
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<PlatformTarget>x64</PlatformTarget>
<PublishSingleFile Condition="'$(Configuration)' == 'Release'">true</PublishSingleFile>
<DebugType>embedded</DebugType>
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
Файл проекта теперь должен выглядеть вот так:
<Project Sdk="Microsoft.NET.Sdk.Worker">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UserSecretsId>dotnet-Svc2-d4d482f3-62dd-4f7d-99cd-b6c3c130f446</UserSecretsId>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<PlatformTarget>x64</PlatformTarget>
<PublishSingleFile Condition="'$(Configuration)' == 'Release'">true</PublishSingleFile>
<DebugType>embedded</DebugType>
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.0" />
</ItemGroup>
</Project>
Еще раз пересоберем и опубликуем проект. Теперь видим, что в папке с публикацией всего 3-и файлика.
Да, думаю так удобнее.
5. Регистрация сервиса
Для того, что бы наш сервис был виден в Консоли Управления Службами Windows воспользуемся утилитой SC.EXE. Откроем окно командной строки под админской учеткой и выполним следующую команду:
sc create "AM Telebot" binPath=D:\Projects\CoreService\_pub\Svc2.exe
Тут написано: create значит создать сервис с именем AM Telebot, и вот там, куда указывает binPath, лежит его исполняемый файл. Выполним это в окне командной строки (запущенной под админской учеткой).
Отлично, полный успех, теперь смотрим что у нас в консоли управления службами.
И таки да, наш AM Telebot появился в списке служб. Тут уже, в свойствах Службы, можно поменять ему пользователя и тип запуска, например на Автомат, что бы сам запускался при перезагрузке сервера. Еще можно задать режим восстановления при непредвиденной ошибке и падении, в общем там есть полезные настройки.
Теперь проверим как она запустится.
Вроде как все норм, работает служба.
Кстати, для удаления службы используем ту же утилиту SC.exe с опцией delete и указав имя службы, вот так.
sc delete "AM Telebot"
6. Добавление Тедебота в проект
Телебот у меня уже реализован в отдельной dll и я просто добавляю его в решение. Как реализован сам Телебот покажу как нибудь подробнее в следующем опусе, а для добавления его в наш сервис я просто включаю зависимость от этого проекта, вот так как на картинке.
Телебот сам по себе тоже зависит от трех проектов: amSecrets, amLogger и amFireWall. В последнем зашита как раз полезная часть функционала бота, именно там происходит взаимодействие с Брэндмауаром сервера. С его помощью можно добавить IP в белый список для доступа к серверу по RDP, удалить IP адрес из белого списка, получить список всех разрешенных IP адресов, а еще можно полностью отрубить правило доступа по RDP, это на случай когда надо срочно закрыть доступ к серверу вообще всем, такой вот Аларм батон.
amSecrets скрывает от посторонних глаз ключи, пароли и другие коннекшен стринги, не очень интересно, а вот amLogger прикольная штука, про него ниже расскажу подробнее.
Класс Worker службы чуть изменим, добавим строки запуска и остановки Телебота вот так.
namespace Svc2;
public class Worker : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
// Service started
await amTelebot.Worker.Start("Телебот запущен");
}
public override async Task StopAsync(CancellationToken cancellationToken)
{
// Service stoped
await amTelebot.Worker.Stop("Телебот выключен");
await base.StopAsync(cancellationToken);
}
}
Теперь можно пересобрать и заново опубликовать Службу. Не забываем перед публикацией выключать Службу, иначе файлики не обновятся и студия выдаст ошибку, так как не сможет скопировать файлы в папку где Служба запущена.
7. Запуск и тестирование Телебота
После публикации проекта службы идем в Консоль Управления Службами windows и запускаем наш Телебот.
В телеграмм появилось сообщение – Бот запущен.
Остановим службу.
Пришло сообщение, что Телебот выключен. Все отлично работает.
8. Добавим логирование
Служба Windows это стандартное средство этой операционной системы, поэтому для логирования, с учетом того, что мы пишем приложение определенно под Windows, будем использовать стандартный Журнал Событий Windows (Windows Event Log). Всего три небольшие правки в коде помогут нам это сделать легко и непринужденно.
Во первых в файле appsettings.json надо добавить секцию
“EventLog”: {
“LogLevel”: {
“Default”: “Information”
}
}
В результате файл должен выглядеть примерно как то так
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.Hosting.Lifetime": "Information"
},
"EventLog": {
"LogLevel": {
"Default": "Information"
}
}
}
}
Во вторых, в файле Program.cs добавим внедрение зависимости (Dependency Injection) для сервиса EventLog следующим образом
builder.Logging.AddEventLog(c => {
c.LogName = "AM Telebot LN";
c.SourceName = "AM Telebot SRC";
});
таким образом, создаем журнал AM Telebot LN и источник AM Telebot SRC.
Файл Program.cs теперь будет выглядеть так
using Svc2;
var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddHostedService<Worker>();
builder.Services.AddWindowsService(options =>
options.ServiceName = "AM Telebot"
);
builder.Logging.AddEventLog(c => {
c.LogName = "AM Telebot LN";
c.SourceName = "AM Telebot SRC";
});
var host = builder.Build();
host.Run();
После того как отработает этот код в Консоли Просмотра Событий в папочке Журналы приложений и служб появится новый журнал AM Telebot.
В этот журнал и будут писаться наши логи. Писать в лог будем в классе Worker в файле Worker.cs, для этого поменяем код в этом файле следующим образом
namespace Svc2;
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
public Worker(ILogger<Worker> logger)
{
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
// Service started
var msg = "Телебот запущен";
_logger.LogInformation(msg);
await amTelebot.Worker.Start(msg);
}
public override async Task StopAsync(CancellationToken cancellationToken)
{
// Service stoped
var msg = "Телебот выключен";
_logger.LogInformation(msg);
await amTelebot.Worker.Stop(msg);
await base.StopAsync(cancellationToken);
}
}
Если откомпилируем и опубликуем этот код, то при запуске и остановке сервиса получим сообщения Телебот запущен и Телебот выключен не только уже в Телеграм, но и в Консоли Просмотра событий Windows, в Журнале AM Telebot LN появятся те же сообщения.
С помощью своей библиотечки amLogger.dll (которую более подробно опишу в отдельной статейке, а тут покажу только как его можно использовать) изменю файлик Worker.cs в проекте службы следующим образом.
using amLogger;
namespace Svc2;
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
public Worker(ILogger<Worker> logger)
{
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
// Service started
Logger.Instance.Init(log => OnLog(log));
var msg = "Телебот запущен";
await amTelebot.Worker.Start(msg);
Log.Info(1, "Svc2.Worker.ExecuteAsync", msg);
}
public override async Task StopAsync(CancellationToken cancellationToken)
{
// Service stoped
var msg = "Телебот выключен";
await amTelebot.Worker.Stop(msg);
Log.Info(1, "Svc2.Worker.StopAsync", msg);
await base.StopAsync(cancellationToken);
}
void OnLog(Log log)
{
// Писать лог для внешнего просмотра будем тут
// удобнее всего писать в журнал событий windows (Windows Event Log)
switch (log.lvl)
{
case Level.Info:
_logger.LogInformation(log.id, log.msg, log.src);
break;
case Level.Error:
_logger.LogError(log.id, log.msg, log.src);
break;
case Level.Debug:
_logger.LogDebug(log.msg);
break;
}
}
}
Более подробно опишу идею такого логгера в отдельной статейке, а тут покажу только как его можно использовать.
Для начала при старте службы надо инициализировать наш Logger вот так.
Logger.Instance.Init(log => OnLog(log));
Лямбда в параметре Init указывает куда будет переходить управление при логировании. В моем случае это будет метод OnLog, и в него будет передаваться объект log вот такой структуры:
public class Log
{
public int id; // Some ID
public int type; // Type of message
public Level lvl;
public string src = ""; // Source
public string msg = ""; // Message
}
У класса Log есть еще набор статических методов, которые после инициализации можно вызывать в любой части, любого модуля солюшена, одинаково во всех DLL. Все эти методы соответствуют определенному уровню логирования:
-
Trace
-
Info
-
Warn
-
Error
-
Fatal
Весь код логера можно посмотреть тут
https://github.com/amizerov/CoreService/blob/master/amLogger/Logger.cs
После инициализации в проекте Службы, использовать amLogger можно в любом модуле решения, и запись выглядит довольно локанично.
Log.Info("Svc2.Worker.ExecuteAsync", msg);
Имя метода Info соответствует уровню логирования Info, первый параметр это сорс, второй сам мессэдж. При этом вызов этого метода передает управление в OnLog, указанный при инициализации, это единая точка входа для всех вызовов логирования, и тут мы будем выводить лог наружу, часть сообщений в телеграм, а основные в Windows Event Log.
В модуле Телебота логирование ошибки может выглядеть так.
static Task HandleErrorAsync(ITelegramBotClient botClient, Exception exception, CancellationToken token)
{
Log.Fatal("Telebot error", $"An error occurred: {exception.Message}");
return Task.CompletedTask;
}
Весть код Телебота можно посмотреть тут
https://github.com/amizerov/CoreService/tree/master/amTelebot
Заключение
Надеюсь кому то поможет эта инструкция, ну и способ логирования, возможно, будет интересен широким массам в узком кругу программистов самоучек.
На всякий случай, кому интересно ниже ссылка на проект целиком.
https://github.com/amizerov/CoreService.git
Если эта публикация вас вдохновила и вы хотите поддержать автора — не стесняйтесь нажать на кнопку
In this tutorial, we will explore how to create a Windows Service with .NET Core. .NET Core is a versatile and powerful framework that allows developers to build a variety of applications, including Windows Services. The creation of Windows Services can be crucial for developing systems that require continuous background processes, such as system monitoring or data processing tasks.
Here’s the step-by-step guide to creating a Windows Service with .NET Core:
Step 1: Setting Up the .NET Core Console Application
First, you need to set up a new .NET Core console application. Open the command prompt and create a new directory for your project. Navigate into the directory and run the following command:
dotnet new console -n MyWindowsService
This command creates a new console application with the name ‘MyWindowsService’.
Step 2: Implementing the Main Functionality
Next, you need to implement the main functionality of your service. Open the ‘Program.cs’ file and add the following code:
using System;
using System.Threading;
namespace MyWindowsService
{
class Program
{
static void Main(string[] args)
{
while(true)
{
Console.WriteLine("Windows Service is running...");
Thread.Sleep(1000);
}
}
}
}
This code will make the service print a message «Windows Service is running…» every second.
Step 3: Converting the Console App to a Windows Service
To convert the console app to a Windows Service, we will use the Microsoft.Extensions.Hosting package. First, add the package to your project with the following command:
dotnet add package Microsoft.Extensions.Hosting
Now, update the ‘Program.cs’ file:
using Microsoft.Extensions.Hosting;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace MyWindowsService
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<MyService>();
});
}
public class MyService : IHostedService
{
public Task StartAsync(CancellationToken cancellationToken)
{
Console.WriteLine("Service starting...");
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
Console.WriteLine("Service stopping...");
return Task.CompletedTask;
}
}
}
This code will make your console application run as a Windows Service.
Step 4: Installing the Windows Service
To install the Windows Service, publish your application first:
dotnet publish --configuration Release --output ./publish
Then, install the service using the New-Service cmdlet from PowerShell:
New-Service -Name "MyService" -BinaryPathName "C:\path\to\your\app\publish\MyWindowsService.exe"
Your Windows Service is now installed and will start automatically with Windows.
If you’re looking to hire .NET remote developers to assist with similar projects, consider reaching out to experienced professionals.
Conclusion
Creating a Windows Service with .NET Core is a straightforward process that involves setting up a console application, implementing the main functionality, converting the app to a service, and installing the service. With this guide, you should be able to create your own Windows Services using .NET Core.
Remember, if you need professional help, don’t hesitate to hire .NET remote developers.
Most of the time, when I am assigned a new task like integrating a 3rd party tool or a plugin into my .NET based application, I simply rely on trying it out first hand via a simple Console application and quickly integrating the necessary parts. And that is what probably any .NET developer would do (some might have a different approach).
But, sometimes there are requirements where the task is to create a job or a service with a repetitive flow and made runnable in a scheduled fashion. Some common scenarios are creating a job for delivering messages and updates, performing backups, doing cleanups and many more of such stuffs.
There are many options to achieve these requirements by using a third party tool. Some of those tools are: Hangfire, Quartz, Topshelf etc.
But, since I would have already tried out and tested the feature via a console application, my next step would be simply to deploy the code as a service. This generally involves, creating a new windows service application and integrating my previously tested code.
But instead of creating a new application, I do some tweaks here and there, improvise the old console application and make it behave as a windows service and vice-versa. I can then easily install this console app as a windows service into my system whenever I want.
Lets see how it is done …
26.04.2012, 04:08. Показов 8556. Ответов 0
Есть проект в Visual Studio 2011 на C#, консольное приложение.
весь проект состоит из нескольких классов и нескольких функций.
в функции main реализован бесконечный цикл на всё время работы консольного приложения.
чтобы приложение было более устойчивым к случайным действиям пользователя или перезагрузке системы, хочется сделать это приложение службой, чтобы она тихонько висела и делала свою работу и её можно было бы запускать и останавливать по желанию.
погуглил, поискал по форуму. Чёткого ответа какую библиотеку подключить и какие методы реализовать, чтобы приложение запускалось как сервис не нашёл. Пробовал реализовать несколько примеров, но не очень успешно, т.к. всё равно программа как служба работать не хочет, отваливается по таймауту.
есть вариант создать новый проект системной службы и плавно перенести все классы, подменить пространство имён, но это на пару дней работы, а хочется вписать в уже готовое приложение пару заглушек чтобы всё заработало как надо.
как вариант можно использовать это решение:
Как поместить в трей консольное приложение
но скрытие консольного окна таким методом может задеть пользовательские консольные окна, а этого нельзя допускать.
Добавлено через 17 минут
* Единственный минус Консольное приложение в фоновом режиме и
Как поместить в трей консольное приложение в том, что программку будет сложно перезапускать.. хотя с иконкой в трее это уже интереснее.
Добавлено через 2 часа 25 минут
Перенёс всё в проект Windows Service, служба пытается запуститься, но т.к. бесконечный цикл стоит в методе OnStart то она просто висит в попытке запуститься.
Сейчас буду пробовать перенести всё в функцию Main
Добавлено через 24 минуты
помогла вот эта статья:
http://www.realcoding.net/article/view/2690