Pergunta

Existe uma maneira mais fácil de percorrer o código do que para iniciar o serviço através do Service Control Manager do Windows e, em seguida, anexar o depurador para o segmento? É uma espécie de incómodo e eu estou querendo saber se existe uma abordagem mais simples.

Foi útil?

Solução

Se eu quiser depurar rapidamente o serviço, eu apenas uma gota em um Debugger.Break() lá. Quando essa linha é atingido, ele vai cair me de volta para VS. Não se esqueça de remover essa linha quando você está feito.

UPDATE:. Como uma alternativa para pragmas #if DEBUG, você também pode usar atributo Conditional("DEBUG_SERVICE")

[Conditional("DEBUG_SERVICE")]
private static void DebugMode()
{
    Debugger.Break();
}

Em seu OnStart, apenas chamar esse método:

public override void OnStart()
{
     DebugMode();
     /* ... do the rest */
}

Não, o código só será ativado durante a depuração compilações. Enquanto o seu no-lo, ele pode ser útil para criar uma configuração de compilação separada para depuração de serviços.

Outras dicas

Eu também acho que ter uma "versão" separado para a execução normal e como um serviço é o caminho a percorrer, mas é realmente necessário dedicar uma opção de linha de comando separada para esse fim?

Você não pode apenas fazer:

public static int Main(string[] args)
{
  if (!Environment.UserInteractive)
  {
    // Startup as service.
  }
  else
  {
    // Startup as application
  }
}

Isso teria o "benefício", que você pode simplesmente começar a sua aplicação via DoubleClick (OK, se você realmente precisa que) e que você pode apenas bater F5 no Visual Studio (sem a necessidade para modificar as configurações do projeto para incluir que a Opção /console).

Tecnicamente, as verificações Environment.UserInteractive se o WSF_VISIBLE sinalizador é definido para a estação janela atual, mas existe alguma outra razão que ele retornaria false, além de ser executado como um serviço (não interativo)?

Quando eu configurar um novo projeto de serviço de algumas semanas atrás eu encontrei este post. Embora existam muitas ótimas sugestões, eu ainda não encontrou a solução que eu queria:. A possibilidade de chamar métodos OnStart e OnStop as classes de serviço sem qualquer modificação para as classes de serviço

A solução que eu vim com usos do Environment.Interactive a seleccionar o modo de funcionamento, como sugerido por outras respostas para este post.

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

O auxiliar RunInteractive usa reflexão para chamar os métodos OnStart e OnStop protegidas:

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

Este é todo o código necessário, mas eu também escreveu passo a passo com explicações.

Às vezes é importante analisar o que está acontecendo durante o arranque do serviço. Anexar ao processo não ajuda aqui, porque você é o suficiente para não rápido para anexar o depurador enquanto o serviço é iniciando.

A resposta curta é, eu estou usando o seguinte 4 linhas de código para fazer isso:

#if DEBUG
    base.RequestAdditionalTime(600000); // 600*1000ms = 10 minutes timeout
    Debugger.Launch(); // launch and attach debugger
#endif

Estas são inseridas no método OnStart do serviço da seguinte forma:

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

Para aqueles que não têm feito isso antes, eu incluí dicas detalhadas abaixo , porque você pode facilmente ficar preso. As dicas a seguir referem-se a Windows 7x64 e Visual Studio 2010 Team Edition , mas deve ser válido para outros ambientes também.


Importante: Implantar o serviço em modo "manual" (usando o utilitário InstallUtil do comando VS alerta ou executar um projeto de instalação de serviços de ter preparado). Abra o Visual Studio antes você iniciar o serviço e carregar a solução que contém o código fonte do serviço - configurar pontos de interrupção adicionais como você obrigá-los no Visual Studio -, em seguida, iniciar o serviço através do Painel de Controle de Serviços.

Por causa do código Debugger.Launch, isso fará com que uma caixa de diálogo "Um não manipulado Microsoft .NET Framework exceção ocorreu no Servicename.exe ." aparecer. Clique em Elevate Sim, depuração Servicename.exe , como mostrado na imagem:
FrameworkException

Depois, escpecially no Windows 7 UAC pode pedir-lhe para introduzir credenciais de administrador. Inseri-los e prosseguir com Sim :

UACPrompt

Depois disso, o bem conhecido janela Visual Studio Just-In-Time Debugger aparece. Ele pergunta se você deseja depurar usando o depurador delected. Antes de clicar em Sim , selecione que você não quero abrir uma nova instância (2ª opção) - uma nova instância não seria útil aqui, porque o código fonte não seria exibido. Então você selecione a instância Visual Studio que você abriu mais cedo em vez disso: VSDebuggerPrompt

Depois de ter clicado Sim , depois de um tempo Visual Studio irá mostrar a direita seta amarela na linha onde a declaração Debugger.Launch é e você é capaz de depurar o seu código (MyInitOnStart método, que contém a sua inicialização). VSDebuggerBreakpoint

Pressionando F5 continua a execução imediatamente, até o próximo ponto de interrupção que você preparou seja alcançado.

Dica: Para manter o serviço em execução, selecione Debug -> Separar todos . Isso permite que você executar um cliente se comunicar com o serviço depois de ter começado corretamente e terminar a depuração do código de inicialização. Se você pressionar Mudança + F5 (paragem de depuração), isso irá terminar o serviço. Em vez de fazer isso, você deve usar o Painel de controle de serviços para pará-lo.

Nota que

  • Se você construir um Release, seguida, o código de depuração é automaticamente removido e o serviço é executado normalmente.

  • Eu estou usando Debugger.Launch() , que inicia e anexa um depurador . Eu testei Debugger.Break() , assim, que não funcionou , porque não há nenhum depurador anexado no arranque do serviço ainda (fazendo com que o "Erro 1067: O processo foi encerrado inesperadamente." )

  • .
  • RequestAdditionalTime define um longo tempo limite para o arranque do serviço não atrasando o código em si, mas logo vai continuar com a declaração Debugger.Launch). Caso contrário, o tempo limite padrão para iniciar o serviço é muito curta e iniciando o serviço falha se você não chamar base.Onstart(args) rapidez suficiente do depurador. Praticamente, um tempo limite de 10 minutos Evita que você vê a mensagem " o serviço não respondeu ..." imediatamente após o depurador é iniciado.

  • Uma vez que você se acostumar com isso, este método é muito fácil, pois exige apenas que você adicionar 4 linhas a um código de serviço existente, permitindo que você rapidamente para controle de ganho e de depuração.

O que eu costumo fazer é encapsular a lógica do serviço em uma classe separada e começar que a partir de uma classe 'runner'. Esta classe corredor pode ser o serviço real ou apenas um aplicativo de console. Portanto, sua solução tem (atleast) 3 projetos:

/ConsoleRunner
   /....
/ServiceRunner
   /....
/ApplicationLogic
   /....

Este YouTube vídeo por Fabio Scopel explica como depurar um serviço do Windows muito bem ... o método real de fazê-lo começa às 4:45 no vídeo ...

Aqui está o código explicado no vídeo ... em seu arquivo Program.cs, adicione o material para a seção de depuração ...

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

        }
    }
}

Em seu arquivo Service1.cs, adicione o método OnDebug () ...

    public Service1()
    {
        InitializeComponent();
    }

    public void OnDebug()
    {
        OnStart(null);
    }

    protected override void OnStart(string[] args)
    {
        // your code to do something
    }

    protected override void OnStop()
    {
    }

Como funciona

Basicamente, você tem que criar um public void OnDebug() que chama a OnStart(string[] args) como é protegida e não acessível fora. O programa void Main() é adicionado com pré-processador #if com #DEBUG.

Visual Studio define DEBUG se o projeto é compilado em Debug mode.This permitirá a secção de depuração (abaixo) a ser executado quando a condição é verdadeira

Service1 myService = new Service1();
myService.OnDebug();
System.Threading.Thread.Sleep(System.Threading.Timeout.Infinite);

E ele será executado como um aplicativo de console, uma vez que as coisas vão OK você pode alterar o modo de Release ea seção else regular, irá desencadear a lógica

Atualizar

Esta abordagem é de longe o mais fácil:

http://www.codeproject.com/KB/dotnet/DebugWinServices.aspx

deixo a minha resposta original abaixo para a posteridade.


Os meus serviços tendem a ter uma classe que encapsula um temporizador como eu quero o serviço para verificar em intervalos regulares se há algum trabalho para fazer.

O Hotel New até a classe e chamar StartEventLoop () durante o serviço start-up. (Essa classe poderia facilmente ser usado a partir de um console app também.)

O efeito colateral agradável deste projeto é que os argumentos com que você configurar o temporizador pode ser usado para ter um atraso antes que o serviço realmente começa a trabalhar, de modo que você tenha tempo para anexar um depurador manualmente.

P.S. Como anexar o depurador manualmente a um processo em execução ...?

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

Também eu costumava fazer o seguinte (já mencionado nas respostas anteriores, mas com o compilador condicional [#if] bandeiras para ajudar a evitar que disparando em uma compilação de lançamento).

Eu parei de fazê-lo desta maneira, porque às vezes nós esquecemos de construir em lançamento e ter uma pausa depurador em um aplicativo em execução em uma demonstração do cliente (embaraçoso!).

#if DEBUG
if (!System.Diagnostics.Debugger.IsAttached)
{
    System.Diagnostics.Debugger.Break();
}
#endif

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
}

Você também pode iniciar o serviço através do prompt de comando (sc.exe).

Pessoalmente, eu executar o código como um programa stand-alone na fase de depuração, e quando a maioria dos erros são eliminados, mude para executar como serviço.

O que eu costumava fazer era ter uma opção de linha de comando que inicia o programa seja como um serviço ou como uma aplicação regular. Então, na minha IDE gostaria de definir o interruptor para que eu pudesse percorrer o meu código.

Com algumas línguas você pode realmente detectar se ele está correndo em uma IDE, e realizar este interruptor automaticamente.

Que linguagem você está usando?

Use a TopShelf biblioteca.

Criar um aplicativo de console, em seguida, configuração configure em sua principal

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()
        {

        }
    }

Para depurar seu serviço, é só apertar F5 no estúdio visual.

Para instalar o serviço, digite cmd "Console.exe instalar"

Você pode, então, iniciar e parar serviço no gerenciador de serviços do Windows.

Eu acho que depende do que OS você está usando, o Vista é muito mais difícil para anexar a Serviços, por causa da separação entre as sessões.

As duas opções que eu usei no passado são:

  • Use GFlags (nas ferramentas de depuração para Windows) para configurar um depurador permanente para um processo. Isso existe na chave de registo "Execução do Arquivo de Imagem Opções" e é incrivelmente útil. Acho que você vai precisar de ajustar as configurações de serviço para permitir "Interagir com Desktop". Eu uso isso para todos os tipos de depuração, e não apenas os serviços.
  • A outra opção, é separar o código um pouco, de modo que a parte serviço é interchangable com uma inicialização do aplicativo normal. Dessa forma, você pode usar uma bandeira linha de comando simples, e lançamento como um processo (em vez da Service), o que torna muito mais fácil de depurar.

Espero que isso ajude.

Quando eu escrever um serviço que eu colocar toda a lógica de serviço em um projeto de dll e criar dois "exércitos" que a chamada para essa dll, é um serviço do Windows e o outro é um aplicativo de linha de comando.

Eu uso o aplicativo de linha de comando para depuração e anexar o depurador para o serviço real apenas para bugs não posso reproduzir no aplicativo de linha de comando.

Eu você usar essa abordagem, basta lembrar que você tem que testar todo o código durante a execução em um serviço real, enquanto a ferramenta de linha de comando é uma ajuda de depuração agradável é um ambiente diferente e não se comporta exatamente como um verdadeiro serviço .

Eu gosto de ser capaz de depurar todos os aspectos do meu serviço, incluindo qualquer inicialização em OnStart (), enquanto ainda executá-lo com o comportamento de serviço completo no âmbito do SCM ... não "console" ou no modo "app" .

Eu faço isso através da criação de um segundo serviço, no mesmo projeto, a utilização para depuração. O serviço de depuração, quando começou como de costume (ou seja, no plugin serviços MMC), cria o processo de host de serviço. Isto dá-lhe um processo para anexar o depurador ao mesmo que você ainda não começou seu serviço real ainda. Depois de anexar o depurador para o processo, inicie o serviço real e você pode entrar em qualquer lugar do ciclo de vida do serviço, incluindo OnStart ().

Porque exige muito mínimo de intrusão de código, o serviço de depuração pode ser facilmente incluído no seu projeto de instalação de serviço, e é facilmente removido da sua versão de produção comentando uma única linha de código e apagar um único instalador do projeto.

Detalhes:

1) Supondo que você está implementando MyService, também criam MyServiceDebug. Adicionar tanto para a matriz ServiceBase em Program.cs assim:

    /// <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) Adicionar o serviço real e o serviço de depuração para o instalador do projeto para o projeto de serviço:

enter descrição da imagem aqui

Ambos os serviços (real e depuração) são incluídos quando você adiciona a saída do projeto de serviço para o projeto de instalação para o serviço. Após a instalação, os dois serviços aparecerá no plugin service.msc MMC.

3) Inicie o serviço de depuração no MMC.

4) No Visual Studio, anexar o depurador ao processo iniciado pelo serviço de depuração.

5) Inicie o serviço real e desfrutar de depuração.

Quando desenvolver e depurar um serviço do Windows que normalmente executá-lo como um aplicativo de console, adicionando um parâmetro de arranque / console e verificar isso. Torna a vida muito mais fácil.

static void Main(string[] args) {
    if (Console.In != StreamReader.Null) {
        if (args.Length > 0 && args[0] == "/console") {
            // Start your service work.
        }
    }
}

Como cerca Debugger.Break () na primeira linha?

Para depurar serviços do Windows eu combino GFlags e um arquivo .reg criado por regedit.

  1. Executar GFlags, especificando o nome exe e vsjitdebugger
  2. Executar regedit e vá para o local onde GFlags define suas opções
  3. Escolha "Exportar chave" do menu arquivo
  4. Save que em algum lugar do arquivo com a extensão .reg
  5. Sempre que você quiser depurar o serviço: DoubleClick sobre o arquivo .reg
  6. Se você quer parar de depuração, clique duas vezes no segundo arquivo .reg

ou salvar os seguintes trechos e substituir servicename.exe com o nome do executável desejado.


debugon.reg:

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\servicename.exe]
"GlobalFlag"="0x00000000"
"Debugger"="vsjitdebugger.exe"

debugoff.reg:

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\servicename.exe]
"GlobalFlag"="0x00000000"

Para pequenas coisas rotina de programação eu fiz um truque muito simples para facilmente depurar o meu serviço:

No início do serviço, eu verificar se há um parâmetro de linha de comando "/ debug". Se o serviço é chamado com este parâmetro, eu não faço a inicialização do serviço de costume, mas em vez disso começar a todos os ouvintes e apenas exibir um messagebox "Debug em andamento, pressione OK para fim".

Então, se o meu serviço é iniciado da maneira usual, ele vai começar como serviço, se for iniciado com o parâmetro de linha de comando / debug ele vai agir como um programa normal.

Em VS eu vou apenas adicionar / debug como depuração de parâmetros e iniciar o programa de serviço diretamente.

Desta forma eu posso facilmente depuração para a maioria dos pequenos problemas tipo. Claro, algumas coisas ainda precisam ser depurado como serviço, mas para 99% isso é bom o suficiente.

#if DEBUG
    System.Diagnostics.Debugger.Break();
#endif

Eu uso uma variação sobre a resposta de JOP. Usando parâmetros de linha de comando que você pode definir o modo de depuração no IDE com propriedades do projeto ou através do gerente de serviços do Windows.

protected override void OnStart(string[] args)
{
  if (args.Contains<string>("DEBUG_SERVICE"))
  {
    Debugger.Break();
  }
  ...
}

Para a resolução de problemas no programa de serviço do Windows existente, use 'Debugger.Break ()' como outros caras sugeriu.

Para o novo programa de serviço do Windows, gostaria de sugerir utilizando o método de James Michael Hare http://geekswithblogs.net/BlackRabbitCoder/archive/2011/03/01/c-toolbox-debug-able-self-installable-windows- serviço-template-redux.aspx

Basta colocar o depurador almoço em qualquer lugar e anexar VisualStudio na inicialização

#if DEBUG
    Debugger.Launch();
#endif

Além disso, você precisa para começar a VS como Administatrator e você precisa permitir que um processo pode automaticamente ser depurado por um usuário diferente e foi decorado (como explicado aqui ):

reg add "HKCR\AppID{E62A7A31-6025-408E-87F6-81AEB0DC9347}" /v AppIDFlags /t REG_DWORD /d 8 /f

Use Windows Service Template C # projeto para criar um novo serviço de aplicativo https://github.com/ HarpyWar / windows-service-modelo

Há modo console / serviço detectada automaticamente, instalador auto / deinstaller do seu serviço e vários recursos mais usados ??estão incluídos.

Aqui é o método simples que eu usei para testar o serviço, sem quaisquer métodos adicionais "debug" e com testes de unidade VS integrados.

[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
    }
}

Você tem duas opções para fazer a depuração.

  1. criar um arquivo de log: Pessoalmente, prefiro um arquivo de log separado, como arquivo de texto em vez utilizando o registo da aplicação ou evento log.But isso vai custar-lhe muito em nome de tempo, porque o seu ainda difícil descobrir o nosso, onde o erro exato localização é
  2. Converter a aplicação para aplicação de consola:. Isso permitirá que você, todas as ferramentas de depuração que podemos usar em VS

Por favor, consulte este blog que eu criei para o tópico.

Basta colar

Debugger.Break();

em qualquer lugar que você código.

Por exemplo,

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

Ele vai bater Debugger.Break(); quando você executar o programa.

A melhor opção é usar o ' System.Diagnostics ' namespace.

Coloque seu código em bloco if mais para modo de depuração e modo de liberação, como mostrado abaixo para alternar entre depuração e modo de liberação no estúdio visual,

#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
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top