Windows サービス コントロール マネージャーを使用してサービスを開始し、デバッガーをスレッドにアタッチするよりも、コードをステップ実行する簡単な方法はありますか? ちょっと面倒なので、もっと簡単なアプローチがあるかどうか疑問に思っています。
28 に答える
サービスをすばやくデバッグしたい場合は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)、F5Visual 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 Team Editionを参照していますが、他の環境にも当てはまるはずです。
重要:サービスを「手動」モードInstallUtil
で展開します ( VS コマンド プロンプトからユーティリティを使用するか、準備したサービス インストーラー プロジェクトを実行します)。サービスを開始する前にVisual Studioを開き、サービスのソース コードを含むソリューションをロードします。Visual Studio で必要に応じて追加のブレークポイントを設定し、サービス コントロール パネルからサービスを開始します。
コードが原因で、 「 Servicename.exeDebugger.Launch
で未処理の Microsoft .NET Framework 例外が発生しました」というダイアログが表示されます。現れる。スクリーンショットに示すようにクリックします。 Yes, debug Servicename.exe
その後、特に Windows 7 UAC では、管理者の資格情報の入力を求められる場合があります。それらを入力して、次の手順に進みますYes。
その後、おなじみの Visual Studio Just-In-Time Debugger ウィンドウが表示されます。選択したデバッガーを使用してデバッグするかどうかを尋ねられます。をクリックする前にYes、新しいインスタンスを開きたくないことを選択します(2 番目のオプション)。ソース コードが表示されないため、ここでは新しいインスタンスは役に立ちません。そのため、代わりに以前に開いた Visual Studio インスタンスを選択します。
をクリックYesした後、しばらくすると、Visual Studio のステートメントがある行に黄色の矢印が表示され、コード (初期化を含む
Debugger.Launch
メソッド) をデバッグできるようになります。MyInitOnStart
を押すとF5、準備した次のブレークポイントに到達するまで、すぐに実行が続行されます。
ヒント:サービスを実行し続けるには、[ Debug] -> [Detach all ] を選択します。これにより、サービスが正常に起動し、スタートアップ コードのデバッグが完了した後に、サービスと通信するクライアントを実行できます。Shift+F5 (デバッグの停止)を押すと、サービスが終了します。これを行う代わりに、サービス コントロール パネルを使用して停止する必要があります。
注意してください
リリースをビルドすると、デバッグ コードが自動的に削除され、サービスが正常に実行されます。
デバッガー
Debugger.Launch()
を起動してアタッチするを使用しています。私もテストしましたが、サービスの起動時にまだデバッガーが接続されていないため、動作しませんでした ( 「エラー 1067: プロセスが予期せず終了しました」の原因)。Debugger.Break()
RequestAdditionalTime
サービスの起動のタイムアウトを長く設定します(コード自体を遅らせることはありませんDebugger.Launch
が、ステートメントをすぐに続行します)。base.Onstart(args)
そうしないと、サービスを開始するためのデフォルトのタイムアウトが短すぎて、デバッガーから十分に迅速に呼び出さないと、サービスの開始に失敗します。実際には、10 分のタイムアウトにより、デバッガーの開始直後に「サービスが応答しませんでした...」というメッセージが表示されるのを回避できます。慣れると、この方法は非常に簡単です。既存のサービス コードに4 行を追加するだけで、すぐに制御とデバッグを行うことができるからです。
私が通常行うことは、サービスのロジックを別のクラスにカプセル化し、それを「ランナー」クラスから開始することです。このランナー クラスは、実際のサービスまたは単なるコンソール アプリケーションにすることができます。したがって、ソリューションには(少なくとも)3 つのプロジェクトがあります。
/ConsoleRunner
/....
/ServiceRunner
/....
/ApplicationLogic
/....
アップデート
このアプローチははるかに簡単です。
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
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 で実行されているかどうかを実際に検出し、この切り替えを自動的に実行できます。
どの言語を使用していますか?
使用しているOSに依存すると思います.Vistaはセッション間の分離のため、サービスに接続するのがはるかに困難です.
過去に使用した2つのオプションは次のとおりです。
- GFlags (Debugging Tools for Windows) を使用して、プロセスの永続的なデバッガーをセットアップします。これは「Image File Execution Options」レジストリ キーに存在し、非常に便利です。「デスクトップとの対話」を有効にするには、サービス設定を微調整する必要があると思います。サービスだけでなく、あらゆる種類のデバッグにこれを使用します。
- もう 1 つのオプションは、サービス部分が通常のアプリの起動と交換できるように、コードを少し分離することです。そうすれば、単純なコマンド ライン フラグを使用して、(サービスではなく) プロセスとして起動できるため、デバッグがはるかに簡単になります。
お役に立てれば。
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) 実際のサービスを開始し、デバッグをお楽しみください。
サービスを作成するときは、すべてのサービス ロジックを dll プロジェクトに配置し、この dll を呼び出す 2 つの "ホスト" を作成します。1 つは Windows サービスで、もう 1 つはコマンド ライン アプリケーションです。
デバッグにはコマンド ライン アプリケーションを使用し、コマンド ライン アプリケーションで再現できないバグに対してのみデバッガを実際のサービスに接続します。
このアプローチを使用する場合は、実際のサービスで実行中にすべてのコードをテストする必要があることを覚えておいてください。コマンド ライン ツールはデバッグを支援する優れたツールですが、これは異なる環境であり、実際のサービスとまったく同じようには動作しません。
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() はどうですか?
これは、追加の「デバッグ」メソッドを使用せず、統合された 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 });
}
Windows サービスをデバッグするには、GFlags と regedit によって作成された .reg ファイルを組み合わせます。
- exe-name と vsjitdebugger を指定して、GFlags を実行します。
- regedit を実行し、GFlags がオプションを設定する場所に移動します。
- ファイルメニューから「キーのエクスポート」を選択します
- そのファイルを .reg 拡張子でどこかに保存します
- サービスをデバッグしたいときはいつでも: .reg ファイルをダブルクリックします。
- デバッグを停止する場合は、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"
#if DEBUG
System.Diagnostics.Debugger.Break();
#endif
ルーチンの小さなプログラミングでは、サービスを簡単にデバッグするための非常に単純なトリックを実行しました。
サービスの開始時に、コマンド ライン パラメーター「/debug」を確認します。このパラメーターでサービスが呼び出された場合、通常のサービスの起動は行わず、代わりにすべてのリスナーを起動し、「デバッグが進行中です。OK を押して終了します」というメッセージ ボックスを表示するだけです。
したがって、私のサービスが通常の方法で開始されると、サービスとして開始されます。コマンド ライン パラメータ /debug で開始されると、通常のプログラムのように動作します。
VS では、/debug をデバッグ パラメーターとして追加し、サービス プログラムを直接開始します。
このようにして、ほとんどの小さな種類の問題を簡単にデバッグできます。もちろん、サービスとしてデバッグする必要があるものもありますが、99% はこれで十分です。
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
デバッグを行うには 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();
プログラムを実行するとヒットします。