我有控制台应用程序,想作为Windows服务运行。VS2010具有项目模板,该模板允许附加控制台项目并构建Windows服务。我不希望添加单独的服务项目,如果可能的话,请将服务代码集成到控制台应用程序中,以将控制台应用程序保留为一个项目,该项目可以作为控制台应用程序运行,也可以作为Windows服务运行(例如,使用命令行从命令行运行)。
也许有人会建议类库或代码片段,这些类库或代码片段可以快速轻松地将C#控制台应用程序转换为服务?
我有控制台应用程序,想作为Windows服务运行。VS2010具有项目模板,该模板允许附加控制台项目并构建Windows服务。我不希望添加单独的服务项目,如果可能的话,请将服务代码集成到控制台应用程序中,以将控制台应用程序保留为一个项目,该项目可以作为控制台应用程序运行,也可以作为Windows服务运行(例如,使用命令行从命令行运行)。
也许有人会建议类库或代码片段,这些类库或代码片段可以快速轻松地将C#控制台应用程序转换为服务?
Answers:
我通常使用以下技术来与控制台应用程序或服务运行相同的应用程序:
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,对于服务为false。从技术上讲,可以在用户交互模式下运行服务,因此您可以改为检查命令行开关。
我在TopShelf上取得了巨大的成功。
TopShelf是一个Nuget软件包,旨在使创建可作为控制台应用程序或Windows服务运行的.NET Windows应用程序变得容易。您可以快速挂起事件,例如服务的启动和停止事件,使用代码进行配置,例如设置运行帐户,配置对其他服务的依赖关系,以及配置如何从错误中恢复。
从程序包管理器控制台(Nuget):
安装包Topshelf
请参考代码示例以开始使用。
例:
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会为您完成所有这些工作。
这是完整的演练:
*通常可以在这里找到InstallUtil.exe:C:\ windows \ Microsoft.NET \ Framework \ v4.0.30319 \ InstallUtil.ex e
Program.cs
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));
}
}
}
MyService.cs
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);
}
}
}
这是基于最新的.Net Core 3.1将控制台应用程序转换为Windows服务作为工作程序服务的更新方法。
如果从Visual Studio 2019创建辅助服务,则将为您提供开箱即用创建Windows服务所需的几乎所有内容,这也是将控制台应用程序转换为Windows Service所需的内容。
这是您需要做的更改:
安装以下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);
}
}
}
当一切准备就绪,并且应用程序已成功构建后,可以使用sc.exe通过以下命令将控制台应用程序exe作为Windows服务安装:
sc.exe create DemoService binpath= "path/to/your/file.exe"
您可以使用
reg add HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run /v ServiceName /d "c:\path\to\service\file\exe"
它将出现在服务列表中。我不知道这是否正确。服务通常必须侦听几个事件。
但是,有几个服务包装程序可以将任何应用程序作为真实服务运行。例如,Win2003资源工具包中的 Microsoft SrvAny
首先,我将控制台应用程序解决方案嵌入Windows服务解决方案中,并对其进行引用。
然后我将控制台应用程序Program类公开
/// <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 Service混合
据我所知,也许您应该定义所需的内容,但您不能同时将应用程序作为控制台或带命令行的服务运行。请记住,该服务已安装,您必须在Services Manager中启动它,您可以创建一个新应用程序,以启动服务或启动运行控制台应用程序的新进程。但是正如你写的
“将控制台应用程序保留为一个项目”
有一次,我在您的位置上,将控制台应用程序转换为服务。首先,如果要使用VS Express Edition,则需要模板。这里是您可以迈出第一步的链接:C#Windows Service,这对我很有帮助。然后使用该模板,将代码添加到所需的服务事件中。
为了改善您的服务,您还可以做另一件事,但这不是快速和/或轻松的,它是使用appdomains并创建要加载/卸载的dll。在其中一个中,您可以使用控制台应用程序启动一个新过程,在另一个dll中,您可以仅放置该服务必须执行的功能。
祝好运。
您需要将功能分成一个或多个类,然后通过两个存根之一启动它。控制台存根或服务存根。
显而易见,运行Windows时,构成基础结构的各种服务不会(也不能直接)向用户提供控制台窗口。服务需要以非图形方式与用户通信:通过SCM;在事件日志中,到一些日志文件等。该服务还需要通过SCM与Windows通信,否则它将关闭。
拥有一些可以与服务通信的控制台应用程序显然是可以接受的,但是该服务需要独立运行,而无需进行GUI交互。
Console存根对于调试服务行为非常有用,但是不应在“生产化”的环境中使用,毕竟,这是创建服务的目的。
我还没有完全阅读它,但是这篇文章似乎朝着正确的方向发展。
我使用的服务类遵循所规定的标准模式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);
}