web-dev-qa-db-ja.com

Windowsサービスをデバッグするためのより簡単な方法

Windowsサービスコントロールマネージャからサービスを開始してからデバッガをスレッドにアタッチするよりも、コードをステップ実行するより簡単な方法はありますか?これはちょっと面倒なので、もっと直接的なアプローチがあるのか​​と思います。

313
Mats

すぐにサービスをデバッグしたいのであれば、そこにDebugger.Break()を入れるだけです。その行に達すると、VSに戻ります。完了したらその行を削除することを忘れないでください。

PDATE:#if DEBUGプラグマの代わりに、Conditional("DEBUG_SERVICE")属性を使うこともできます。

[Conditional("DEBUG_SERVICE")]
private static void DebugMode()
{
    Debugger.Break();
}

あなたのOnStartで、このメソッドを呼び出すだけです:

public override void OnStart()
{
     DebugMode();
     /* ... do the rest */
}

そこでは、コードはデバッグビルド中にのみ有効になります。あなたがそれをしている間、それはサービスデバッグのために別のBuild Configurationを作成するのに役立つかもしれません。

267
jop

また、通常の実行用とサービス用に別々の「バージョン」を用意するのもいいと思いますが、そのためには別のコマンドラインスイッチを割り当てる必要がありますか。

できませんでした。

public static int Main(string[] args)
{
  if (!Environment.UserInteractive)
  {
    // Startup as service.
  }
  else
  {
    // Startup as application
  }
}

ダブルクリックでアプリを起動することができ(本当に必要な場合はOK)、ヒットすることもできます。 F5 Visual Studioで(その/consoleオプションを含むようにプロジェクト設定を変更する必要はありません)。

技術的には、Environment.UserInteractiveは現在のウィンドウステーションにWSF_VISIBLEフラグが設定されているかどうかをチェックしますが、(非対話型)サービスとして実行される以外にfalseを返す他の理由はありますか?

208
Christian.K

数週間前に新しいサービスプロジェクトを立ち上げたときに、この記事が見つかりました。多くの素晴らしい提案がありますが、私が望む解決策はまだ見つかりませんでした。サービスクラスを変更せずにサービスクラスのOnStartおよびOnStopメソッドを呼び出す可能性。

私が思いついた解決策は、この投稿に対する他の回答で示唆されているように、Environment.Interactive the select実行モードを使用します。

static void Main()
{
    ServiceBase[] servicesToRun;
    servicesToRun = new ServiceBase[] 
    {
        new MyService()
    };
    if (Environment.UserInteractive)
    {
        RunInteractive(servicesToRun);
    }
    else
    {
        ServiceBase.Run(servicesToRun);
    }
}

RunInteractiveヘルパーはリフレクションを使用して保護されたOnStartおよびOnStopメソッドを呼び出します。

static void RunInteractive(ServiceBase[] servicesToRun)
{
    Console.WriteLine("Services running in interactive mode.");
    Console.WriteLine();

    MethodInfo onStartMethod = typeof(ServiceBase).GetMethod("OnStart", 
        BindingFlags.Instance | BindingFlags.NonPublic);
    foreach (ServiceBase service in servicesToRun)
    {
        Console.Write("Starting {0}...", service.ServiceName);
        onStartMethod.Invoke(service, new object[] { new string[] { } });
        Console.Write("Started");
    }

    Console.WriteLine();
    Console.WriteLine();
    Console.WriteLine(
        "Press any key to stop the services and end the process...");
    Console.ReadKey();
    Console.WriteLine();

    MethodInfo onStopMethod = typeof(ServiceBase).GetMethod("OnStop", 
        BindingFlags.Instance | BindingFlags.NonPublic);
    foreach (ServiceBase service in servicesToRun)
    {
        Console.Write("Stopping {0}...", service.ServiceName);
        onStopMethod.Invoke(service, null);
        Console.WriteLine("Stopped");
    }

    Console.WriteLine("All services stopped.");
    // Keep the console alive for a second to allow the user to see the message.
    Thread.Sleep(1000);
}

これで必要なコードはすべて揃っていますが、説明付きで チュートリアル も書きました。

117
Anders Abel

私が通常することは、サービスのロジックを別のクラスにカプセル化して、それを「ランナー」クラスから開始することです。このランナークラスは実際のサービスでもコンソールアプリケーションでもかまいません。だからあなたの解決策は(少なくとも)3つのプロジェクトがあります。

/ConsoleRunner
   /....
/ServiceRunner
   /....
/ApplicationLogic
   /....
40
Paul van Brenk

これ Fabio ScopelによるYouTubeビデオ Windowsサービスをきちんとデバッグする方法を説明しています...実際の方法はビデオの4:45に始まります。

これがビデオで説明されているコードです...あなたのProgram.csファイルに、デバッグセクションのためのものを追加してください...

namespace YourNamespace
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        static void Main()
        {
#if DEBUG
            Service1 myService = new Service1();
            myService.OnDebug();
            System.Threading.Thread.Sleep(System.Threading.Timeout.Infinite);
#else
            ServiceBase[] ServicesToRun;
            ServicesToRun = new ServiceBase[]
            {
                new Service1()
            };
            ServiceBase.Run(ServicesToRun);
#endif

        }
    }
}

Service1.csファイルにOnDebug()メソッドを追加します。

    public Service1()
    {
        InitializeComponent();
    }

    public void OnDebug()
    {
        OnStart(null);
    }

    protected override void OnStart(string[] args)
    {
        // your code to do something
    }

    protected override void OnStop()
    {
    }

使い方

基本的には、保護されていて外部からアクセスできないため、public void OnDebug()を呼び出すOnStart(string[] args)を作成する必要があります。 void Main()プログラムに#ifプリプロセッサと#DEBUGが追加されました。

プロジェクトがデバッグモードでコンパイルされている場合、Visual StudioはDEBUGを定義します。これにより、条件が満たされたときにデバッグセクション(下記)を実行できます。

Service1 myService = new Service1();
myService.OnDebug();
System.Threading.Thread.Sleep(System.Threading.Timeout.Infinite);

そしてそれはコンソールアプリケーションのように実行されます、問題がうまくいったら、あなたはモードReleaseを変更することができ、通常のelseセクションはロジックをトリガーします

25
Jason Miller

UPDATE

この方法ははるかに簡単です。

http://www.codeproject.com/KB/dotnet/DebugWinServices.aspx

私は後世のために私の最初の答えを残します。


私のサービスには、実行する作業があるかどうかを定期的に確認するサービスとして、Timerをカプセル化するクラスがあります。

クラスを新しくして、サービスの起動中にStartEventLoop()を呼び出します。 (このクラスはコンソールアプリケーションからも簡単に使用できます。)

この設計の良い副作用は、あなたがTimerを設定した引数がサービスが実際に動作し始める前に遅延を持つために使用されることができるので、あなたが手動でデバッガをアタッチする時間があるということです。

pS デバッガを手動でアタッチする方法 実行中のプロセスに...?

using System;
using System.Threading;
using System.Configuration;    

public class ServiceEventHandler
{
    Timer _timer;
    public ServiceEventHandler()
    {
        // get configuration etc.
        _timer = new Timer(
            new TimerCallback(EventTimerCallback)
            , null
            , Timeout.Infinite
            , Timeout.Infinite);
    }

    private void EventTimerCallback(object state)
    {
        // do something
    }

    public void StartEventLoop()
    {
        // wait a minute, then run every 30 minutes
        _timer.Change(TimeSpan.Parse("00:01:00"), TimeSpan.Parse("00:30:00");
    }
}

また、私は以前に次のことをしていました(以前の答えですでに言及されていましたが、リリースビルドで発生しないようにするための条件付きコンパイラ[#if]フラグを使用)。

私はこの方法をやめました。なぜならReleaseでビルドするのを忘れてクライアントデモで実行中のアプリケーションでデバッガブレークをするのを忘れたからです(恥ずかしい!).

#if DEBUG
if (!System.Diagnostics.Debugger.IsAttached)
{
    System.Diagnostics.Debugger.Break();
}
#endif
14
rohancragg

static void Main()
{
#if DEBUG
                // Run as interactive exe in debug mode to allow easy
                // debugging.

                var service = new MyService();
                service.OnStart(null);

                // Sleep the main thread indefinitely while the service code
                // runs in .OnStart

                Thread.Sleep(Timeout.Infinite);
#else
                // Run normally as service in release mode.

                ServiceBase[] ServicesToRun;
                ServicesToRun = new ServiceBase[]{ new MyService() };
                ServiceBase.Run(ServicesToRun);
#endif
}
13
Thomas Bratt

コマンドプロンプト(sc.exe)を使用してサービスを開始することもできます。

個人的には、デバッグ段階でコードをスタンドアロンプ​​ログラムとして実行し、ほとんどのバグが解決されたら、サービスとして実行するように変更します。

10
akauppi

私がしていたのは、サービスまたは通常のアプリケーションとしてプログラムを起動するコマンドラインスイッチを使用することでした。それから、私のIDEに自分のコードをステップ実行できるようにスイッチを設定します。

一部の言語では、IDEで実行されているかどうかを実際に検出し、この切り替えを自動的に実行できます。

あなたはどの言語を使っていますか?

10
RB.

TopShelf ライブラリを使用してください。

コンソールアプリケーションを作成してから、メインのセットアップを構成します。

class Program
    {
        static void Main(string[] args)
        {
            HostFactory.Run(x =>
            {

                // setup service start and stop.
                x.Service<Controller>(s =>
                {
                    s.ConstructUsing(name => new Controller());
                    s.WhenStarted(controller => controller.Start());
                    s.WhenStopped(controller => controller.Stop());
                });

                // setup recovery here
                x.EnableServiceRecovery(rc =>
                {
                    rc.RestartService(delayInMinutes: 0);
                    rc.SetResetPeriod(days: 0);
                });

                x.RunAsLocalSystem();
            });
        }
}

public class Controller
    {
        public void Start()
        {

        }

        public void Stop()
        {

        }
    }

サービスをデバッグするには、ビジュアルスタジオでF5を押すだけです。

サービスをインストールするには、cmd「console.exe install」と入力します。

その後、Windowsサービスマネージャでサービスを開始および停止できます。

9
Misterhex

私はそれがあなたが使用しているOSに依存していると思います、セッション間の分離のためにVistaはサービスに接続するのがはるかに難しいです。

私が過去に使用した2つのオプションは以下のとおりです。

  • (Windows用デバッグツールの)GFlagsを使用して、プロセス用の恒久的なデバッガを設定します。これは "Image File Execution Options"レジストリキーにあり、非常に便利です。 「デスクトップとの対話」を有効にするには、サービス設定を調整する必要があると思います。サービスだけでなく、あらゆる種類のデバッグにこれを使用します。
  • もう1つの選択肢は、サービス部分が通常のアプリ起動と交換可能になるように、コードを少し分けることです。こうすることで、単純なコマンドラインフラグを使用して(サービスではなく)プロセスとして起動できるため、デバッグがはるかに簡単になります。

お役に立てれば。

8
RichS

私がサービスを書くとき、私はすべてのサービスロジックをdllプロジェクトに入れて、このdllを呼び出す2つの「ホスト」を作成します。1つはWindowsサービスで、もう1つはコマンドラインアプリケーションです。

私はデバッグにコマンドラインアプリケーションを使用し、コマンドラインアプリケーションでは再現できないバグについてのみデバッガを実際のサービスにアタッチします。

このアプローチを使用するのは、実際のサービスで実行している間にすべてのコードをテストする必要があることを覚えておいてください。

5
Nir

SCMのフレームワーク内で完全なサービスの振る舞いで実行しながら、OnStart()での初期化を含め、私のサービスのあらゆる側面をデバッグできることを望みます。

私はこれを、同じプロジェクト内でデバッグに使用するための2番目のサービスを作成することによって行います。デバッグサービスは、通常通りに(つまり、services MMC pluginで)開始されると、サービスHostプロセスを作成します。実際のサービスをまだ開始していない場合でも、これによってデバッガをアタッチするプロセスが得られます。デバッガをプロセスにアタッチした後、実際のサービスを開始すると、OnStart()を含むサービスライフサイクルのどこにでも侵入できます。

最小限のコードの侵入しか必要としないため、デバッグサービスはサービスセットアッププロジェクトに簡単に含めることができ、1行のコードをコメントアウトして1つのプロジェクトインストーラーを削除することで運用リリースから簡単に削除できます。

詳細:

1)MyServiceを実装していると仮定して、MyServiceDebugも作成します。以下のように、両方をProgram.csServiceBase配列に追加します。

    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    static void Main()
    {
        ServiceBase[] ServicesToRun;
        ServicesToRun = new ServiceBase[] 
        { 
            new MyService(),
            new MyServiceDebug()
        };
        ServiceBase.Run(ServicesToRun);
    }

2)サービスプロジェクトのプロジェクトインストーラに実際のサービスとデバッグサービスを追加します。

enter image description here

サービスプロジェクトの出力をサービスのセットアッププロジェクトに追加すると、両方のサービス(実際とデバッグ)が含まれます。インストール後、両方のサービスがservice.msc MMCプラグインに表示されます。

3)MMCでデバッグサービスを開始します。

4)Visual Studioで、デバッグサービスによって開始されたプロセスにデバッガを接続します。

5)実際のサービスを開始してデバッグを楽しんでください。

5
BitMask777

Windowsサービスを開発しデバッグするとき、私は通常/ console起動パラメータを追加してこれをチェックすることによってそれをコンソールアプリケーションとして実行します。人生をずっと楽にします。

static void Main(string[] args) {
    if (Console.In != StreamReader.Null) {
        if (args.Length > 0 && args[0] == "/console") {
            // Start your service work.
        }
    }
}
4
Maurice

1行目のDebugger.Break()はどうですか。

4
leppie

Windowsサービスをデバッグするには、GFlagsとregeditによって作成された.regファイルを組み合わせます。

  1. Exe-nameとvsjitdebuggerを指定してGFlagsを実行します。
  2. Regeditを実行して、GFlagsがオプションを設定している場所に移動します
  3. ファイルメニューから「キーのエクスポート」を選択します
  4. そのファイルを.reg拡張子を付けてどこかに保存します。
  5. サービスをデバッグしたいときはいつでも:.regファイルをダブルクリック
  6. デバッグを中止したい場合は、2番目の.regファイルをダブルクリックします。

または、次のスニペットを保存してservicename.exeを目的の実行可能ファイル名に置き換えます。


debugon.reg:

WindowsレジストリエディタVersion 5.00 
 
 [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Imageファイル実行オプション\ servicename.exe] 
 "GlobalFlag" = "0x00000000" 
 "Debugger" = "vsjitdebugger.exe"

debugoff.reg:

WindowsレジストリエディタVersion 5.00 
 
 [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Imageファイル実行オプション\ servicename.exe] 
 "GlobalFlag" = "0x00000000"
2
janv8000

これは質問の後にかなりありますが、今後の参考になるかもしれません。

できれば、優秀な TopShelf プロジェクトを使うことをお勧めします。それはWindowsサービスの開発とデバッグを非常に容易にし、追加も展開を幾分容易にします。

それをチェックしてください: http://topshelf-project.com/

1
Jeremy Wiebe
#if DEBUG
    System.Diagnostics.Debugger.Break();
#endif
1
Ervin Ter

私はJOPの答えにバリエーションを使います。コマンドラインパラメータを使用して、プロジェクトのプロパティを使用して、またはWindowsのサービスマネージャを使用してIDEでデバッグモードを設定できます。

protected override void OnStart(string[] args)
{
  if (args.Contains<string>("DEBUG_SERVICE"))
  {
    Debugger.Break();
  }
  ...
}
1
nh99

既存のWindowsサービスプログラムのトラブルシューティングについては、他の人が示唆しているように 'Debugger.Break()'を使用してください。

新しいWindowsサービスプログラムのために、私はジェームズマイケルヘアーの方法を使うことをお勧めします http://geekswithblogs.net/BlackRabbitCoder/archive/2011/03/01/c-toolbox-debug-able-self-installable-windows- service-template-redux.aspx

1
Yang You

日常的な小規模なプログラミングのために、私は自分のサービスを簡単にデバッグするための非常に簡単なトリックをしました:

サービスの開始時に、私はコマンドラインパラメータ "/ debug"をチェックします。このパラメータでサービスが呼び出された場合は、通常のサービス起動は行わず、すべてのリスナーを起動し、「Debug in progress、end to ok to end」というメッセージボックスを表示するだけです。

したがって、私のサービスが通常の方法で開始された場合は、サービスとして開始されます。コマンドラインパラメータ/ debugを指定して開始された場合は、通常のプログラムのように動作します。

VSでは、デバッグパラメータとして/ debugを追加し、サービスプログラムを直接起動します。

このようにして、私はほとんどの小さな種類の問題を簡単にデバッグすることができます。もちろん、いくつかのものはまだサービスとしてデバッグされる必要があるでしょう、しかし99%のためにこれは十分に良いです。

1
Sam

デバッガのランチをどこかに置いて、起動時にVisualstudioをアタッチするだけです。

#if DEBUG
    Debugger.Launch();
#endif

また、管理者としてVSを起動する必要があります。また、プロセスは異なるユーザーによって自動的にデバッグされるようにする必要があります(ここで説明されているように)。

reg add "HKCR\AppID{E62A7A31-6025-408E-87F6-81AEB0DC9347}" /v AppIDFlags /t REG_DWORD /d 8 /f
1
wotanii

WindowsサービステンプレートC#プロジェクトを使用して新しいサービスアプリを作成します https://github.com/HarpyWar/windows-service-template

自動的に検出されるコンソール/サービスモード、あなたのサービスの自動インストーラ/アンインストーラ、そしていくつかの最もよく使われる機能が含まれています。

1
HarpyWar
static class Program
{
    static void Main()
    {
        #if DEBUG

        // TODO: Add code to start application here

        //    //If the mode is in debugging
        //    //create a new service instance
        Service1 myService = new Service1();

        //    //call the start method - this will start the Timer.
        myService.Start();

        //    //Set the Thread to sleep
        Thread.Sleep(300000);

        //    //Call the Stop method-this will stop the Timer.
        myService.Stop();

         #else
        ServiceBase[] ServicesToRun;
        ServicesToRun = new ServiceBase[] 
        { 
            new Service1() 
        };

        ServiceBase.Run(ServicesToRun);
         #endif
    }
}
1
Mansoor

これは、私がサービスをテストするために使用した簡単な方法です。追加の "Debug"メソッドはなく、統合されたVSユニットテストがあります。

[TestMethod]
public void TestMyService()
{
    MyService fs = new MyService();

    var OnStart = fs.GetType().BaseType.GetMethod("OnStart", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);

    OnStart.Invoke(fs, new object[] { null });
}

// As an extension method
public static void Start(this ServiceBase service, List<string> parameters)
{
     string[] par = parameters == null ? null : parameters.ToArray();

     var OnStart = service.GetType().GetMethod("OnStart", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);

     OnStart.Invoke(service, new object[] { par });
}
1
MisterDr

最善の選択肢は、 'System.Diagnostics'名前空間を使用することです。

以下のように、デバッグモードとリリースモードをブロックする場合は、コードを囲み、ビジュアルスタジオでデバッグモードとリリースモードを切り替えます。

#if DEBUG  // for debug mode
       **Debugger.Launch();**  //debugger will hit here
       foreach (var job in JobFactory.GetJobs())
            {
                //do something 
            }

#else    // for release mode
      **Debugger.Launch();**  //debugger will hit here
     // write code here to do something in Release mode.

#endif
0
Amey P Naik

貼り付けるだけ

Debugger.Break();

コード内のどこにでもあります。

例えば ​​、

internal static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        private static void Main()
        {
            Debugger.Break();
            ServiceBase[] ServicesToRun;
            ServicesToRun = new ServiceBase[]
            {
                new Service1()
            };
            ServiceBase.Run(ServicesToRun);
        }
    }

プログラムを実行するとDebugger.Break();がヒットします。

0

デバッグを実行する方法は2つあります。

  1. ログファイルを作成します。個人的には、アプリケーションログやイベントログを使用するのではなく、テキストファイルのような別のログファイルを使用することをお勧めします。
  2. アプリケーションをコンソールアプリケーションに変換します。これにより、VSで使用できるすべてのデバッグツールが有効になります。

トピックについては私が作成した THIS ブログ記事を参照してください。

0
Sandaru