質問

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

役に立ちましたか?

解決

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

アップデート: の代替として #if DEBUG プラグマも使用できます Conditional("DEBUG_SERVICE") 属性。

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

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

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

そこでは、コードはデバッグ ビルド中にのみ有効になります。その際、サービスのデバッグ用に別のビルド構成を作成すると便利な場合があります。

他のヒント

また、通常の実行とサービスとして別の「バージョン」を持つことが望ましい方法だとも思いますが、その目的のために別のコマンドラインスイッチを専用にする必要は本当にあるのでしょうか?

次のことはできませんか:

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, (非対話型) サービスとして実行されることは別として?

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

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

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

必要なコードはこれだけですが、次のコードも書きました ウォークスルー 説明付き。

何が起こっているかを分析することが重要な場合もあります サービスの立ち上げ時。 サービスの起動中にデバッガーを接続するのに十分な時間がないため、プロセスに接続してもここでは役に立ちません。

簡単に言うと、私は次のものを使用しています 4行のコード これをする:

#if DEBUG
    base.RequestAdditionalTime(600000); // 600*1000ms = 10 minutes timeout
    Debugger.Launch(); // launch and attach debugger
#endif

これらは、 OnStart サービスの方法は次のとおりです。

protected override void OnStart(string[] args)
{
    #if DEBUG
       base.RequestAdditionalTime(600000); // 10 minutes timeout for startup
       Debugger.Launch(); // launch and attach debugger
    #endif
    MyInitOnstart(); // my individual initialization code for the service
    // allow the base class to perform any work it needs to do
    base.OnStart(args);
}

まだやったことがない人のために、以下を含めました 詳細なヒントは以下にあります, 、行き詰まりやすいからです。次のヒントは以下を参照します Windows 7x64 そして Visual Studio 2010 チーム エディション, ですが、他の環境でも有効なはずです。


重要: サービスをデプロイする 「マニュアル」モード (いずれかを使用して、 InstallUtil VS コマンド プロンプトからユーティリティを実行するか、準備したサービス インストーラー プロジェクトを実行します)。Visual Studioを開く 前に サービスを開始し、サービスのソース コードを含むソリューションを読み込みます。Visual Studio で必要に応じて追加のブレークポイントを設定します。次に、 サービスコントロールパネル。

のせいで Debugger.Launch コードを実行すると、「ハンドルされない Microsoft .NET Framework 例外が発生しました」というダイアログが表示されます。 サービス名.exe。" 現れる。クリック Elevate はい、デバッグします サービス名.exe スクリーンショットに示すように:
FrameworkException

その後、特に Windows 7 では、UAC によって管理者の資格情報の入力を求められる場合があります。それらを入力して次に進みます はい:

UACPrompt

その後、よく知られているのは、 Visual Studio のジャストインタイム デバッガー ウィンドウ が表示されます。選択したデバッガーを使用してデバッグするかどうかを尋ねられます。 クリックする前に はい, あなたがそれを選択してください 新しいインスタンスを開きたくない (2 番目のオプション) - ソース コードが表示されないため、新しいインスタンスはここでは役に立ちません。そこで、代わりに、以前に開いた Visual Studio インスタンスを選択します。VSDebuggerPrompt

クリックした後 はい, しばらくすると、Visual Studio は、 Debugger.Launch ステートメントが存在し、コードをデバッグできるようになります (メソッド MyInitOnStart, 、これには初期化が含まれます)。VSDebuggerBreakpoint

プレス中 F5 すぐに実行を続行し、 準備した次のブレークポイントに到達するまで。

ヒント: サービスを実行し続けるには、次を選択します。 デバッグ -> すべてデタッチ. 。これにより、サービスが正しく起動し、起動コードのデバッグが完了した後で、サービスと通信するクライアントを実行できるようになります。押したら シフト+F5 (デバッグの停止)、これによりサービスが終了します。これを行う代わりに、 サービスコントロールパネル それを止めるために。

注記 それ

  • を構築する場合 リリース、 そうして デバッグコードは自動的に削除されます そしてサービスは正常に実行されます。

  • 使っています Debugger.Launch(), 、 どれの デバッガを起動してアタッチします. 。テストしました Debugger.Break() 同様に、 動作しませんでした, サービスの起動時にデバッガがまだアタッチされていないため ( 「エラー 1067:プロセスが予期せず終了しました。」).

  • RequestAdditionalTime より長く設定します サービス起動のタイムアウト (それは ない コード自体は遅れますが、すぐに続行されます。 Debugger.Launch 声明)。そうしないと、サービスを開始するためのデフォルトのタイムアウトが短すぎるため、呼び出さないとサービスの開始が失敗します。 base.Onstart(args) デバッガからすぐに実行できます。実際には、10 分のタイムアウトにより、「」というメッセージが表示されなくなります。サービスが応答しませんでした...」 デバッガ起動直後。

  • 慣れてしまえば、この方法は以下を行うだけなのでとても簡単です。 4行追加 既存のサービス コードに追加できるため、すぐに制御してデバッグできるようになります。

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

/ConsoleRunner
   /....
/ServiceRunner
   /....
/ApplicationLogic
   /....

これ Fabio Scopel による YouTube ビデオ Windows サービスを非常にうまくデバッグする方法を説明しています...実際の方法はビデオの 4:45 から始まります。

ビデオで説明されているコードは次のとおりです...Program.cs ファイルに、Debug セクションの内容を追加します。

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 プロジェクトがデバッグ モードでコンパイルされている場合。これにより、条件が true のときにデバッグ セクション (下記) を実行できるようになります。

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

コンソール アプリケーションと同じように実行され、問題がなければモードを変更できます。 Release そして定期的な else セクションがロジックをトリガーします

アップデート

このアプローチはこれまでで最も簡単です。

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

後世のために、私の元の答えを以下に残しておきます。


私のサービスには、実行すべき作業があるかどうかを定期的にチェックしてもらいたいため、Timer をカプセル化するクラスが含まれる傾向があります。

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

この設計の良い副作用は、タイマーを設定する引数を使用して、サービスが実際に動作し始める前に遅延を与えることができるため、デバッガーを手動でアタッチする時間を確保できることです。

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

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

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
}

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

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

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

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

どの言語を使用していますか?

使用 いちばん上の棚 図書館。

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

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()
        {

        }
    }

サービスをデバッグするには、Visual Studio で F5 キーを押すだけです。

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

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

使用している OS にもよると思いますが、Vista ではセッションが分離されているため、サービスに接続するのがはるかに困難です。

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

  • GFlags (Windows 用デバッグ ツール内) を使用して、プロセスの永続的なデバッガーをセットアップします。これは「イメージ ファイル実行オプション」レジストリ キーに存在し、非常に便利です。「デスクトップとの対話」を有効にするには、サービス設定を調整する必要があると思います。私はこれをサービスだけでなく、あらゆる種類のデバッグに使用します。
  • もう 1 つのオプションは、コードを少し分離して、サービス部分を通常のアプリの起動と交換できるようにすることです。そうすることで、単純なコマンド ライン フラグを使用し、(サービスではなく) プロセスとして起動できるため、デバッグがはるかに簡単になります。

お役に立てれば。

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

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

このアプローチを使用する場合、実際のサービスで実行中にすべてのコードをテストする必要があることに注意してください。コマンド ライン ツールはデバッグに役立ちますが、別の環境であり、実際のサービスとまったく同じように動作するわけではありません。

SCM のフレームワーク内で完全なサービス動作を実行しながら、OnStart() での初期化を含むサービスのあらゆる側面をデバッグできるようにしたいと考えています...「コンソール」モードや「アプリ」モードはありません。

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

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

詳細:

1) 実装していると仮定します。 MyService, 、も作成します MyServiceDebug. 。両方を追加します ServiceBase 配列 Program.cs そのようです:

    /// <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) 実際のサービスを起動し、デバッグをお楽しみください。

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

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

最初の行の Debugger.Break() はどうでしょうか?

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

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

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


デブゴン.reg:

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\servicename.exe]
"GlobalFlag"="0x00000000"
"Debugger"="vsjitdebugger.exe"

デバッグオフ.reg:

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\servicename.exe]
"GlobalFlag"="0x00000000"

日常的なちょっとしたプログラミングでは、サービスを簡単にデバッグするための非常に簡単なトリックを実行しました。

サービスの開始時に、コマンド ライン パラメーター「/debug」を確認します。このパラメーターを使用してサービスが呼び出された場合、通常のサービスの起動は行わず、代わりにすべてのリスナーを起動し、「デバッグが進行中です。終了するには OK を押してください」というメッセージ ボックスを表示するだけです。

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

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

こうすることで、ほとんどの小さな種類の問題を簡単にデバッグできます。もちろん、サービスとしてデバッグする必要があるものもありますが、99% の場合はこれで十分です。

#if DEBUG
    System.Diagnostics.Debugger.Break();
#endif

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

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

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

新しい Windows サービス プログラムの場合は、James Michael Hare の方法を使用することをお勧めします。 http://geekswithblogs.net/BlackRabbitCoder/archive/2011/03/01/c-toolbox-debug-able-self-installable-windows-service-template-redux.aspx

デバッガー ランチを任意の場所に置き、起動時に Visualstudio をアタッチするだけです

#if DEBUG
    Debugger.Launch();
#endif

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

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

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

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

これは、追加の「デバッグ」メソッドを使用せず、統合された 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 });
}
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
    }
}

デバッグを行うには 2 つのオプションがあります。

  1. ログ ファイルを作成します。個人的には、アプリケーション ログやイベント ログを使用するのではなく、テキスト ファイルのような別個のログ ファイルを使用することを好みます。しかし、正確なエラーの場所を把握するのは依然として難しいため、時間の代わりに多大なコストがかかります。
  2. アプリケーションをコンソール アプリケーションに変換します。これにより、VS で使用できるすべてのデバッグ ツールが有効になります。

ご参照ください これ トピック用に作成したブログ投稿。

貼り付けるだけ

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(); プログラムを実行するとき。

最良のオプションは、「」を使用することです。システム診断' 名前空間。

Visual Studio でデバッグ モードとリリース モードを切り替えるには、以下に示すようにコードをデバッグ モードとリリース モードの if else ブロックで囲みます。

#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
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top