[C#] 보다 쉬운 Windows 서비스 디버깅 방법

Windows 서비스 제어 관리자를 통해 서비스를 시작한 다음 디버거를 스레드에 연결하는 것보다 코드를 단계별로 쉽게 수행 할 수 있습니까? 그것은 번거롭고 더 직접적인 접근법이 있는지 궁금합니다.



답변

서비스를 빠르게 디버깅하려면 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(비 대화식) 서비스로 실행되는 것 외에 다른 이유가 있습니까?


답변

몇 주 전에 새로운 서비스 프로젝트를 시작했을 때이 게시물을 찾았습니다. 많은 훌륭한 제안이 있지만 여전히 원하는 해결책을 찾지 못했습니다 . 서비스 클래스를 수정하지 않고 서비스 클래스 OnStartOnStop메소드 를 호출 할 가능성 .

내가 Environment.Interactive제안한 솔루션은 이 게시물에 대한 다른 답변에서 제안한대로 선택 실행 모드 를 사용합니다 .

static void Main()
{
    ServiceBase[] servicesToRun;
    servicesToRun = new ServiceBase[]
    {
        new MyService()
    };
    if (Environment.UserInteractive)
    {
        RunInteractive(servicesToRun);
    }
    else
    {
        ServiceBase.Run(servicesToRun);
    }
}

RunInteractive도우미는 보호 호출 반사를 사용 OnStartOnStop방법 :

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);
}

이것은 필요한 모든 코드이지만 설명과 함께 연습 을 작성했습니다 .


답변

때로는 서비스를 시작하는 동안 무슨 일이 일어나고 있는지 분석하는 것이 중요합니다 . 서비스를 시작하는 동안 디버거를 연결하기에 충분히 빠르지 않기 때문에 프로세스에 연결해도 도움이되지 않습니다.

짧은 대답은 다음과 같은 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 7×64Visual Studio 2010 Team Edition에 대한 힌트 이지만 다른 환경에서도 유효합니다.


중요 : VS 명령 프롬프트 의 유틸리티를 사용하거나 준비한 서비스 설치 프로그램 프로젝트를 실행 하여 “수동”모드로 서비스를 배포하십시오 InstallUtil. 서비스를 시작 하기 전에 Visual Studio 열고 서비스 소스 코드가 포함 된 솔루션을로드하고 (Visual Studio에서 필요에 따라 추가 중단 점을 설정 한 후) 서비스 제어판을 통해 서비스를 시작하십시오 .

의 때문에 Debugger.Launch코드,이 대화 상자의 원인이됩니다 “는 마이크로 소프트 .NET 프레임 워크 예외에 발생 처리되지 않은 Servicename.exe .” 표시하는. 스크린 샷에 표시된대로 클릭하십시오 .Elevate Yes, debug Servicename.exe
FrameworkException

그 후 Windows 7 UAC에서 특히 관리자 자격 증명을 입력하라는 메시지가 표시 될 수 있습니다. 그것들을 입력하고 진행하십시오 Yes:

UAC 프롬프트

그런 다음 잘 알려진 Visual Studio Just-In-Time 디버거 창이 나타납니다. 선택한 디버거를 사용하여 디버깅 할 것인지 묻습니다. 를 클릭하기 전에 Yes, 당신이 것을 선택 새 인스턴스를 열고 싶지 않아 (2 옵션) – 소스 코드를 표시 할 수 없습니다 때문에 새로운 인스턴스가 여기에 도움이되지 것입니다. 따라서 앞에서 연 Visual Studio 인스턴스를 대신 선택하십시오.
VSDebugger 프롬프트

당신이 클릭 한 후 Yes, 어디에 후 Visual Studio를하는 동안 줄에 노란색 화살표를 오른쪽으로 표시됩니다 Debugger.Launch문이며 (방법 코드를 디버깅 할 수있는 MyInitOnStart귀하의 초기화를 포함).
VSDebugger 브레이크 포인트

F5준비한 다음 중단 점에 도달 할 때까지를 누르면 즉시 실행이 계속 됩니다.

힌트 : 서비스를 계속 실행하려면 디버그-> 모두 분리를 선택하십시오 . 이를 통해 올바르게 시작된 서비스와 통신하는 클라이언트를 실행할 수 있으며 시작 코드 디버깅이 완료됩니다. Shift+F5 (디버깅 중지) 를 누르면 서비스가 종료됩니다. 이를 수행하는 대신 서비스 제어판 을 사용하여 중지해야합니다.

참고

  • 릴리스 를 빌드 하면 디버그 코드가 자동으로 제거 되고 서비스가 정상적으로 실행됩니다.

  • 내가 사용하고 Debugger.Launch()있는 시작하고 디버거를 연결합니다 . 내가 테스트 한 Debugger.Break()어떤 잘으로 작업하지 않았다 더에 부착 디버거 아직 서비스의 시작이 없기 때문에,합니다 (원인 “오류 1067 : 프로세스가 예기치 않게 종료되었습니다.” ).

  • RequestAdditionalTime더 이상 설정 서비스의 시작을위한 타임 아웃을 (가되어 있지 코드 자체를 지연하지만, 즉시로 계속 Debugger.Launch문). 그렇지 않으면 서비스를 시작하기위한 기본 시간 초과가 너무 짧아 base.Onstart(args)디버거에서 빠르게 호출하지 않으면 서비스 시작에 실패합니다 . 실제로 10 분의 시간 초과 는 디버거가 시작된 직후에 “서비스가 응답하지 않습니다 …”라는 메시지 가 표시되지 않도록 합니다.

  • 일단 익숙해지면이 방법은 기존 서비스 코드 에 4 줄만 추가 하면되므로 제어 및 디버그를 빠르게 수행 할 수 있으므로 매우 쉽습니다 .


답변

내가 일반적으로하는 일은 서비스의 논리를 별도의 클래스로 캡슐화하고 ‘주자’클래스에서 시작하는 것입니다. 이 러너 클래스는 실제 서비스이거나 콘솔 응용 프로그램 일 수 있습니다. 따라서 솔루션에는 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