¿Cómo depuro los servicios de Windows en Visual Studio?

85

¿Es posible depurar los servicios de Windows en Visual Studio?

Usé código como

System.Diagnostics.Debugger.Break();

pero está dando algún error de código como:

Recibí dos errores de eventos: eventID 4096 VsJITDebugger y "El servicio no respondió a la solicitud de inicio o control de manera oportuna".

PawanS
fuente

Respuestas:

124

Utilice el siguiente código en el OnStartmétodo de servicio :

System.Diagnostics.Debugger.Launch();

Elija la opción de Visual Studio en el mensaje emergente.

Nota: Para usarlo solo en modo de depuración, #if DEBUGse puede usar una directiva de compilador, de la siguiente manera. Esto evitará la depuración accidental o en modo de lanzamiento en un servidor de producción.

#if DEBUG
    System.Diagnostics.Debugger.Launch();
#endif
Chirag
fuente
9
Recuerde ejecutar VS como administrador. Entonces estará disponible en la lista.
Michael
1
¿Alguien puede aclarar qué significa el mensaje emergente? ¿Cuándo / cómo aparece eso?
Mike
@Mike, instala e inicia el servicio, aparecerá.
Harshit
@ Mike, es un cuadro de diálogo de Windows que aparece en el escritorio interactivo (con sesión iniciada) y le pregunta si desea elegir una aplicación para depurar el proceso. Si elige VS, se iniciará el depurador y se adjuntará al proceso
Chris Johnson
63

También puedes probar esto.

  1. Cree su servicio de Windows e instálelo y comience…. Es decir, los servicios de Windows deben estar ejecutándose en su sistema.
  2. Mientras su servicio se está ejecutando, vaya al menú Depurar , haga clic en Adjuntar proceso (o proceso en el antiguo Visual Studio)
  3. Busque su servicio en ejecución y luego asegúrese de que esté seleccionado Mostrar proceso de todos los usuarios y Mostrar procesos en todas las sesiones ; si no, selecciónelo.

ingrese la descripción de la imagen aquí

  1. Haga clic en el botón Adjuntar
  2. Haga clic en Aceptar
  3. Haga clic en Cerrar
  4. Establezca un punto de interrupción en su ubicación deseada y espere a que se ejecute. Depurará automáticamente cada vez que su código llegue a ese punto.
  5. Recuerde, coloque su punto de interrupción en un lugar accesible , si está en Inicio (), luego detenga e inicie el servicio nuevamente

(Después de mucho buscar en Google, encontré esto en "Cómo depurar los servicios de Windows en Visual Studio").

PawanS
fuente
2
No puedo ver esta opción en VS2013 Update 5. :(
sandeep talabathula
1
Pero necesita ejecutar su Vs-2017 como administrador
chozha rajan
1
Intenté esto. Funcionó con un punto de interrupción en onStop pero no en onStart porque cuando el servicio se detiene, el depurador se
desacopla
22

Usted debe separar todo el código que va a hacer las cosas desde el proyecto de servicio en un proyecto separado, y luego hacer una aplicación de prueba que se puede ejecutar y depurar normalmente.

El proyecto de servicio sería solo el shell necesario para implementar la parte de servicio del mismo.

Lasse V. Karlsen
fuente
¡¡OOW !! ctrl + C luego ctrl + V, me refiero a un nuevo proyecto. Ya eso solo lo estoy haciendo yo. ¿No es posible adjuntar ningún proceso para depurar o cualquier otra opción en lugar de un proyecto separado?
PawanS
1
Por supuesto que es posible, pero es mucho más fácil desarrollar un servicio de Windows si quita la parte del servicio durante el desarrollo.
Lasse V. Karlsen
hmmm ... esa es una buena manera, pero simplemente duplica el trabajo. Pensé que existiría cualquier otra forma.
PawanS
9
No veo cómo "duplicaría" el trabajo. Claro, agregará un poco de sobrecarga al hacer el proyecto adicional y separar el código en el servicio a un tercer proyecto, pero aparte de eso, no haría una copia del código, lo movería, y hacer referencia a ese proyecto.
Lasse V. Karlsen
3
^ + 1. Es el doble de trabajo para administrar el servicio, que es prácticamente cero en términos de tiempo de desarrollo, y solo lo hace una vez. La depuración de un servicio es DOLOROSA, en lugar de hacerlo con un inicio dual como línea de comando. Consulte en Google las clases de envoltura predefinidas que permiten hacer esto (utilizan la reflexión para simular la clase de servicio de inicio / parada). una hora de trabajo, toneladas de ahorros, pérdida neta: negativo: gana tiempo.
TomTom
14

O eso, como lo sugirió Lasse V. Karlsen, o configure un bucle en su servicio que esperará a que se conecte un depurador. El mas simple es

while (!Debugger.IsAttached)
{
    Thread.Sleep(1000);
}

... continue with code

De esa manera puede iniciar el servicio y dentro de Visual Studio, elija "Adjuntar al proceso ..." y adjuntarlo a su servicio, que luego reanudará la ejecución normal.

Pauli Østerø
fuente
¿Dónde debo poner el código anterior ... y en el proceso de adjuntar
obtengo
3
también usamos código como este if (Environment.UserInteractive) { InteractiveRun(args); } else { Service instance = new Service(); ServiceBase[] servicesToRun = new ServiceBase[] { instance }; ServiceBase.Run(servicesToRun); }
Kirill Kovalenko
ese código debe ser lo antes posible antes de que se pueda ejecutar cualquier código que desee depurar.
Pauli Østerø
@Pawan: En Start/ OnStart()supongo
abatishchev
@Kirill: use tildes para resaltar el código dentro de los comentarios, por ejemplofoo(bar)
abatishchev
7

Dado que ServiceBase.OnStarttiene protectedvisibilidad, fui por la ruta de reflexión para lograr la depuración.

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

Tenga en cuenta que Thread, de forma predeterminada, es un hilo de primer plano. returning Mainmientras se ejecutan los subprocesos del servicio falso no terminará el proceso.

ta.speot.is
fuente
Dado que se supone que OnStart regresa rápidamente, no debería necesitar hacerlo en otro hilo. Sin embargo, si el servicio no inicia otro hilo, su proceso se cerrará inmediatamente.
Matt Connolly
@MattConnolly Sobre lo último: si es necesario, cambio el código anterior para iniciar un hilo en primer plano que duerme para siempre (antes del procesamiento normal).
ta.speot.is
Esta debería ser una respuesta real. ¡Funciona maravillosamente!
lentyai
4

Un artículo de Microsoft explica cómo depurar un servicio de Windows aquí y qué parte se puede perder si lo depura al adjuntarlo a un proceso.

A continuación se muestra mi código de trabajo. He seguido el enfoque sugerido por Microsoft.

Agregue este código a 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);
    }
}

Agregue este código a la clase ServiceMonitor.

internal void TestOnStartAndOnStop(string[] args)
{
    this.OnStart(args);
    Console.ReadLine();
    this.OnStop();
}

Ahora vaya a Propiedades del proyecto , seleccione la pestaña "Aplicación" y seleccione Tipo de salida como "Aplicación de consola" cuando realice la depuración, o "Aplicación de Windows" cuando haya terminado con la depuración, vuelva a compilar e instale su servicio.

Ingrese la descripción de la imagen aquí

Kumar Chandraketu
fuente
1
¿Hay alguna manera de configurar la salida para la aplicación de consola en la depuración y la aplicación de ventana en la versión?
kofifus
3

Puede crear una aplicación de consola. Yo uso esta mainfunción:

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

Mi ImportFileServiceclase es exactamente la misma que en la aplicación de mi servicio de Windows, excepto la herencia ( ServiceBase).

kerrubin
fuente
¿Está en el mismo proyecto o en otro proyecto para esta aplicación de consola?
PawanS
Son 2 proyectos diferentes con clases similares. En mi caso, es un servicio simple con solo la clase ImportFileService que está duplicada. Cuando quiero desarrollar / probar, uso consoleapp y luego copio / pego. Como dijo Lasse V. Karlsen, es un programa de depuración, toda la lógica (negocio) está en un tercer proyecto.
kerrubin
¿No está protegido OnStart?
Joe Phillips
Sí, es es. Es por eso que dije "excepto el heredero ( ServiceBase).". Me resulta más fácil depurar en una aplicación de consola, pero entiendo si eso no convence a todos.
kerrubin
3

Utilizo un gran paquete de Nuget llamado ServiceProcess.Helpers.

Y cito...

Ayuda a la depuración de los servicios de Windows mediante la creación de una interfaz de usuario de reproducción / parada / pausa cuando se ejecuta con un depurador adjunto, pero también permite que el entorno del servidor de Windows instale y ejecute el servicio.

Todo esto con una línea de código.

http://windowsservicehelper.codeplex.com/

Una vez instalado y conectado, todo lo que necesita hacer es configurar su proyecto de servicio de Windows como el proyecto de inicio y hacer clic en iniciar en su depurador.

Christophe Chang
fuente
¡Gracias por compartir! ¡Fue, con mucho, la solución más sencilla!
Wellington Zanelli
2

También puede probar el método System.Diagnostics.Debugger.Launch () . Ayuda a llevar el puntero del depurador a la ubicación especificada y luego puede depurar su código.

Antes de este paso , instale su service.exe usando la línea de comando del símbolo del sistema de Visual Studio - installutil projectservice.exe

Luego inicie su servicio desde el Panel de control -> Herramientas administrativas -> Administración de computadoras -> Servicio y aplicación -> Servicios -> Nombre de su servicio

Abhishek Srivastava
fuente
2

Acabo de agregar este código a mi clase de servicio para poder llamar indirectamente a OnStart, similar para OnStop.

    public void MyOnStart(string[] args)
    {
        OnStart(args);
    }
RichardHowells
fuente
2

Estoy usando el /Consoleparámetro en el proyecto de Visual Studio DepurarOpciones de inicioArgumentos de la línea de comando :

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() {....}
}
Sean M
fuente
2

Encontré esta pregunta, pero creo que falta una respuesta clara y simple.

No quiero adjuntar mi depurador a un proceso, pero aún quiero poder llamar al servicio OnStarty a los OnStopmétodos. También quiero que se ejecute como una aplicación de consola para poder registrar información de NLog en una consola.

Encontré estas guías brillantes que hacen esto:

Empiece por cambiar los proyectos Output typea Console Application.

Ingrese la descripción de la imagen aquí

Cambia tu Program.cspara que se vea así:

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

Luego agregue el siguiente método para permitir que los servicios se ejecuten en modo interactivo.

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);
}
Ogglas
fuente
¡Gran código! Sencillo, eficaz. +1. Pero igual de fácil hice de esta una aplicación de Formularios. Realmente odio las aplicaciones de consola. Además, puede implementar fácilmente un botón de formularios para cada evento de servicio.
Roland
1

Desafortunadamente, si está tratando de depurar algo al comienzo de una operación de servicio de Windows, "adjuntar" al proceso en ejecución no funcionará. Intenté usar Debugger.Break () dentro del procedimiento de OnStart, pero con una aplicación compilada de Visual Studio 2010 de 64 bits, el comando de interrupción arroja un error como este:

System error 1067 has occurred.

En ese momento, debe configurar una opción de "Ejecución de archivo de imagen" en su registro para su ejecutable. Se necesitan cinco minutos para configurarlo y funciona muy bien. Aquí hay un artículo de Microsoft donde están los detalles:

Cómo: iniciar el depurador automáticamente

Brian
fuente
1

Pruebe la propia línea de comandos de eventos posteriores a la compilación de Visual Studio .

Intente agregar esto en la post-construcción:

@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

Si el error de compilación con un mensaje como Error 1 The command "@echo off sc query "ServiceName" > nulasí sucesivamente, Ctrl+ CentoncesCtrl + Vel mensaje de error en el Bloc de notas y mira la última oración del mensaje.

Podría estar diciendo exited with code x. Busque el código en algún error común aquí y vea cómo resolverlo.

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.

Más sobre códigos de error aquí .

Y si el error de compilación con un mensaje como este,

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

abre cmd y luego intenta matarlo primero con taskkill /fi "services eq ServiceName" /f

Si todo va bien, F5debería bastar con depurarlo.

asakura89
fuente
0

En el OnStartmétodo, haga lo siguiente.

protected override void OnStart(string[] args)
{
    try
    {
        RequestAdditionalTime(600000);
        System.Diagnostics.Debugger.Launch(); // Put breakpoint here.

        .... Your code
    }
    catch (Exception ex)
    {
        .... Your exception code
    }
}

Luego, ejecute un símbolo del sistema como administrador e ingrese lo siguiente:

c:\> sc create test-xyzService binPath= <ProjectPath>\bin\debug\service.exe type= own start= demand

La línea anterior creará test-xyzService en la lista de servicios.

Para iniciar el servicio, esto le pedirá que se adjunte para debutar en Visual Studio o no.

c:\> sc start text-xyzService

Para detener el servicio:

c:\> sc stop test-xyzService

Para eliminar o desinstalar:

c:\> sc delete text-xyzService
usuario3942119
fuente
0

Depurar un servicio de Windows a través de http (probado con VS 2015 Update 3 y .Net FW 4.6)

En primer lugar, debe crear un proyecto de consola dentro de su solución VS (Agregar -> Nuevo proyecto -> Aplicación de consola).

Dentro del nuevo proyecto, cree una clase "ConsoleHost" con ese código:

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

Y este es el código para la clase 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();
    }
}

Las configuraciones como las cadenas de conexión deben copiarse en el archivo App.config del proyecto de la consola.

Para instalar la consola, haga clic con el botón derecho en el proyecto de la consola y haga clic en Depurar -> Iniciar nueva instancia.

mggSoft
fuente
0

Simplemente agregue un constructor a su clase de servicio (si aún no lo tiene). A continuación, puede consultar un ejemplo de visual basic .net.

Public Sub New()
   OnStart(Nothing) 
End Sub

Después de eso, haga clic derecho en el proyecto y seleccione " Depurar -> Iniciar una nueva instancia ".

Pedro Martín
fuente