Czy można debugować usługi systemu Windows w programie Visual Studio?
Użyłem kodu takiego jak
System.Diagnostics.Debugger.Break();
ale powoduje błąd w kodzie, taki jak:
Wystąpiły dwa błędy zdarzenia: eventID 4096 VsJITDebugger i „Usługa nie odpowiedziała na żądanie uruchomienia lub sterowania w odpowiednim czasie”.
Możesz także spróbować tego.
(Po wielu googlach znalazłem to w artykule „Jak debugować usługi Windows w programie Visual Studio”).
źródło
Powinieneś oddzielić cały kod, który będzie robił rzeczy z projektu usługi, do osobnego projektu, a następnie stworzyć aplikację testową, którą możesz normalnie uruchamiać i debugować.
Projekt usługi byłby tylko powłoką potrzebną do zaimplementowania części usługowej.
źródło
Albo zgodnie z sugestią Lasse V. Karlsena, albo skonfiguruj pętlę w usłudze, która będzie czekać na podłączenie debuggera. Najprostsze jest to
while (!Debugger.IsAttached) { Thread.Sleep(1000); } ... continue with code
W ten sposób możesz uruchomić usługę i w programie Visual Studio wybrać opcję „Dołącz do procesu ...” i dołączyć do usługi, która następnie wznowi normalne wykonywanie.
źródło
if (Environment.UserInteractive) { InteractiveRun(args); } else { Service instance = new Service(); ServiceBase[] servicesToRun = new ServiceBase[] { instance }; ServiceBase.Run(servicesToRun); }
Start
/OnStart()
Chybafoo(bar)
Biorąc pod uwagę, że
ServiceBase.OnStart
jest toprotected
widoczne, poszedłem na ścieżkę refleksji, aby uzyskać debugowanie.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); }
Zauważ, że
Thread
domyślnie jest to wątek pierwszego planu.return
ing odMain
natomiast gwinty faux-usługowe działają nie zakończy proces.źródło
Artykuł firmy Microsoft wyjaśnia, jak debugować usługę systemu Windows w tym miejscu i jaką część można przegapić, jeśli debuguje ją przez dołączenie do procesu.
Poniżej znajduje się mój działający kod. Postępowałem zgodnie z podejściem sugerowanym przez firmę Microsoft.
Dodaj ten kod do
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); } }
Dodaj ten kod do klasy ServiceMonitor.
internal void TestOnStartAndOnStop(string[] args) { this.OnStart(args); Console.ReadLine(); this.OnStop(); }
Teraz przejdź do Właściwości projektu , wybierz zakładkę „Aplikacja” i wybierz Typ wyjścia jako „Aplikacja konsolowa” podczas debugowania lub „Aplikacja Windows” po zakończeniu debugowania, przekompiluj i zainstaluj usługę.
źródło
Możesz stworzyć aplikację konsolową. Używam tej
main
funkcji: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(); }
Moja
ImportFileService
klasa jest dokładnie taka sama, jak w aplikacji mojej usługi Windows, z wyjątkiem inheritant (ServiceBase
).źródło
ServiceBase
)”. Debugowanie jest łatwiejsze w aplikacji konsolowej, ale rozumiem, czy to nie przekonuje wszystkich.Używam świetnego pakietu Nuget o nazwie ServiceProcess.Helpers.
I cytuję...
Ułatwia debugowanie usług systemu Windows, tworząc interfejs odtwarzania / zatrzymywania / wstrzymywania podczas pracy z podłączonym debugerem, ale także umożliwia zainstalowanie i uruchomienie usługi w środowisku serwera Windows.
Wszystko to za pomocą jednej linii kodu.
http://windowsservicehelper.codeplex.com/
Po zainstalowaniu i podłączeniu wszystko, co musisz zrobić, to ustawić projekt usługi systemu Windows jako projekt startowy i kliknąć przycisk Start w debugerze.
źródło
Możesz także wypróbować metodę System.Diagnostics.Debugger.Launch () . Pomaga w przeniesieniu wskaźnika debugera do określonej lokalizacji, a następnie można debugować kod.
Przed tym krokiem zainstaluj plik service.exe za pomocą wiersza poleceń wiersza poleceń programu Visual Studio - installutil projectservice.exe
Następnie uruchom usługę z Panelu sterowania -> Narzędzia administracyjne -> Zarządzanie komputerem -> Usługa i aplikacja -> Usługi -> Nazwa usługi
źródło
Właśnie dodałem ten kod do mojej klasy usług, więc mogłem pośrednio wywołać OnStart, podobnie jak w przypadku OnStop.
public void MyOnStart(string[] args) { OnStart(args); }
źródło
Używam
/Console
parametru w projekcie Visual Studio Debug → Start Options → Command line arguments :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() {....} }
źródło
Znalazłem to pytanie, ale myślę, że brakuje jasnej i prostej odpowiedzi.
Nie chcę dołączać debugera do procesu, ale nadal chcę mieć możliwość wywoływania usługi
OnStart
iOnStop
metod. Chcę również, aby działał jako aplikacja konsolowa, abym mógł logować informacje z NLog do konsoli.Znalazłem te genialne przewodniki, które to robią:
Debugowanie projektu usługi systemu Windows
Łatwiejszy sposób debugowania usługi systemu Windows
Zacznij od zmiany projektów
Output type
naConsole Application
.Zmień swój
Program.cs
wygląd, aby wyglądał tak: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); } } }
Następnie dodaj następującą metodę, aby zezwolić na działanie usług w trybie interaktywnym.
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); }
źródło
Niestety, jeśli próbujesz debugować coś na samym początku operacji usługi Windows, „dołączanie” do uruchomionego procesu nie zadziała. Próbowałem użyć Debugger.Break () w ramach procedury OnStart, ale w przypadku 64-bitowej, skompilowanej aplikacji Visual Studio 2010, polecenie break po prostu zgłasza następujący błąd:
System error 1067 has occurred.
W tym momencie musisz ustawić w rejestrze opcję „Wykonywanie pliku obrazu” dla pliku wykonywalnego. Konfiguracja zajmuje pięć minut i działa bardzo dobrze. Oto artykuł firmy Microsoft zawierający szczegóły:
Instrukcje: automatyczne uruchamianie debugera
źródło
Wypróbuj własny wiersz polecenia zdarzenia po kompilacji programu Visual Studio .
Spróbuj dodać to w post-build:
@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
W przypadku błędu kompilacji z takim komunikatem
Error 1 The command "@echo off sc query "ServiceName" > nul
, Ctrl+ Cnastępnie Ctrl+ Vkomunikat o błędzie do Notatnika i spójrz na ostatnie zdanie wiadomości.To mogłoby mówić
exited with code x
. Poszukaj kodu w typowym błędzie tutaj i zobacz, jak go rozwiązać.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.
Więcej o kodach błędów tutaj .
A jeśli błąd kompilacji z takim komunikatem,
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
otwórz cmd, a następnie spróbuj najpierw go zabić
taskkill /fi "services eq ServiceName" /f
Jeśli wszystko jest w porządku, F5powinno wystarczyć do debugowania.
źródło
W
OnStart
metodzie wykonaj następujące czynności.protected override void OnStart(string[] args) { try { RequestAdditionalTime(600000); System.Diagnostics.Debugger.Launch(); // Put breakpoint here. .... Your code } catch (Exception ex) { .... Your exception code } }
Następnie uruchom wiersz polecenia jako administrator i wpisz:
Powyższa linia utworzy test-xyzService na liście usług.
Aby uruchomić usługę, pojawi się monit o dołączenie do debiutu w programie Visual Studio, czy nie.
Aby zatrzymać usługę:
Aby usunąć lub odinstalować:
źródło
Debugowanie usługi systemu Windows za pośrednictwem protokołu HTTP (testowane z wersją VS 2015 Update 3 i .Net FW 4.6)
Po pierwsze, musisz utworzyć projekt konsoli w swoim rozwiązaniu VS (Dodaj -> Nowy projekt -> Aplikacja konsolowa).
W nowym projekcie utwórz klasę „ConsoleHost” z tym kodem:
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(); } }
A to jest kod klasy 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(); } }
Konfiguracje, takie jak connectionstrings, należy skopiować do pliku App.config projektu konsoli.
Aby uruchomić konsolę, kliknij prawym przyciskiem myszy projekt konsoli i kliknij Debuguj -> Uruchom nową instancję.
źródło
Po prostu dodaj konstruktora do swojej klasy usług (jeśli jeszcze go nie masz). Poniżej możesz sprawdzić przykład dla Visual Basic .net.
Public Sub New() OnStart(Nothing) End Sub
Następnie kliknij prawym przyciskiem myszy projekt i wybierz „ Debuguj -> Uruchom nową instancję ”.
źródło