342

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

4

28 に答える 28

277

サービスをすばやくデバッグしたい場合は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 */
}

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

于 2008-09-24T08:24:47.453 に答える
218

また、通常の実行用とサービスとしての別の「バージョン」を用意するのも良い方法だと思いますが、そのために別のコマンドラインスイッチを専用にする必要があるのでしょうか。

あなたはただすることができませんでした:

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

これには「利点」があり、ダブルクリックでアプリを起動するだけで(本当に必要な場合はOK)、F5Visual Studioでヒットするだけです(プロジェクト設定を変更してその/consoleオプションを含める必要はありません)。

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

于 2008-09-24T09:13:59.800 に答える
134

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

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

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

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

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

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

于 2012-05-31T17:12:43.753 に答える
54

サービスの開始時に何が起こっているかを分析することが重要な場合があります。サービスの起動中にデバッガーをアタッチするのに十分な速さがないため、プロセスへのアタッチはここでは役に立ちません。

簡単に言えば、これを行うために次の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 Team Editionを参照していますが、他の環境にも当てはまるはずです。


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

コードが原因で、 「 Servicename.exeDebugger.Launchで未処理の Microsoft .NET Framework 例外が発生しました」というダイアログが表示されます。現れる。スクリーンショットに示すようにクリックします。Elevate Yes, debug Servicename.exe
FrameworkException

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

UACプロンプト

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

をクリックYesた後、しばらくすると、Visual Studio のステートメントがある行に黄色の矢印が表示され、コード (初期化を含む Debugger.Launchメソッド) をデバッグできるようになります。MyInitOnStartVSDebuggerブレークポイント

を押すとF5、準備した次のブレークポイントに到達するまで、すぐに実行が続行されます。

ヒント:サービスを実行し続けるには、[ Debug] -> [Detach all ] を選択します。これにより、サービスが正常に起動し、スタートアップ コードのデバッグが完了した後に、サービスと通信するクライアントを実行できます。Shift+F5 (デバッグの停止)を押すと、サービスが終了します。これを行う代わりに、サービス コントロール パネルを使用して停止する必要があります。

注意してください

  • リリースをビルドすると、デバッグ コードが自動的に削除され、サービスが正常に実行されます。

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

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

  • 慣れると、この方法は非常に簡単です。既存のサービス コードに4 行を追加するだけで、すぐに制御とデバッグを行うことができるからです。

于 2012-10-08T14:00:59.143 に答える
40

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

/ConsoleRunner
   /....
/ServiceRunner
   /....
/ApplicationLogic
   /....
于 2008-09-24T08:14:07.200 に答える
15

アップデート

このアプローチははるかに簡単です。

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

私は後世のために私の元の答えを以下に残します。


私のサービスには、タイマーをカプセル化するクラスがある傾向があります。これは、サービスに実行する作業があるかどうかを定期的にチェックするためです。

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

この設計の優れた副作用は、タイマーを設定する引数を使用して、サービスが実際に動作を開始する前に遅延を生じさせることができるため、デバッガーを手動で接続する時間ができることです。

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]フラグを使用しました)。

リリースでビルドするのを忘れて、クライアントデモで実行されているアプリでデバッガーが中断することがあるため、この方法をやめました(恥ずかしいです!)。

#if DEBUG
if (!System.Diagnostics.Debugger.IsAttached)
{
    System.Diagnostics.Debugger.Break();
}
#endif
于 2008-09-24T08:59:11.117 に答える
13

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
}
于 2008-09-29T21:43:46.123 に答える
10

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

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

于 2008-09-24T08:13:20.400 に答える
10

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

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

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

于 2008-09-24T08:13:57.793 に答える
8

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

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

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

お役に立てれば。

于 2008-09-24T08:14:42.127 に答える
6

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

これを行うには、デバッグに使用する 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) サービス プロジェクトのプロジェクト インストーラーに、実際のサービスとデバッグ サービスを追加します。

ここに画像の説明を入力

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

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

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

5) 実際のサービスを開始し、デバッグをお楽しみください。

于 2011-07-01T17:20:33.753 に答える
5

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

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

このアプローチを使用する場合は、実際のサービスで実行中にすべてのコードをテストする必要があることを覚えておいてください。コマンド ライン ツールはデバッグを支援する優れたツールですが、これは異なる環境であり、実際のサービスとまったく同じようには動作しません。

于 2008-09-24T08:16:00.283 に答える
4

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

static void Main(string[] args) {
    if (Console.In != StreamReader.Null) {
        if (args.Length > 0 && args[0] == "/console") {
            // Start your service work.
        }
    }
}
于 2008-09-24T08:17:38.673 に答える
4

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

于 2008-09-24T08:18:22.493 に答える
2

これは、追加の「デバッグ」メソッドを使用せず、統合された 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 });
}
于 2017-01-04T13:20:38.543 に答える
2

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 レジストリ エディタ バージョン 5.00

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

debugoff.reg:

Windows レジストリ エディタ バージョン 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\servicename.exe]
"GlobalFlag"="0x00000000"
于 2008-09-24T13:30:19.793 に答える
1
#if DEBUG
    System.Diagnostics.Debugger.Break();
#endif
于 2008-11-24T07:49:37.530 に答える
1

ルーチンの小さなプログラミングでは、サービスを簡単にデバッグするための非常に単純なトリックを実行しました。

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

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

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

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

于 2008-09-24T08:15:24.497 に答える
1

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

protected override void OnStart(string[] args)
{
  if (args.Contains<string>("DEBUG_SERVICE"))
  {
    Debugger.Break();
  }
  ...
}
于 2009-04-27T23:27:33.040 に答える
1

既存の 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

于 2015-05-19T23:02:36.613 に答える
0

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

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

このトピックについて作成したこのブログ投稿を参照してください。

于 2015-07-21T08:19:22.640 に答える
0

貼り付けるだけ

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();プログラムを実行するとヒットします。

于 2017-03-16T09:59:02.083 に答える