簡體   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