답변:
서비스를 빠르게 디버깅하려면 Debugger.Break()
거기에 들어갑니다. 그 라인에 도달하면 VS로 돌아갑니다. 완료되면 해당 줄을 제거하는 것을 잊지 마십시오.
업데이트 :#if DEBUG
pragma 의 대안으로 Conditional("DEBUG_SERVICE")
attribute 를 사용할 수도 있습니다 .
[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
제안한 솔루션은 이 게시물에 대한 다른 답변에서 제안한대로 선택 실행 모드 를 사용합니다 .
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에 대한 힌트 이지만 다른 환경에서도 유효합니다.
중요 : VS 명령 프롬프트 의 유틸리티를 사용하거나 준비한 서비스 설치 프로그램 프로젝트를 실행 하여 "수동"모드로 서비스를 배포하십시오 InstallUtil
. 서비스를 시작 하기 전에 Visual Studio 를 열고 서비스 소스 코드가 포함 된 솔루션을로드하고 (Visual Studio에서 필요에 따라 추가 중단 점을 설정 한 후) 서비스 제어판을 통해 서비스를 시작하십시오 .
의 때문에 Debugger.Launch
코드,이 대화 상자의 원인이됩니다 "는 마이크로 소프트 .NET 프레임 워크 예외에 발생 처리되지 않은 Servicename.exe ." 표시하는. 스크린 샷에 표시된대로 클릭하십시오 . Yes, debug Servicename.exe
그 후 Windows 7 UAC에서 특히 관리자 자격 증명을 입력하라는 메시지가 표시 될 수 있습니다. 그것들을 입력하고 진행하십시오 Yes:
그런 다음 잘 알려진 Visual Studio Just-In-Time 디버거 창이 나타납니다. 선택한 디버거를 사용하여 디버깅 할 것인지 묻습니다. 를 클릭하기 전에 Yes, 당신이 것을 선택 새 인스턴스를 열고 싶지 않아 (2 옵션) - 소스 코드를 표시 할 수 없습니다 때문에 새로운 인스턴스가 여기에 도움이되지 것입니다. 따라서 앞에서 연 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)
호출하지 않으면 서비스 제어가 10 분 동안 서비스를 종료하지 못하게합니다 base.OnStart(args)
. 그 외에도, 잠시 후에 관리자 자격 증명을 입력하지 않으면 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 ()을 호출합니다. (이 클래스는 콘솔 앱에서도 쉽게 사용할 수 있습니다.)
이 디자인의 좋은 부작용은 서비스를 실제로 시작하기 전에 타이머를 설정하는 인수를 사용하여 지연시킬 수 있으므로 디버거를 수동으로 연결할 시간이 있다는 것입니다.
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
// 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 라이브러리를 사용하십시오 .
콘솔 응용 프로그램을 만든 다음 Main에서 설정을 구성하십시오.
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 서비스 관리자에서 서비스를 시작하고 중지 할 수 있습니다.
세션이 분리되어 Vista가 서비스에 연결하기가 훨씬 어렵습니다.
과거에 사용한 두 가지 옵션은 다음과 같습니다.
도움이 되었기를 바랍니다.
SCM의 프레임 워크 내에서 "콘솔"또는 "앱"모드없이 전체 서비스 동작으로 서비스를 실행하면서 OnStart ()의 초기화를 포함하여 서비스의 모든 측면을 디버깅 할 수 있기를 원합니다.
동일한 프로젝트에서 디버깅에 사용할 두 번째 서비스를 작성하여이를 수행합니다. 디버그 서비스는 평소처럼 시작될 때 (예 : 서비스 MMC 플러그인에서) 서비스 호스트 프로세스를 만듭니다. 실제 서비스를 아직 시작하지 않았더라도 디버거를 연결하는 프로세스를 제공합니다. 프로세스에 디버거를 연결 한 후 실제 서비스를 시작하면 OnStart ()를 포함하여 서비스 라이프 사이클의 어느 곳으로나 침입 할 수 있습니다.
최소한의 코드 침입이 필요하기 때문에 디버그 서비스를 서비스 설정 프로젝트에 쉽게 포함시킬 수 있으며 단일 코드 행을 주석 처리하고 단일 프로젝트 설치 프로그램을 삭제하여 프로덕션 릴리스에서 쉽게 제거 할 수 있습니다.
세부:
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을 호출하는 두 개의 "호스트"를 만듭니다. 하나는 Windows 서비스이고 다른 하나는 명령 줄 응용 프로그램입니다.
디버깅을 위해 명령 줄 응용 프로그램을 사용하고 명령 줄 응용 프로그램에서 재현 할 수없는 버그에 대해서만 디버거를 실제 서비스에 연결합니다.
이 접근법을 사용하면 실제 서비스에서 실행하는 동안 모든 코드를 테스트해야한다는 것을 기억하십시오. 명령 줄 도구는 다른 환경이므로 훌륭한 서비스이므로 실제 서비스처럼 정확하게 작동하지는 않습니다.
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 \ 이미지 파일 실행 옵션 \ servicename.exe] "GlobalFlag"= "0x00000000" "Debugger"= "vsjitdebugger.exe"
debugoff.reg :
Windows 레지스트리 편집기 버전 5.00 [HKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ Windows NT \ CurrentVersion \ 이미지 파일 실행 옵션 \ servicename.exe] "GlobalFlag"= "0x00000000"
일상적인 작은 물건 프로그래밍을 위해 서비스를 쉽게 디버깅하는 매우 간단한 트릭을 수행했습니다.
서비스를 시작할 때 명령 줄 매개 변수 "/ debug"를 확인합니다. 이 매개 변수를 사용하여 서비스를 호출하면 일반적인 서비스 시작을 수행하지 않고 모든 리스너를 시작하고 "디버그가 진행 중입니다. 확인을 눌러 종료하십시오"라는 메시지 상자 만 표시합니다.
따라서 내 서비스가 일반적인 방식으로 시작되면 서비스로 시작되고 명령 줄 매개 변수 / 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 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