Visual StudioでWindowsサービスをデバッグすることはできますか?
私は次のようなコードを使用しました
System.Diagnostics.Debugger.Break();
しかし、それは次のようなコードエラーを与えています:
2つのイベントエラーが発生しました:eventID 4096 VsJITDebuggerと「サービスが開始要求または制御要求にタイムリーに応答しませんでした」。
これを試すこともできます。
(多くのグーグルの後、「Visual StudioでWindowsサービスをデバッグする方法」でこれを見つけました。)
Service OnStart
メソッドで次のコードを使用します。
System.Diagnostics.Debugger.Launch();
ポップアップメッセージからVisual Studioオプションを選択します。
注:デバッグモードでのみ使用するには、#if DEBUG
コンパイラ指令は、次のように使用できます。これにより、実稼働サーバーでのリリースモードでの偶発的なデバッグを防ぎます。
#if DEBUG
System.Diagnostics.Debugger.Launch();
#endif
doを行うすべてのコードをサービスプロジェクトから別のプロジェクトに分離してから、正常に実行およびデバッグできるテストアプリケーションを作成する必要があります。
サービスプロジェクトは、サービス部分を実装するために必要なシェルだけになります。
Lasse V. Karlsenが提案したとおり、またはデバッガーがアタッチするのを待つループをサービスに設定します。最も簡単なのは
while (!Debugger.IsAttached)
{
Thread.Sleep(1000);
}
... continue with code
この方法でサービスを開始し、Visual Studio内で[プロセスにアタッチ...]を選択してサービスにアタッチすると、通常の実行が再開されます。
とすれば 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
は、デフォルトではフォアグラウンドスレッドであることに注意してください。 faux-serviceスレッドの実行中にreturn
からMain
ingしてもプロセスは終了しません。
Microsoftの記事では、Windowsサービスのデバッグ方法 here と、プロセスにアタッチすることでデバッグした場合に誰が見逃すことができるかについて説明しています。
以下は私の作業コードです。私はマイクロソフトが提案したアプローチに従いました。
このコードを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サービスのアプリケーションとまったく同じです。
System.Diagnostics.Debugger.Launch()メソッドを試すこともできます。指定した場所へのデバッガーポインターを取得するのに役立ち、コードをデバッグできます。
この手順の前にservice.exeをインストールしてください Visual Studioコマンドプロンプトのコマンドラインを使用してください-installutil projectservice.exe
次に、コントロールパネル->管理ツール->コンピューターの管理->サービスとアプリケーション->サービス->サービス名からサービスを開始します。
ServiceProcess.Helpersと呼ばれる素晴らしいNugetパッケージを使用します。
引用します...
デバッガをアタッチして実行するときに再生/停止/一時停止UIを作成することにより、Windowsサービスのデバッグを支援しますが、Windowsサーバー環境でサービスをインストールして実行することもできます。
これらすべてを1行のコードで行います。
http://windowsservicehelper.codeplex.com/
インストールして配線するだけで、Windowsサービスプロジェクトをスタートアッププロジェクトとして設定し、デバッガで[開始]をクリックするだけです。
OnStopの場合と同様に、間接的にOnStartを呼び出すことができるように、このコードをサービスクラスに追加しました。
public void MyOnStart(string[] args)
{
OnStart(args);
}
Visual Studioプロジェクトで/Console
パラメーターを使用していますデバッグ→開始オプション→コマンドライン引数:
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() {....}
}
残念ながら、Windowsサービス操作の開始時に何かをデバッグしようとしている場合、実行中のプロセスへの「アタッチ」は機能しません。 OnStartプロシージャ内でDebugger.Break()を使用しようとしましたが、64ビットのVisual Studio 2010でコンパイルされたアプリケーションでは、breakコマンドは次のようなエラーをスローします。
System error 1067 has occurred.
その時点で、実行可能ファイルのレジストリに「イメージファイル実行」オプションを設定する必要があります。セットアップには5分かかり、非常にうまく機能します。詳細が記載されているMicrosoftの記事は次のとおりです。
私はこの質問を見つけましたが、明確で簡単な答えが欠けていると思います。
デバッガーをプロセスにアタッチしたくありませんが、それでもサービス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);
}
Visual Studio独自のビルド後イベントコマンドラインを試してください。
これをポストビルドに追加してみてください:
@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を作成します。
サービスを開始するには、Visual Studioでデビューするかどうかを確認するメッセージが表示されます。
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ファイルにコピーする必要があります。
コンソールを起動するには、コンソールプロジェクトを右クリックして、[デバッグ]-> [新しいインスタンスを開始]をクリックします。