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

问题描述 投票:126回答:9

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

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

c# .net-4.0 windows-services console-application
9个回答
170
投票

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

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
    }
}

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


49
投票

我和Environment.UserInteractive取得了很大的成功。

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

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

安装包Topshelf

请参阅TopShelf开始使用。

例:

code samples

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

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");                       
}); 

你不需要连接一个ServiceInstaller就可以了--TopShelf为你完成所有这些工作。


23
投票

所以这是完整的演练:

  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

Program.cs中

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

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

MyServiceInstaller.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();
        }
    }
}

2
投票

您可以使用

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);
        }
    }
}

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

但是有几个服务包装器可以将任何应用程序作为实际服务运行。例如来自reg add HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run /v ServiceName /d "c:\path\to\service\file\exe" 的微软SrvAny


2
投票

我听到你想要一个程序集停止重复代码的观点但是,这将是最简单的并减少代码重复,并且如果......将其分解为3个程序集,将来可以更容易地以其他方式重用代码。

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

2
投票

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

然后我将控制台应用程序程序类公之于众

Win2003 Resource Kit

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

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

然后在Windows服务本身内实例化程序并调用OnStart和OnStop中添加的Start和Stop功能。见下文

    /// <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应用程序/ Windows服务混合


0
投票

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

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

有一次,我处于你的位置,将控制台应用程序转变为服务。首先,您需要模板,以防您使用VS Express Edition。这是一个你可以迈出第一步的链接: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(); } } ,这对我很有帮助。然后使用该模板,将代码添加到服务的所需事件中。

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

祝好运。


0
投票

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

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

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

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

我没有完全阅读,但C# Windows Service似乎正在向正确的方向发展。


0
投票

我使用的服务类遵循this article规定的标准模式,并使用帮助程序轻松进行F5调试。这样可以保持服务中定义的服务数据,使其易于查找,并且易于管理其生命周期。

我通常使用以下结构创建一个Windows应用程序。我没有创建控制台应用程序;这样我每次运行应用程序时都不会在我的脸上弹出一个大黑盒子。我留在调试器中,所有操作都在。我使用ServiceBase以便消息进入输出窗口,该窗口很好地停靠并在应用程序终止后保持可见。

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

Console.ReadKey
© www.soinside.com 2019 - 2024. All rights reserved.