web-dev-qa-db-ja.com

Windowsサービスとしての.NETコンソールアプリケーション

コンソールアプリケーションがあり、Windowsサービスとして実行したい。 VS2010には、コンソールプロジェクトを接続してWindowsサービスを構築できるプロジェクトテンプレートがあります。分離されたサービスプロジェクトを追加せず、可能な場合は、サービスコードをコンソールアプリケーションに統合して、コンソールアプリケーションを、スイッチを使用してコマンドラインから実行する場合、コンソールアプリケーションまたはWindowsサービスとして実行できる1つのプロジェクトとして維持します。

たぶん誰かがC#コンソールアプリケーションをサービスにすばやく簡単に変換できるクラスライブラリまたはコードスニペットを提案できますか?

133
Tomas

通常、次のテクニックを使用して、同じアプリケーションをコンソールアプリケーションまたはサービスとして実行します。

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です。技術的には、ユーザーインタラクティブモードでサービスを実行することが可能であるため、代わりにコマンドラインスイッチを確認できます。

170
VladV

TopShelf で大成功しました。

TopShelfは、コンソールアプリまたはWindowsサービスとして実行できる.NET Windowsアプリを簡単に作成できるように設計されたNugetパッケージです。サービスの開始イベントや停止イベントなどのイベントをすばやく接続し、コードを使用して設定できます。実行するアカウントを設定し、他のサービスへの依存関係を設定し、エラーからの回復方法を設定します。

パッケージマネージャーコンソール(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がすべてを行います。

50
saille

だからここに完全なチュートリアルがあります:

  1. 新しいコンソールアプリケーションプロジェクト(例:MyService)を作成する
  2. 2つのライブラリ参照を追加します:System.ServiceProcessおよびSystem.Configuration.Install
  3. 以下に印刷された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

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

使用できます

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

そして、それはサービスリストに表示されます。しかし、それが正しく機能するかどうかはわかりません。通常、サービスはいくつかのイベントをリッスンする必要があります。

ただし、いくつかのサービスラッパーがあり、任意のアプリケーションを実際のサービスとして実行できます。たとえば、Microsoft SrvAny from Win2003 Resource Kit

2
Darcara

まず、コンソールアプリケーションソリューションをWindowsサービスソリューションに埋め込み、参照します。

次に、コンソールアプリケーションのProgramクラスをpublicにします

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

次に、コンソールアプリケーション内に2つの関数を作成します

    /// <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サービスハイブリッドにも使用できます。

2

1つのアセンブリで繰り返されるコードを停止したいというあなたの意見を聞きますが、それは最も簡単で、コードの繰り返しを減らし、将来的に他の方法でコードを簡単に再利用できるようになります。

  1. すべての作業を行う1つのライブラリアセンブリ。次に、非常にスリムでシンプルな2つのプロジェクトを作成します。
  2. コマンドラインであるもの
  3. 1つはWindowsサービスです。
2
JonAlb

必要なものを定義する必要があるかもしれませんが、私の知る限り、コマンドラインでコンソールまたはサービスとして同時にアプリを実行することはできません。サービスがインストールされており、Services Managerでサービスを開始する必要があることに注意してください。サービスを開始するか、コンソールアプリを実行する新しいプロセスを開始する新しいアプリケーションを作成できます。しかし、あなたが書いたように

「コンソールアプリケーションを1つのプロジェクトとして保持する」

かつて、私はあなたの立場で、コンソールアプリケーションをサービスに変えました。 VS Express Editionを使用している場合、最初にテンプレートが必要です。最初の手順を実行できるリンクを次に示します。 C#Windows Service 、これは私にとって非常に役に立ちました。次に、そのテンプレートを使用して、サービスの目的のイベントにコードを追加します。

サービスを改善するために、別の方法がありますが、これは迅速かつ/または簡単ではありません。appdomainsを使用し、dllを作成してロード/アンロードします。 1つでは、コンソールアプリで新しいプロセスを開始でき、別のdllでは、サービスが行う必要のある機能を配置できます。

幸運を。

0
BlackCath

機能を1つまたは複数のクラスに分離し、2つのスタブのいずれかを介して起動する必要があります。コンソールスタブまたはサービススタブ。

わかりやすいように、Windowsを実行しているとき、インフラストラクチャを構成する無数のサービスは、ユーザーにコンソールウィンドウを表示しません(直接表示することはできません)。サービスは、非グラフィカルな方法でユーザーと通信する必要があります。SCMを介して。イベントログで、いくつかのログファイルなどに。サービスはSCMを介してWindowsと通信する必要もあります。そうでない場合、シャットダウンされます。

サービスと通信できるコンソールアプリがあることは明らかですが、GUIの相互作用を必要とせずにサービスを独立して実行する必要があります。

コンソールスタブは、サービスの動作をデバッグするのに非常に役立ちますが、結局のところ、サービスを作成する目的である「実稼働環境」では使用しないでください。

私は完全には読んでいませんが、 この記事 は正しい方向に向いているようです。

0
Jodrell

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);
}
0
Edward Brey