回答:
サービスをすばやくデバッグしたい場合は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
、(非インタラクティブ)サービスとして実行される以外に、フラグが返される理由は他にありますか?
System.Diagnostics.Debugger.IsAttached
代わりにを使用できますEnvironment.UserInteractive
。
数週間前に新しいサービスプロジェクトを立ち上げたときに、この投稿を見つけました。多くのすばらしい提案がありますが、私が望んでいた解決策はまだ見つかりませんでした。サービスクラスを変更せずにサービスクラスOnStart
とOnStop
メソッドを呼び出す可能性。
私が思いついたソリューションは、Environment.Interactive
この投稿の他の回答で示唆されているように、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);
}
これが必要なすべてのコードですが、説明付きのウォークスルーも作成しました。
walk through
)追加するのは、Console Application
コンパイルして実行する前に、プロジェクトのプロパティに移動し、出力タイプをに変更することだけです。で見つけてくださいProject Properties -> Application -> Output type -> Console Application
。また、これが正しく機能するために、start
コマンドを使用してアプリケーションを実行する必要がありました。例:C:\"my app name.exe" -service
私にはうまくいきません。代わりに使用しましたC:\start /wait "" "my app name.exe" -service
サービスの起動時に何が起こっているかを分析することが重要な場合があります。プロセスのアタッチはここでは役に立ちません。サービスの起動中にデバッガーをアタッチするのに十分な速さがないためです。
簡単に言えば、私はこれを行うために次の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を開き、サービスのソースコードを含むソリューションを読み込みます。VisualStudioで必要に応じて追加のブレークポイントを設定し、サービスコントロールパネルからサービスを開始します。
そのためのDebugger.Launch
コード、これはダイアログの原因となります「未処理のMicrosoft .NET Frameworkの例外が発生した中でServicename.exe。」現れる。スクリーンショットに示すようにクリックします。 Yes, debug Servicename.exe
その後、特にWindows 7では、UACが管理者の資格情報の入力を要求する場合があります。それらを入力して続行してYesください:
その後、有名なVisual Studio Just-In-Time Debuggerウィンドウが表示されます。選択したデバッガを使用してデバッグするかどうかを尋ねます。あなたがクリックする前にYes、あなたがすることを選択し、新しいインスタンスオープンにしたくない(第二オプション) -ソースコードが表示されないため、新しいインスタンスが、ここでは有用ではないでしょう。代わりに、先ほど開いたVisual Studioインスタンスを選択します。
をクリックYesした後、しばらくすると、Visual StudioによってDebugger.Launch
ステートメントのある行に黄色の矢印が表示され、コード(メソッドMyInitOnStart
、初期化を含む)をデバッグできるようになります。
を押すとF5、準備した次のブレークポイントに到達するまで、すぐに実行が続行されます。
ヒント:サービスの実行を継続するには、[ デバッグ]-> [すべて切り離す]を選択します。これにより、サービスが正しく起動し、起動コードのデバッグが終了した後で、サービスと通信するクライアントを実行できます。Shift+F5(デバッグを停止)を押すと、サービスが終了します。これを行う代わりに、サービスコントロールパネルを使用して停止する必要があります。
注意してください
リリースをビルドすると、デバッグコードが自動的に削除され、サービスが正常に実行されます。
使ってます Debugger.Launch()
デバッガーを起動してアタッチするています。私がテストしているDebugger.Break()
され、同様に仕事しませんでしたサービスの起動時に添付なしデバッガがまだ存在しないため(原因、「エラー1067:プロセスが予期せず終了します」)。
RequestAdditionalTime
サービスの起動により長いタイムアウトを設定します(コード自体を遅延させることはありませんが、すぐにDebugger.Launch
ステートメントを続行します)。そうしないと、サービスを開始するためのデフォルトのタイムアウトが短すぎbase.Onstart(args)
て、デバッガーから十分に速く呼び出さないとサービスの開始が失敗します。実際には、10分のタイムアウトにより、デバッガーの起動直後に「サービスが応答しませんでした...」というメッセージが表示されなくなります。
慣れれば、この方法は非常に簡単です。既存のサービスコードに4行を追加するだけで、すぐに制御とデバッグを行うことができます。
base.RequestAdditionalTime(600000)
呼び出さbase.OnStart(args)
れない場合、サービスコントロールはサービスを10分間終了しません)。それとは別に、しばらくして管理者の資格情報を入力しなかった場合もUACが中止されることを覚えています(正確には秒数はわかりませんが、1分以内に入力する必要があると思います。それ以外の場合はUACが中止されます) 、デバッグセッションを終了します。
私が通常行うことは、サービスのロジックを別のクラスにカプセル化し、それを「ランナー」クラスから開始することです。このランナークラスは、実際のサービスまたは単なるコンソールアプリケーションにすることができます。したがって、ソリューションには(少なくとも)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
元の答えは後世に残しておきます。
私のサービスには、タイマーがカプセル化されたクラスがある傾向があります。これは、サービスが実行する作業があるかどうかを定期的にチェックするためです。
クラスを新しくして、サービスの起動中に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]フラグを使用しています)。
リリースでビルドするのを忘れて、クライアントデモで実行されているアプリでデバッガーが中断することがあるので、この方法でこれをやめました(恥ずかしい!)。
#if DEBUG
if (!System.Diagnostics.Debugger.IsAttached)
{
System.Diagnostics.Debugger.Break();
}
#endif
// do something
完了までに30分以上かかるとどうなりますか?
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
}
OnStart
でprotected
、あなたはアクセスレベルを変更することはできません:(
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()
{
}
}
サービスをデバッグするには、Visual StudioでF5キーを押します。
サービスをインストールするには、cmd「console.exe install」と入力します
その後、Windowsサービスマネージャーでサービスを開始および停止できます。
私はそれがあなたが使っているOSに依存すると思います、Vistaはセッション間の分離のためにサービスに接続するのがはるかに困難です。
過去に使用した2つのオプションは次のとおりです。
お役に立てれば。
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.
}
}
}
Windowsサービスをデバッグするために、GFlagsとregeditによって作成された.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"
日常的な小規模なプログラミングでは、サービスを簡単にデバッグするために非常に簡単なトリックを実行しました。
サービスの開始時に、コマンドラインパラメータ「/ debug」を確認します。このパラメーターを使用してサービスが呼び出された場合、通常のサービスの起動は行わず、代わりにすべてのリスナーを起動し、「Debug in progress、press ok to end」というメッセージボックスを表示します。
したがって、私のサービスが通常の方法で開始されると、サービスとして開始されます。コマンドラインパラメータ/ debugで開始されると、通常のプログラムのように動作します。
VSでは、デバッグパラメータとして/ debugを追加し、サービスプログラムを直接開始します。
このようにして、ほとんどの小さな問題を簡単にデバッグできます。もちろん、一部のものはサービスとしてデバッグする必要がありますが、99%の場合はこれで十分です。
既存の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
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
}
}
貼り付けるだけ
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();
プログラムを実行するとヒットします。
最良のオプションは、 ' System.Diagnostics '名前空間を使用することです。
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