Windowsサービスコントロールマネージャからサービスを開始してからデバッガをスレッドにアタッチするよりも、コードをステップ実行するより簡単な方法はありますか?これはちょっと面倒なので、もっと直接的なアプローチがあるのかと思います。
すぐにサービスをデバッグしたいのであれば、そこに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を作成するのに役立つかもしれません。
また、通常の実行用とサービス用に別々の「バージョン」を用意するのもいいと思いますが、そのためには別のコマンドラインスイッチを割り当てる必要がありますか。
できませんでした。
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
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);
}
これで必要なコードはすべて揃っていますが、説明付きで チュートリアル も書きました。
私が通常することは、サービスのロジックを別のクラスにカプセル化して、それを「ランナー」クラスから開始することです。このランナークラスは実際のサービスでもコンソールアプリケーションでもかまいません。だからあなたの解決策は(少なくとも)3つのプロジェクトがあります。
/ConsoleRunner
/....
/ServiceRunner
/....
/ApplicationLogic
/....
これ 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
セクションはロジックをトリガーします
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
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で実行されているかどうかを実際に検出し、この切り替えを自動的に実行できます。
あなたはどの言語を使っていますか?
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サービスマネージャでサービスを開始および停止できます。
私はそれがあなたが使用しているOSに依存していると思います、セッション間の分離のためにVistaはサービスに接続するのがはるかに難しいです。
私が過去に使用した2つのオプションは以下のとおりです。
お役に立てれば。
私がサービスを書くとき、私はすべてのサービスロジックをdllプロジェクトに入れて、このdllを呼び出す2つの「ホスト」を作成します。1つはWindowsサービスで、もう1つはコマンドラインアプリケーションです。
私はデバッグにコマンドラインアプリケーションを使用し、コマンドラインアプリケーションでは再現できないバグについてのみデバッガを実際のサービスにアタッチします。
このアプローチを使用するのは、実際のサービスで実行している間にすべてのコードをテストする必要があることを覚えておいてください。
SCMのフレームワーク内で完全なサービスの振る舞いで実行しながら、OnStart()での初期化を含め、私のサービスのあらゆる側面をデバッグできることを望みます。
私はこれを、同じプロジェクト内でデバッグに使用するための2番目のサービスを作成することによって行います。デバッグサービスは、通常通りに(つまり、services MMC pluginで)開始されると、サービスHostプロセスを作成します。実際のサービスをまだ開始していない場合でも、これによってデバッガをアタッチするプロセスが得られます。デバッガをプロセスにアタッチした後、実際のサービスを開始すると、OnStart()を含むサービスライフサイクルのどこにでも侵入できます。
最小限のコードの侵入しか必要としないため、デバッグサービスはサービスセットアッププロジェクトに簡単に含めることができ、1行のコードをコメントアウトして1つのプロジェクトインストーラーを削除することで運用リリースから簡単に削除できます。
詳細:
1)MyService
を実装していると仮定して、MyServiceDebug
も作成します。以下のように、両方をProgram.cs
のServiceBase
配列に追加します。
/// <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)実際のサービスを開始してデバッグを楽しんでください。
Windowsサービスを開発しデバッグするとき、私は通常/ console起動パラメータを追加してこれをチェックすることによってそれをコンソールアプリケーションとして実行します。人生をずっと楽にします。
static void Main(string[] args) {
if (Console.In != StreamReader.Null) {
if (args.Length > 0 && args[0] == "/console") {
// Start your service work.
}
}
}
1行目のDebugger.Break()はどうですか。
Windowsサービスをデバッグするには、GFlagsとregeditによって作成された.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"
これは質問の後にかなりありますが、今後の参考になるかもしれません。
できれば、優秀な TopShelf プロジェクトを使うことをお勧めします。それはWindowsサービスの開発とデバッグを非常に容易にし、追加も展開を幾分容易にします。
それをチェックしてください: http://topshelf-project.com/
#if DEBUG
System.Diagnostics.Debugger.Break();
#endif
私はJOPの答えにバリエーションを使います。コマンドラインパラメータを使用して、プロジェクトのプロパティを使用して、またはWindowsのサービスマネージャを使用してIDEでデバッグモードを設定できます。
protected override void OnStart(string[] args)
{
if (args.Contains<string>("DEBUG_SERVICE"))
{
Debugger.Break();
}
...
}
既存のWindowsサービスプログラムのトラブルシューティングについては、他の人が示唆しているように 'Debugger.Break()'を使用してください。
新しいWindowsサービスプログラムのために、私はジェームズマイケルヘアーの方法を使うことをお勧めします http://geekswithblogs.net/BlackRabbitCoder/archive/2011/03/01/c-toolbox-debug-able-self-installable-windows- service-template-redux.aspx
日常的な小規模なプログラミングのために、私は自分のサービスを簡単にデバッグするための非常に簡単なトリックをしました:
サービスの開始時に、私はコマンドラインパラメータ "/ debug"をチェックします。このパラメータでサービスが呼び出された場合は、通常のサービス起動は行わず、すべてのリスナーを起動し、「Debug in progress、end to ok to end」というメッセージボックスを表示するだけです。
したがって、私のサービスが通常の方法で開始された場合は、サービスとして開始されます。コマンドラインパラメータ/ debugを指定して開始された場合は、通常のプログラムのように動作します。
VSでは、デバッグパラメータとして/ debugを追加し、サービスプログラムを直接起動します。
このようにして、私はほとんどの小さな種類の問題を簡単にデバッグすることができます。もちろん、いくつかのものはまだサービスとしてデバッグされる必要があるでしょう、しかし99%のためにこれは十分に良いです。
デバッガのランチをどこかに置いて、起動時に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
自動的に検出されるコンソール/サービスモード、あなたのサービスの自動インストーラ/アンインストーラ、そしていくつかの最もよく使われる機能が含まれています。
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
}
}
これは、私がサービスをテストするために使用した簡単な方法です。追加の "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 });
}
最善の選択肢は、 '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
貼り付けるだけ
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();
がヒットします。
デバッグを実行する方法は2つあります。
トピックについては私が作成した THIS ブログ記事を参照してください。