作为 Windows 服务的.NET 控制台应用

我有一些控制台应用,希望以 Windows 服务的形式运行它。VS2010有项目模板,允许附加控制台项目和构建 Windows 服务。 我不想添加单独的服务项目,如果可能的话,将服务代码集成到控制台应用中,以保持控制台应用作为一个项目,可以作为控制台应用运行,或者作为 windows 服务,如果使用开关从命令行运行。

也许有人可以推荐一个类库或者代码片段,这样可以快速轻松地把 c # 控制台应用转换成服务?

170528 次浏览

你可以用

reg add HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run /v ServiceName /d "c:\path\to\service\file\exe"

它会出现在服务列表中。我不知道,这样做是否正确。服务通常必须监听多个事件。

但是,有几种服务包装器可以将任何应用程序作为实际服务运行。例如微软 SrvAnyWin2003资源套件

也许你应该定义你需要什么,据我所知,你不能运行你的应用作为控制台或服务与命令行,在同一时间。请记住,服务已经安装,您必须在服务管理器中启动它,您可以创建一个新的应用程序来启动服务或启动一个运行您的控制台应用程序的新进程。但就像你写的那样

“保持控制台应用一致”

有一次,我处在你的位置把一个控制台应用变成了一种服务。首先,您需要模板,以防您正在使用 VS Express Edition。这里有一个链接,在那里你可以有你的第一步: C # Windows 服务,这对我很有帮助。然后使用该模板,将代码添加到所需的服务事件中。

为了改善服务,还有另外一件事情可以做,但这并不快捷和/或容易,就是使用 appdomain,并创建 dls 来加载/卸载。在其中一个版本中,您可以使用控制台应用程序启动一个新进程,而在另一个 dll 中,您可以只放置服务必须执行的功能。

祝你好运。

您需要将功能分离到一个或多个类中,并通过两个存根中的一个来启动它。控制台存根或服务存根。

显而易见,在运行 Windows 时,组成基础结构的无数服务不(也不能直接)向用户显示控制台窗口。服务需要以一种非图形化的方式与用户通信: 通过 SCM; 在事件日志中,与一些日志文件等进行通信。该服务还需要通过 SCM 与窗口通信,否则它将被关闭。

有一些可以与服务通信的控制台应用程序显然是可以接受的,但是服务需要独立运行,而不需要 GUI 交互。

控制台存根对于调试服务行为非常有用,但是不应该在“生产化”环境中使用,毕竟,“生产化”环境是创建服务的目的。

我还没有完全读完,但 这篇文章似乎指出了正确的方向。

我听到你的观点,希望一个程序集停止重复的代码,但是,这将是最简单的,减少代码重复,并使其更容易重用您的代码,在未来的其他方式,如果..。你把它分成三个部分。

  1. 一个完成所有工作的库程序集。 然后有两个非常非常小/简单的项目:
  2. 一个是命令行
  3. 窗口服务。

我通常使用以下技术作为控制台应用或服务来运行同一个应用程序:

using System.ServiceProcess


public static class Program
{
#region Nested classes to support running as service
public const string ServiceName = "MyService";


public class Service : ServiceBase
{
public Service()
{
ServiceName = Program.ServiceName;
}


protected override void OnStart(string[] args)
{
Program.Start(args);
}


protected override void OnStop()
{
Program.Stop();
}
}
#endregion
    

static void Main(string[] args)
{
if (!Environment.UserInteractive)
// running as service
using (var service = new Service())
ServiceBase.Run(service);
else
{
// running as console app
Start(args);


Console.WriteLine("Press any key to stop...");
Console.ReadKey(true);


Stop();
}
}
    

private static void Start(string[] args)
{
// onstart code here
}


private static void Stop()
{
// onstop code here
}
}

对于控制台应用程序,Environment.UserInteractive通常为 true,对于服务,Environment.UserInteractive为 false。从技术上讲,可以在用户交互模式下运行服务,因此您可以检查命令行开关。

我在 顶级货架上取得了巨大的成功。

TopShelf 是一个 Nuget 软件包,旨在使创建变得更加容易。NET Windows 应用程序,可以作为控制台应用程序或 Windows 服务运行。您可以快速地连接事件,例如您的服务 Start 和 Stop 事件,使用代码进行配置,例如设置它运行的帐户,配置对其他服务的依赖,以及配置它如何从错误中恢复。

从包管理控制台(Nuget) :

安装-包装顶架

请参考 代码示例开始。

例如:

HostFactory.Run(x =>
{
x.Service<TownCrier>(s =>
{
s.ConstructUsing(name=> new TownCrier());
s.WhenStarted(tc => tc.Start());
s.WhenStopped(tc => tc.Stop());
});
x.RunAsLocalSystem();


x.SetDescription("Sample Topshelf Host");
x.SetDisplayName("Stuff");
x.SetServiceName("stuff");
});

TopShelf 还负责服务安装,这可以节省大量时间,并从解决方案中删除样板代码。安装您的。Exe 作为服务,只需在命令提示符下执行以下操作:

myservice.exe install -servicename "MyService" -displayname "My Service" -description "This is my service."

你不需要连接 ServiceInstaller 等等—— TopShelf 可以为你做所有的事情。

首先,我将控制台应用解决方案嵌入到 windows 服务解决方案中,并参考它。

然后我把控制台应用课程公开

/// <summary>
/// Hybrid service/console application
/// </summary>
public class Program
{
}

然后在控制台应用中创建两个函数

    /// <summary>
/// Used to start as a service
/// </summary>
public void Start()
{
Main();
}


/// <summary>
/// Used to stop the service
/// </summary>
public void Stop()
{
if (Application.MessageLoop)
Application.Exit();   //windows app
else
Environment.Exit(1);  //console app
}

然后在 Windows 服务本身中实例化程序,并调用在 OnStart 和 OnStop 中添加的 Start 和 Stop 函数。请看下面

class WinService : ServiceBase
{
readonly Program _application = new Program();


/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main()
{
ServiceBase[] servicesToRun = { new WinService() };
Run(servicesToRun);
}


/// <summary>
/// Set things in motion so your service can do its work.
/// </summary>
protected override void OnStart(string[] args)
{
Thread thread = new Thread(() => _application.Start());
thread.Start();
}


/// <summary>
/// Stop this service.
/// </summary>
protected override void OnStop()
{
Thread thread = new Thread(() => _application.Stop());
thread.Start();
}
}

这种方法也可以用于 Windows 应用程序/Windows 服务的混合

以下是完整的演练:

  1. 创建新的控制台应用项目(例如 MyService)
  2. 添加两个库引用: System.ServiceProcess 和 System.Configuration
  3. 添加下面打印的三个文件
  4. 构建项目并运行“ InstallUtil.exe c: path to MyService.exe”
  5. 现在您应该在服务列表中看到 MyService (运行 services.msc)

* InstallUtil.exe 通常可以在这里找到: C: windows Microsoft.NET Framework v4.0.30319 InstallUtil.ex e

程序

using System;
using System.IO;
using System.ServiceProcess;


namespace MyService
{
class Program
{
public const string ServiceName = "MyService";


static void Main(string[] args)
{
if (Environment.UserInteractive)
{
// running as console app
Start(args);


Console.WriteLine("Press any key to stop...");
Console.ReadKey(true);


Stop();
}
else
{
// running as service
using (var service = new Service())
{
ServiceBase.Run(service);
}
}
}


public static void Start(string[] args)
{
File.AppendAllText(@"c:\temp\MyService.txt", String.Format("{0} started{1}", DateTime.Now, Environment.NewLine));
}


public static void Stop()
{
File.AppendAllText(@"c:\temp\MyService.txt", String.Format("{0} stopped{1}", DateTime.Now, Environment.NewLine));
}
}
}

我的服务

using System.ServiceProcess;


namespace MyService
{
class Service : ServiceBase
{
public Service()
{
ServiceName = Program.ServiceName;
}


protected override void OnStart(string[] args)
{
Program.Start(args);
}


protected override void OnStop()
{
Program.Stop();
}
}
}

Myserviceinstaller.cs

using System.ComponentModel;
using System.Configuration.Install;
using System.ServiceProcess;


namespace MyService
{
[RunInstaller(true)]
public class MyServiceInstaller : Installer
{
public MyServiceInstaller()
{
var spi = new ServiceProcessInstaller();
var si = new ServiceInstaller();


spi.Account = ServiceAccount.LocalSystem;
spi.Username = null;
spi.Password = null;


si.DisplayName = Program.ServiceName;
si.ServiceName = Program.ServiceName;
si.StartType = ServiceStartMode.Automatic;


Installers.Add(spi);
Installers.Add(si);
}
}
}

我使用了一个服务类,它遵循 ServiceBase规定的标准模式,并附加了一些帮助程序,以便于 F5调试。这样可以在服务中定义服务数据,使其易于查找和管理。

我通常使用下面的结构创建一个 Windows 应用程序。我不会创建一个控制台应用,这样我就不会在每次运行这个应用程序的时候都看到一个大黑匣子出现在我的脸上。我停留在调试器中的所有操作。我使用 Debug.WriteLine,这样消息就会进入输出窗口,该窗口停靠得很好,并在应用程序终止后保持可见。

我通常不会添加用于停止的调试代码; 我只是使用调试器。如果确实需要停止调试,我会将项目设置为一个控制台应用程序,添加一个 Stop转发器方法,并在调用 Console.ReadKey之后调用它。

public class Service : ServiceBase
{
protected override void OnStart(string[] args)
{
// Start logic here.
}


protected override void OnStop()
{
// Stop logic here.
}


static void Main(string[] args)
{
using (var service = new Service()) {
if (Environment.UserInteractive) {
service.Start();
Thread.Sleep(Timeout.Infinite);
} else
Run(service);
}
}
public void Start() => OnStart(null);
}

下面是一个更新的方法,如何将控制台应用转换为基于最新的 .NET 6的作为工作者服务的 Windows 服务。

在 VisualStudio2022中,可以将 WorkService 开箱即用作项目模板。

这给了您一个 main 方法和一个 Worker.cs,在这两个方法上还需要几行代码

Cs,我在其中添加了 StartAsync 和 StopAsync 重写,以选择我的服务在启动/停止时的功能。

namespace WorkerService
{
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;


public Worker(ILogger<Worker> logger)
{
_logger = logger;
}


protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
//do some operation
}


public override Task StartAsync(CancellationToken cancellationToken)
{
return base.StartAsync(cancellationToken);
}


public override Task StopAsync(CancellationToken cancellationToken)
{
return base.StopAsync(cancellationToken);
}
}
}

和 Program.cs,您需要在其上添加 .UseWindowsService()

using WorkerService;




IHost host = Host.CreateDefaultBuilder(args)
.ConfigureServices(services =>
{
services.AddHostedService<Worker>();
})
.UseWindowsService()
.Build();


await host.RunAsync();

您需要为此方法安装以下 NuGet

Install-Package Microsoft.Extensions.Hosting.WindowsServices

旧答案-> .NET Core 3.1

如果你从 Visual Studio 2019中创建一个 Worker 服务,它会提供几乎所有你需要的即时创建 Windows 服务的东西,这也是你需要改变控制台应用以便将其转换为 Windows 服务的东西。

以下是你需要做的改变:

安装以下 NuGet 包

Install-Package Microsoft.Extensions.Hosting.WindowsServices -Version 3.1.0
Install-Package Microsoft.Extensions.Configuration.Abstractions -Version 3.1.0

将 Program.cs 更改为具有如下实现:

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;


namespace ConsoleApp
{
class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).UseWindowsService().Build().Run();
}


private static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<Worker>();
});
}
}

并添加 Worker.cs,您将在其中放置将由服务操作运行的代码:

using Microsoft.Extensions.Hosting;
using System.Threading;
using System.Threading.Tasks;


namespace ConsoleApp
{
public class Worker : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
//do some operation
}


public override Task StartAsync(CancellationToken cancellationToken)
{
return base.StartAsync(cancellationToken);
}


public override Task StopAsync(CancellationToken cancellationToken)
{
return base.StopAsync(cancellationToken);
}
}
}

将应用程序安装为 Windows 服务

当一切准备就绪,并且应用程序已经成功构建,你可以使用以下命令 使用 < em > sc.exe 来安装你的控制台应用 exe 作为 Windows 服务:

sc.exe create DemoService binpath= "path/to/your/file.exe"