VisualStudioでWindowsサービスをデバッグすることは可能ですか?
私は次のようなコードを使用しました
System.Diagnostics.Debugger.Break();
しかし、次のようなコードエラーが発生しています。
2つのイベントエラーが発生しました。eventID4096VsJITDebuggerと「サービスが開始要求または制御要求にタイムリーに応答しませんでした。」
回答:
サービスOnStart
メソッドで次のコードを使用します。
System.Diagnostics.Debugger.Launch();
ポップアップメッセージからVisualStudioオプションを選択します。
注:デバッグモードでのみ#if DEBUG
使用するには、次のようにコンパイラ指令を使用できます。これにより、運用サーバーでのリリースモードでの偶発的なデバッグやデバッグを防ぐことができます。
#if DEBUG
System.Diagnostics.Debugger.Launch();
#endif
これを試すこともできます。
(何度もグーグルした後、「VisualStudioでWindowsサービスをデバッグする方法」でこれを見つけました。)
サービスプロジェクトから処理を実行するすべてのコードを別のプロジェクトに分離してから、通常どおり実行およびデバッグできるテストアプリケーションを作成する必要があります。
サービスプロジェクトは、そのサービス部分を実装するために必要なシェルにすぎません。
Lasse V. Karlsenによって提案されたとおりにするか、デバッガーが接続するのを待機するループをサービスに設定します。最も単純なのは
while (!Debugger.IsAttached)
{
Thread.Sleep(1000);
}
... continue with code
このようにして、サービスを開始し、Visual Studio内で[プロセスにアタッチ...]を選択してサービスにアタッチすると、通常の実行が再開されます。
if (Environment.UserInteractive) { InteractiveRun(args); } else { Service instance = new Service(); ServiceBase[] servicesToRun = new ServiceBase[] { instance }; ServiceBase.Run(servicesToRun); }
Start
/OnStart()
私は推測する
foo(bar)
それServiceBase.OnStart
がprotected
可視性を持っているので、私はデバッグを達成するためにリフレクションルートをたどりました。
private static void Main(string[] args)
{
var serviceBases = new ServiceBase[] {new Service() /* ... */ };
#if DEBUG
if (Environment.UserInteractive)
{
const BindingFlags bindingFlags =
BindingFlags.Instance | BindingFlags.NonPublic;
foreach (var serviceBase in serviceBases)
{
var serviceType = serviceBase.GetType();
var methodInfo = serviceType.GetMethod("OnStart", bindingFlags);
new Thread(service => methodInfo.Invoke(service, new object[] {args})).Start(serviceBase);
}
return;
}
#endif
ServiceBase.Run(serviceBases);
}
Thread
デフォルトでは、フォアグラウンドスレッドであることに注意してください。フェイクサービススレッドの実行中にreturn
からMain
実行しても、プロセスは終了しません。
Microsoftの記事では、Windowsサービスをデバッグする方法についてここで説明していますでと、プロセスにアタッチしてデバッグした場合に誰もが見逃す可能性のある部分しています。
以下は私の作業コードです。私はマイクロソフトによって提案されたアプローチに従いました。
このコードをに追加しますprogram.cs
:
static void Main(string[] args)
{
// 'If' block will execute when launched through Visual Studio
if (Environment.UserInteractive)
{
ServiceMonitor serviceRequest = new ServiceMonitor();
serviceRequest.TestOnStartAndOnStop(args);
}
else // This block will execute when code is compiled as a Windows application
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new ServiceMonitor()
};
ServiceBase.Run(ServicesToRun);
}
}
このコードをServiceMonitorクラスに追加します。
internal void TestOnStartAndOnStop(string[] args)
{
this.OnStart(args);
Console.ReadLine();
this.OnStop();
}
次に、[プロジェクトのプロパティ]に移動し、[アプリケーション]タブを選択し、デバッグ時に[コンソールアプリケーション]として[出力タイプ]を選択し、デバッグが完了したら[Windowsアプリケーション]を選択して、サービスを再コンパイルしてインストールします。
コンソールアプリケーションを作成できます。私はこのmain
関数を使用します:
static void Main(string[] args)
{
ImportFileService ws = new ImportFileService();
ws.OnStart(args);
while (true)
{
ConsoleKeyInfo key = System.Console.ReadKey();
if (key.Key == ConsoleKey.Escape)
break;
}
ws.OnStop();
}
私のImportFileService
クラスは、継承(ServiceBase
)を除いて、Windowsサービスのアプリケーションとまったく同じです。
ServiceBase
)を除いて」と言ったのです。コンソールアプリでデバッグする方が簡単だと思いますが、それでもすべての人を納得させることができないかどうかは理解しています。
私はServiceProcess.Helpersと呼ばれる素晴らしいNugetパッケージを使用しています。
そして私は引用します...
デバッガーを接続して実行するときに再生/停止/一時停止UIを作成することにより、Windowsサービスのデバッグに役立ちますが、Windowsサーバー環境でサービスをインストールして実行することもできます。
これらすべてを1行のコードで実行できます。
http://windowsservicehelper.codeplex.com/
インストールして配線したら、Windowsサービスプロジェクトをスタートアッププロジェクトとして設定し、デバッガーで[開始]をクリックするだけです。
System.Diagnostics.Debugger.Launch()を試すこともできますメソッドをます。デバッガーポインタを指定された場所に移動するのに役立ち、コードをデバッグできます。
この手順の前に、VisualStudioコマンドプロンプトのコマンドラインを使用してservice.exeをインストールしてください--installutilprojectservice.exe
次に、[コントロールパネル]-> [管理ツール]-> [コンピューターの管理]-> [サービスとアプリケーション]-> [サービス]-> [サービス名]からサービスを開始します。
このコードをサービスクラスに追加したので、OnStopの場合と同様に、間接的にOnStartを呼び出すことができます。
public void MyOnStart(string[] args)
{
OnStart(args);
}
私が使用している/Console
Visual Studioのプロジェクトでパラメータをデバッグ→開始オプション→コマンドライン引数:
public static class Program
{
[STAThread]
public static void Main(string[] args)
{
var runMode = args.Contains(@"/Console")
? WindowsService.RunMode.Console
: WindowsService.RunMode.WindowsService;
new WinodwsService().Run(runMode);
}
}
public class WindowsService : ServiceBase
{
public enum RunMode
{
Console,
WindowsService
}
public void Run(RunMode runMode)
{
if (runMode.Equals(RunMode.Console))
{
this.StartService();
Console.WriteLine("Press <ENTER> to stop service...");
Console.ReadLine();
this.StopService();
Console.WriteLine("Press <ENTER> to exit.");
Console.ReadLine();
}
else if (runMode.Equals(RunMode.WindowsService))
{
ServiceBase.Run(new[] { this });
}
}
protected override void OnStart(string[] args)
{
StartService(args);
}
protected override void OnStop()
{
StopService();
}
/// <summary>
/// Logic to Start Service
/// Public accessibility for running as a console application in Visual Studio debugging experience
/// </summary>
public virtual void StartService(params string[] args){ ... }
/// <summary>
/// Logic to Stop Service
/// Public accessibility for running as a console application in Visual Studio debugging experience
/// </summary>
public virtual void StopService() {....}
}
この質問を見つけましたが、明確で単純な答えが欠けていると思います。
デバッガーをプロセスにアタッチしたくありませんが、サービスOnStart
とOnStop
メソッドを呼び出せるようにしたいのです。また、NLogから情報をログに記録できるように、コンソールアプリケーションとして実行したいからコンソールに。
私はこれを行うこれらの素晴らしいガイドを見つけました:
プロジェクトOutput type
をに変更することから始めConsole Application
ます。
次のように変更しますProgram.cs
。
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main()
{
// Startup as service.
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new Service1()
};
if (Environment.UserInteractive)
{
RunInteractive(ServicesToRun);
}
else
{
ServiceBase.Run(ServicesToRun);
}
}
}
次に、次のメソッドを追加して、サービスをインタラクティブモードで実行できるようにします。
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);
}
残念ながら、Windowsサービス操作の最初に何かをデバッグしようとすると、実行中のプロセスへの「アタッチ」は機能しません。OnStartプロシージャ内でDebugger.Break()を使用しようとしましたが、64ビットのVisual Studio 2010コンパイル済みアプリケーションでは、breakコマンドは次のようなエラーをスローします。
System error 1067 has occurred.
その時点で、実行可能ファイルのレジストリに「イメージファイルの実行」オプションを設定する必要があります。セットアップには5分かかり、非常にうまく機能します。詳細は次のMicrosoftの記事です。
VisualStudio独自のビルド後のイベントコマンドラインをお試しください。
ビルド後にこれを追加してみてください:
@echo off
sc query "ServiceName" > nul
if errorlevel 1060 goto install
goto stop
:delete
echo delete
sc delete "ServiceName" > nul
echo %errorlevel%
goto install
:install
echo install
sc create "ServiceName" displayname= "Service Display Name" binpath= "$(TargetPath)" start= auto > nul
echo %errorlevel%
goto start
:start
echo start
sc start "ServiceName" > nul
echo %errorlevel%
goto end
:stop
echo stop
sc stop "ServiceName" > nul
echo %errorlevel%
goto delete
:end
このようなメッセージでビルドエラーが発生した場合はError 1 The command "@echo off sc query "ServiceName" > nul
、Ctrl+C次にCtrl+Vエラーメッセージをメモ帳に入力して、メッセージの最後の文を確認します。
それは言っている可能性がありますexited with code x
。ここでいくつかの一般的なエラーのコードを探し、それを解決する方法を確認してください。
1072 -- Marked for deletion → Close all applications that maybe using the service including services.msc and Windows event log.
1058 -- Can't be started because disabled or has no enabled associated devices → just delete it.
1060 -- Doesn't exist → just delete it.
1062 -- Has not been started → just delete it.
1053 -- Didn't respond to start or control → see event log (if logged to event log). It may be the service itself throwing an exception.
1056 -- Service is already running → stop the service, and then delete.
エラーコードの詳細については、こちらをご覧ください。
そして、このようなメッセージでビルドエラーが発生した場合、
Error 11 Could not copy "obj\x86\Debug\ServiceName.exe" to "bin\Debug\ServiceName.exe". Exceeded retry count of 10. Failed. ServiceName
Error 12 Unable to copy file "obj\x86\Debug\ServiceName.exe" to "bin\Debug\ServiceName.exe". The process cannot access the file 'bin\Debug\ServiceName.exe' because it is being used by another process. ServiceName
cmdを開き、最初にそれを殺そうとします taskkill /fi "services eq ServiceName" /f
すべてが順調でF5あれば、デバッグするのに十分なはずです。
このOnStart
方法では、次のようにします。
protected override void OnStart(string[] args)
{
try
{
RequestAdditionalTime(600000);
System.Diagnostics.Debugger.Launch(); // Put breakpoint here.
.... Your code
}
catch (Exception ex)
{
.... Your exception code
}
}
次に、管理者としてコマンドプロンプトを実行し、次のように入力します。
c:\> sc create test-xyzService binPath= <ProjectPath>\bin\debug\service.exe type= own start= demand
上記の行は、サービスリストにtest-xyzServiceを作成します。
サービスを開始するには、VisualStudioでデビューするかどうかを確認するように求められます。
c:\> sc start text-xyzService
サービスを停止するには:
c:\> sc stop test-xyzService
削除またはアンインストールするには:
c:\> sc delete text-xyzService
まず、VSソリューション内にコンソールプロジェクトを作成する必要があります([追加]-> [新しいプロジェクト]-> [コンソールアプリケーション])。
新しいプロジェクト内で、次のコードを使用してクラス「ConsoleHost」を作成します。
class ConsoleHost : IDisposable
{
public static Uri BaseAddress = new Uri(http://localhost:8161/MyService/mex);
private ServiceHost host;
public void Start(Uri baseAddress)
{
if (host != null) return;
host = new ServiceHost(typeof(MyService), baseAddress ?? BaseAddress);
//binding
var binding = new BasicHttpBinding()
{
Name = "MyService",
MessageEncoding = WSMessageEncoding.Text,
TextEncoding = Encoding.UTF8,
MaxBufferPoolSize = 2147483647,
MaxBufferSize = 2147483647,
MaxReceivedMessageSize = 2147483647
};
host.Description.Endpoints.Clear();
host.AddServiceEndpoint(typeof(IMyService), binding, baseAddress ?? BaseAddress);
// Enable metadata publishing.
var smb = new ServiceMetadataBehavior
{
HttpGetEnabled = true,
MetadataExporter = { PolicyVersion = PolicyVersion.Policy15 },
};
host.Description.Behaviors.Add(smb);
var defaultBehaviour = host.Description.Behaviors.OfType<ServiceDebugBehavior>().FirstOrDefault();
if (defaultBehaviour != null)
{
defaultBehaviour.IncludeExceptionDetailInFaults = true;
}
host.Open();
}
public void Stop()
{
if (host == null)
return;
host.Close();
host = null;
}
public void Dispose()
{
this.Stop();
}
}
そして、これはProgram.csクラスのコードです。
public static class Program
{
[STAThread]
public static void Main(string[] args)
{
var baseAddress = new Uri(http://localhost:8161/MyService);
var host = new ConsoleHost();
host.Start(null);
Console.WriteLine("The service is ready at {0}", baseAddress);
Console.WriteLine("Press <Enter> to stop the service.");
Console.ReadLine();
host.Stop();
}
}
接続文字列などの構成は、コンソールプロジェクトのApp.configファイルにコピーする必要があります。
コンソールを起動するには、コンソールプロジェクトを右クリックし、[デバッグ]-> [新しいインスタンスの開始]をクリックします。
サービスクラスにコンストラクターを追加するだけです(まだ持っていない場合)。以下では、Visual Basic.netの確認と例を示します。
Public Sub New()
OnStart(Nothing)
End Sub
その後、プロジェクトを右クリックし、「デバッグ->新しいインスタンスの開始」を選択します。