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

时间:2011-10-14 06:58:07

标签: c# .net-4.0 windows-services console-application

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

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

10 个答案:

答案 0 :(得分:166)

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

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通常适用于控制台应用,而false适用于服务。从技术上讲,可以在用户交互模式下运行服务,因此您可以检查命令行开关。

答案 1 :(得分:47)

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

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

从软件包管理器控制台(Nuget):

  

Install-Package Topshelf

请参阅code samples开始使用。

示例:

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为您完成所有操作。

答案 2 :(得分:23)

所以这是完整的演练:

  1. 创建新的控制台应用程序项目(例如MyService)
  2. 添加两个库引用:System.ServiceProcess和System.Configuration.Install
  3. 添加下面打印的三个文件
  4. 构建项目并运行&#34; InstallUtil.exe c:\ path \ to \ MyService.exe&#34;
  5. 现在您应该在服务列表上看到MyService(运行services.msc)
  6. * 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);
            }
        }
    }
    

答案 3 :(得分:4)

这是基于最新的 .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);
        }
    }
}

一切准备就绪,并且应用程序已成功构建后,您可以use sc.exe使用以下命令将控制台应用程序exe安装为Windows服务:

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

答案 4 :(得分:2)

您可以使用

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

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

虽然有几个服务包装器,它可以将任何应用程序作为实际服务运行。例如来自SrvAny

的示例微软Win2003 Resource Kit

答案 5 :(得分:2)

我听到你的意思是希望一个程序集停止重复的代码但是,这将是最简单的并减少代码重复,并且如果......将其分解为更容易在将来以其他方式重用代码3个组件。

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

答案 6 :(得分:2)

首先,我将控制台应用程序解决方案嵌入到Windows服务解决方案中并引用它。

然后我将控制台应用程序Program class 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中添加的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服务混合

答案 7 :(得分:0)

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

  

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

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

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

祝你好运。

答案 8 :(得分:0)

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

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

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

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

我没有完全阅读,但this article似乎正朝着正确的方向发展。

答案 9 :(得分:0)

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