Visual Studio에서 Windows 서비스를 디버깅 할 수 있습니까?
나는 다음과 같은 코드를 사용했다.
System.Diagnostics.Debugger.Break();
하지만 다음과 같은 코드 오류가 발생합니다.
두 가지 이벤트 오류가 발생했습니다. eventID 4096 VsJITDebugger 및 "서비스가 시작 또는 제어 요청에 적시에 응답하지 않았습니다."
답변:
서비스 OnStart
메소드 에서 다음 코드를 사용하십시오 .
System.Diagnostics.Debugger.Launch();
팝업 메시지에서 Visual Studio 옵션을 선택합니다.
참고 : 디버그 모드에서만 사용하려면 #if DEBUG
다음과 같이 컴파일러 지시문을 사용할 수 있습니다. 이렇게하면 프로덕션 서버의 릴리스 모드에서 우발적이거나 디버깅이 방지됩니다.
#if DEBUG
System.Diagnostics.Debugger.Launch();
#endif
이것을 시도 할 수도 있습니다.
(많은 인터넷 검색 끝에 "Visual Studio에서 Windows 서비스를 디버그하는 방법"에서이를 발견했습니다.)
서비스 프로젝트에서 작업을 수행 할 모든 코드를 별도의 프로젝트로 분리 한 다음 정상적으로 실행하고 디버그 할 수있는 테스트 애플리케이션을 만들어야합니다.
서비스 프로젝트는 서비스 부분을 구현하는 데 필요한 셸일뿐입니다.
Lasse V. Karlsen이 제안한대로 또는 디버거가 연결될 때까지 대기하는 서비스 루프를 설정합니다. 가장 간단한 것은
while (!Debugger.IsAttached)
{
Thread.Sleep(1000);
}
... continue with code
이렇게하면 서비스를 시작할 수 있으며 Visual Studio 내에서 "Attach to Process ..."를 선택하고 서비스에 연결하면 정상적인 배출이 재개됩니다.
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 서비스를 디버깅하는 방법 과 프로세스에 연결하여 디버깅하는 경우 놓칠 수있는 부분 을 설명합니다 .
아래는 내 작업 코드입니다. Microsoft에서 제안한 접근 방식을 따랐습니다.
이 코드를 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 서버 환경에서 서비스를 설치하고 실행할 수도 있습니다.
이 모든 것이 한 줄의 코드로.
http://windowsservicehelper.codeplex.com/
설치 및 연결이 완료되면 Windows 서비스 프로젝트를 시작 프로젝트로 설정하고 디버거에서 시작을 클릭하기 만하면됩니다.
System.Diagnostics.Debugger.Launch () 메서드를 사용해 볼 수도 있습니다 . 디버거 포인터를 지정된 위치로 가져가는 데 도움이되며 코드를 디버깅 할 수 있습니다.
이 단계 전에 Visual Studio 명령 프롬프트의 명령 줄 -installutil projectservice.exe를 사용 하여 service.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 문서입니다.
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 파일에 복사해야합니다.
콘솔을 시작하려면 콘솔 프로젝트를 마우스 오른쪽 버튼으로 클릭하고 디버그-> 새 인스턴스 시작을 클릭합니다.