繁体   English   中英

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

[英].NET console application as Windows service

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

也许有人可以建议可以快速轻松地将 c# 控制台应用程序转换为服务的类库或代码片段?

我通常使用以下技术来运行与控制台应用程序或服务相同的应用程序:

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,对于服务为 false。 从技术上讲,可以在用户交互模式下运行服务,因此您可以改为检查命令行开关。

我在TopShelf 上取得了巨大的成功。

TopShelf 是一个 Nuget 包,旨在简化创建可作为控制台应用程序或 Windows 服务运行的 .NET Windows 应用程序。 您可以快速连接诸如服务启动和停止事件之类的事件,使用代码进行配置,例如设置它运行的帐户,配置对其他服务的依赖关系,以及配置它如何从错误中恢复。

从包管理器控制台 (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 为您完成这一切。

所以这是完整的演练:

  1. 创建新的控制台应用程序项目(例如 MyService)
  2. 添加两个库引用:System.ServiceProcess 和 System.Configuration.Install
  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

程序.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));
        }
    }
}

我的服务.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();
        }
    }
}

我的服务安装程序

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 服务的内容。

以下是您需要做的更改:

安装以下 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"

首先我将控制台应用方案嵌入到windows服务方案中并引用。

然后我将控制台应用程序 Program 类设为 public

/// <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 中添加的启动和停止函数。 见下文

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服务混合

我听到了您希望一个程序集停止重复代码的观点,但是,如果……您将其分解为 3 个程序集,那么这将是最简单的并减少代码重复,并且将来可以更轻松地以其他方式重用您的代码。

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

您可以使用

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

它将出现在服务列表中。 我不知道,这是否正常工作。 一个服务通常必须监听多个事件。

不过,有几个服务包装器可以将任何应用程序作为真正的服务运行。 例如,来自Win2003 Resource Kit 的Microsofts SrvAny

我使用遵循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);
}

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

“将控制台应用程序作为一个项目”

曾经,我在您的位置上,将控制台应用程序转变为服务。 首先,您需要模板,以防您使用 VS Express Edition。 这是一个链接,您可以在其中进行第一步: C# Windows Service ,这对我非常有帮助。 然后使用该模板,将您的代码添加到服务的所需事件中。

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

祝你好运。

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

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

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

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

我还没有完全阅读它,但这篇文章似乎朝着正确的方向发展。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM