Como faço para depurar serviços do Windows no Visual Studio?

85

É possível depurar os serviços do Windows no Visual Studio?

Eu usei código como

System.Diagnostics.Debugger.Break();

mas está apresentando algum erro de código como:

Recebi dois erros de evento: eventID 4096 VsJITDebugger e "O serviço não respondeu à solicitação de início ou controle em tempo hábil."

PawanS
fonte

Respostas:

124

Use o seguinte código no OnStartmétodo de serviço :

System.Diagnostics.Debugger.Launch();

Escolha a opção Visual Studio na mensagem pop-up.

Observação: para usá-lo apenas no modo de depuração, uma #if DEBUGdiretiva do compilador pode ser usada, conforme a seguir. Isso impedirá acidental ou depuração no modo de lançamento em um servidor de produção.

#if DEBUG
    System.Diagnostics.Debugger.Launch();
#endif
Chirag
fonte
9
Lembre-se de executar o VS como Administrador. Em seguida, ele estará disponível na lista.
Michael
1
Alguém pode esclarecer o que significa mensagem pop-up? Quando / como isso aparece?
Mike
@Mike, instale e inicie o serviço, ele aparecerá.
Harshit
@Mike, é uma caixa de diálogo do Windows que aparece na área de trabalho interativa (conectada) e pergunta se você deseja escolher um aplicativo para depurar o processo. Se você escolher o VS, ele iniciará o depurador e será anexado ao processo
Chris Johnson
63

Você também pode tentar isso.

  1. Crie o seu serviço do Windows, instale e inicie…. Ou seja, os serviços do Windows devem estar em execução no seu sistema.
  2. Enquanto seu serviço estiver em execução, vá para o menu Debug , clique em Attach Process (ou processo no antigo Visual Studio)
  3. Encontre o serviço em execução e certifique-se de que Mostrar processo de todos os usuários e Mostrar processos em todas as sessões esteja selecionado; caso contrário, selecione-o.

insira a descrição da imagem aqui

  1. Clique no botão Anexar
  2. Clique OK
  3. Clique em Fechar
  4. Defina um ponto de interrupção no local desejado e aguarde a execução. Ele será depurado automaticamente sempre que seu código chegar a esse ponto.
  5. Lembre-se, coloque seu ponto de interrupção em um local acessível , se for onStart (), pare e inicie o serviço novamente

(Depois de muito pesquisar no Google, encontrei isso em "Como depurar os serviços do Windows no Visual Studio".)

PawanS
fonte
2
Não consigo ver esta opção na atualização 5 do VS2013. :(
sandeep talabathula
1
Mas você precisa executar seu Vs-2017 como administrador
chozha rajan
1
Eu tentei isso. Funcionou com um ponto de interrupção em onStop, mas não em onStart porque quando o serviço para, o depurador é desconectado
KansaiRobot
22

Você deve separar todo o código que fará as coisas do projeto de serviço em um projeto separado e, em seguida, fazer um aplicativo de teste que possa executar e depurar normalmente.

O projeto de serviço seria apenas o shell necessário para implementar sua parte de serviço.

Lasse V. Karlsen
fonte
OOW !! ctrl + C então ctrl + V, quero dizer, para um novo projeto. Sim, isso só eu estou fazendo. Não é possível anexar qualquer processo para depurar ou qualquer outra opção em vez de separar o projeto.?
PawanS
1
Claro que é possível, mas é muito mais fácil desenvolver um serviço do Windows se você retirar a parte do serviço durante o desenvolvimento.
Lasse V. Karlsen
hmmm ... esse é um bom caminho, mas simplesmente duplica o trabalho. Achei que existisse qualquer outra forma.
PawanS
9
Não vejo como isso "dobraria" o trabalho. Claro, isso vai adicionar um pouco de sobrecarga ao fazer o projeto extra e separar o código no serviço para um terceiro projeto, mas fora isso, você não faria uma cópia do código, você o moveria, e fazer referência a esse projeto.
Lasse V. Karlsen
3
^ + 1. É o dobro do trabalho de gerenciamento do serviço, que é praticamente zero em termos de tempo de desenvolvimento, e você só faz uma vez. Depurar um seriço é DOLOROSO - em vez disso, inicie-o duplamente como linha de comando. Verifique no google as classes de wrapper predefinidas que permitem fazer isso (elas usam reflexão para simular o start / stop no service class). uma hora de trabalho, toneladas de economia, perda líquida: negativo - você ganha tempo.
TomTom
14

Ou isso, conforme sugerido por Lasse V. Karlsen, ou configure um loop em seu serviço que irá aguardar a conexão de um depurador. O mais simples é

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

... continue with code

Dessa forma, você pode iniciar o serviço e dentro do Visual Studio escolher "Anexar ao Processo ..." e anexar ao seu serviço que então irá retomar a execução normal.

Pauli Østerø
fonte
onde devo colocar o código acima ... e no processo de anexação estou recebendo meu serviço chamado disable
PawanS
3
nós também 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
esse código deve ser o mais cedo possível antes que qualquer código que você deseja depurar possa ser executado.
Pauli Østerø
@Pawan: Em Start / OnStart()eu acho
abatishchev
@Kirill: use tis para destacar o código nos comentários, por exemplofoo(bar)
abatishchev
7

Dado que ServiceBase.OnStarttem protectedvisibilidade, desci a rota de reflexão para conseguir a depuração.

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

Observe que Threadé, por padrão, um thread de primeiro plano. returning da Mainenquanto os fios de serviço faux estão em execução não vai terminar o processo.

ta.speot.is
fonte
Como OnStart deve retornar rapidamente, você não deve precisar fazer isso em outro thread. No entanto, se o serviço não iniciar outro thread, seu processo será encerrado imediatamente.
Matt Connolly
@MattConnolly Sobre o último: se necessário, altero o código acima para iniciar um thread de primeiro plano que dorme para sempre (antes do processamento normal).
ta.speot.is
Esta deve ser uma resposta real. Funciona lindamente!
lentyai
4

Um artigo da Microsoft explica como depurar um serviço do Windows aqui e que parte alguém pode perder se o depurar anexando a um processo.

Abaixo está meu código de trabalho. Eu segui a abordagem sugerida pela Microsoft.

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

Adicione este código à classe ServiceMonitor.

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

Agora vá para Propriedades do projeto , selecione a guia "Aplicativo" e selecione Tipo de saída como "Aplicativo de console" ao depurar, ou "Aplicativo do Windows" ao concluir a depuração, recompile e instale seu serviço.

Insira a descrição da imagem aqui

Kumar Chandraketu
fonte
1
existe uma maneira de definir a saída para o aplicativo de console em depurar e o aplicativo de janela na versão?
kofifus
3

Você pode fazer um aplicativo de console. Eu uso esta mainfunção:

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

Minha ImportFileServiceclasse é exatamente a mesma do meu aplicativo de serviço do Windows, exceto o inheritant ( ServiceBase).

Kerrubin
fonte
é no mesmo projeto ou em outro projeto para este aplicativo de console
PawanS
São 2 projetos diferentes com classes semelhantes. No meu caso, é um serviço simples com apenas as classes ImportFileService que estão duplicadas. Quando eu quero desenvolver / testar, eu uso consoleapp e copio / colo. Como Lasse V. Karlsen disse, é um programa de depuração, toda a lógica (negócios) está em um terceiro projeto.
kerrubin
Sim, é. É por isso que eu disse "exceto o herdeiro ( ServiceBase).". Acho mais fácil depurar em um aplicativo de console, mas entendo se isso não convence a todos.
kerrubin
3

Eu uso um ótimo pacote Nuget chamado ServiceProcess.Helpers.

E eu cito ...

Ele ajuda na depuração de serviços do Windows criando uma interface de usuário para reproduzir / parar / pausar ao executar com um depurador conectado, mas também permite que o serviço seja instalado e executado pelo ambiente do servidor Windows.

Tudo isso com uma linha de código.

http://windowsservicehelper.codeplex.com/

Depois de instalado e conectado, tudo o que você precisa fazer é definir seu projeto de serviço do Windows como o projeto de inicialização e clicar em iniciar em seu depurador.

Christophe Chang
fonte
Obrigado por compartilhar! Foi de longe a solução mais simples!
Wellington Zanelli
2

Você também pode tentar System.Diagnostics.Debugger.Launch () método . Isso ajuda a levar o ponteiro do depurador para o local especificado e você pode depurar o código.

Antes desta etapa , instale seu service.exe usando a linha de comando do prompt de comando do Visual Studio - installutil projectservice.exe

Em seguida, inicie seu serviço no Painel de Controle -> Ferramentas Administrativas -> Gerenciamento do Computador -> Serviço e Aplicativo -> Serviços -> Seu Nome de Serviço

Abhishek Srivastava
fonte
2

Acabei de adicionar esse código à minha classe de serviço para que pudesse chamar indiretamente OnStart, semelhante ao OnStop.

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

Estou usando o /Consoleparâmetro no projeto Visual Studio DebugIniciar OpçõesArgumentos da linha 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
fonte
2

Encontrei esta pergunta, mas acho que falta uma resposta clara e simples.

Não quero anexar meu depurador a um processo, mas ainda quero poder chamar o serviço OnStarte os OnStopmétodos. Eu também quero que ele seja executado como um aplicativo de console para que eu possa registrar informações do NLog em um console.

Eu encontrei estes guias brilhantes que fazem isso:

Comece alterando os projetos Output typepara Console Application.

Insira a descrição da imagem aqui

Mude seu Program.cspara ficar assim:

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

Em seguida, adicione o método a seguir para permitir que os serviços sejam executados no modo interativo.

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
fonte
Ótimo código! Simples, eficaz. +1. Mas tão fácil quanto eu tornei este um aplicativo de formulários. Eu realmente odeio aplicativos de console. Além disso, você pode implementar facilmente um Botão de Formulários para cada evento de serviço.
Roland
1

Infelizmente, se você estiver tentando depurar algo bem no início de uma operação de serviço do Windows, "anexar" ao processo em execução não funcionará. Tentei usar Debugger.Break () no procedimento OnStart, mas com um aplicativo compilado do Visual Studio 2010 de 64 bits, o comando break apenas gera um erro como este:

System error 1067 has occurred.

Nesse ponto, você precisa configurar uma opção "Execução de arquivo de imagem" no registro para o executável. Leva cinco minutos para configurar e funciona muito bem. Aqui está um artigo da Microsoft onde os detalhes são:

Como: iniciar o depurador automaticamente

Brian
fonte
1

Experimente a linha de comando de pós-construção do próprio Visual Studio .

Tente adicionar isso na pós-construção:

@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

Se o erro de compilação com uma mensagem como esta Error 1 The command "@echo off sc query "ServiceName" > nulpor diante, Ctrl+ Centão Ctrl+ Va mensagem de erro no Bloco de Notas e observe a última frase da mensagem.

Pode ser dizer exited with code x. Procure o código de algum erro comum aqui e veja como resolvê-lo.

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.

Mais sobre códigos de erro aqui .

E se o erro de compilação com mensagem como esta,

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

abra o cmd e tente matá-lo primeiro com taskkill /fi "services eq ServiceName" /f

Se tudo estiver bem, F5deve ser suficiente para depurá-lo.

asakura89
fonte
0

No OnStartmétodo, faça o seguinte.

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

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

Em seguida, execute um prompt de comando como administrador e insira o seguinte:

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

A linha acima criará test-xyzService na lista de serviços.

Para iniciar o serviço, isso solicitará que você anexe a estreia no Visual Studio ou não.

c:\> sc start text-xyzService

Para parar o serviço:

c:\> sc stop test-xyzService

Para excluir ou desinstalar:

c:\> sc delete text-xyzService
user3942119
fonte
0

Depure um serviço do Windows em http (testado com VS 2015 Atualização 3 e .Net FW 4.6)

Em primeiro lugar, você deve criar um projeto de console dentro de sua solução VS (Adicionar -> Novo projeto -> Aplicativo de console).

Dentro do novo projeto, crie uma classe "ConsoleHost" com esse 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();
    }
}

E este é o código para a classe 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();
    }
}

Configurações como as cadeias de conexão devem ser copiadas no arquivo App.config do projeto do console.

Para fortalecer o console, clique com o botão direito do mouse no projeto do console e clique em Depurar -> Iniciar nova instância.

mggSoft
fonte
0

Basta adicionar um construtor à sua classe de serviço (se ainda não o tiver). Abaixo, você pode verificar um exemplo para o .net visual basic.

Public Sub New()
   OnStart(Nothing) 
End Sub

Depois disso, clique com o botão direito do mouse no projeto e selecione " Debug -> Iniciar uma nova instância ".

Pedro Martín
fonte