Páginas

terça-feira, 28 de agosto de 2012

Criando um Serviço Windows - WindowsService + C# .Net4.0 + Thread + App.Conf + Auto Install

Olá Pessoal,

DOWNLOAD O PROJETO: GOOGLE CODE.com

 
Hoje vou ensinar vocês a como criar um Windows Service [ Serviço Windows] utilizando C# .NET 4.0 e Thread, pois tive que fazer um e o pessoal que cria ServiçosWindows geralmente não usam Threads e nem configurações no App.Conf e muito menos fazem o serviço ser Auto Instalável, clicando no aplicativo e BUM... tá funcionando para sempre.



Bem vamos começar:
  1. Abra seu Visual Studio 2010
  2. Crie um Novo Projeto (File->New->Project ou CRTL+SHIFT+N)
  3. Selecione a Opção "Windows Service"
  4. No meu caso o nome da minha aplicação é ServiceWSFeixe e ela vai para a Solução que já tenho criada.
  5. No seu Solution Explorer deve conter os seguintes arquivos: 
    • Properties
    • References
    • Program.cs
    • Service1.cs
  6. A primeira coisa que iremos fazer é organizar os arquivos "CS" pois queremos que ele seja AutoInstaller.
  7. Renomeie o Service1.cs para Service.cs
  8. Crie uma nova classe chamada Processo.cs
  9. Agora vamos programar saída do serviço editando o Service.cs
  10. Clique com o botão direito no Service.cs e clique em View Code 
  11. E vamo adicionar um método static Main para substituirmos nosso Program.cs
    static void Main(string[] args)
    {
    }
    
  12. E dentro deste método iremos criar uma sessão Debug/Release para teste de Threads/Serviço já que é meio chato fazer o debug deles na IDE.
  13. Deixe a IDE no modo RELEASE de compilação pois iremos fazer o Instalador primeiro e ele não roda em modo DEBUG.
  14. Para isso usaremos as tags de compilação
    static void Main(string[] args)
    {
    #if DEBUG
    #else
    #endif
    }
  15. Dentro de #if DEBUG será chamado o Single Processo para "testar" nosso Thread e no #else estará nosso instalador.
  16. Vá no #else, e a primeira coisa que iremos adicionar será o código que faz o RUN do serviço, para isso abra o Program.cs e copie o que está dentro do static Main() e cole no else, Depois adicione um if de checagem de Ambiente conforme o código abaixo
    static void Main(string[] args)
    {
    #if DEBUG
    #else
                //Verifica se a chamada do Serviço foi ou não chamado pelo usuário.
                if (!Environment.UserInteractive) { //Chamada pelo Sistema => Executa o Serviço
        ServiceBase[] ServicesToRun;
        ServicesToRun = new ServiceBase[] { new Service() };
        ServiceBase.Run(ServicesToRun);
       }else { //Chamada pelo Usuário => Instala ou Desinstala o Serviço
    
       };
    #endif
    }
  17. Agora podemos Deletar nosso velho amigo Program.cs
  18. Agora temos que adicionar as variáveis necessárias para o funcionamento do instalador.
    public const string NAME = "ServiceWSFeixe";
    public const string DISPLAY_NAME = "Feixe WSFeixe";
    
    static void Main(string[] args)
    {
    #if DEBUG
    #else
                //Verifica se a chamada do Serviço foi ou não chamado pelo usuário.
                if (!Environment.UserInteractive) { //Chamada pelo Sistema => Executa o Serviço
        ServiceBase[] ServicesToRun;
        ServicesToRun = new ServiceBase[] { new Service() };
        ServiceBase.Run(ServicesToRun);
       }else { //Chamada pelo Usuário => Instala ou Desinstala o Serviço
    
       }
    #endif
    };
  19. Adicione agora um Novo WindowsService, no projeto clique com o botão direito, Add->New Item (CRTL+SHIFT+A)
  20. Selecione a opção Windows Service novamente e nomeio como ProjectInstaller.cs
  21. Clique com o Botão Direito no ProjectInstaller.cs e abra o View Code
  22. E vamos fazer uma completa mudança nela, deixando exatamente conforme abaixo.>
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Diagnostics;
    using System.Linq;
    using System.ServiceProcess;
    using System.Text;
    using System.IO;
    using System.Configuration.Install;
    
    namespace ServiceWSFeixe
    {
        [RunInstaller(true)]
        public partial class ProjectInstaller : System.Configuration.Install.Installer
        {
            public ProjectInstaller()
            {
                ServiceProcessInstaller spi = new ServiceProcessInstaller();
                spi.Account = ServiceAccount.LocalSystem;
                System.ServiceProcess.ServiceInstaller si = new System.ServiceProcess.ServiceInstaller();
                si.ServiceName = Service.NAME;
                si.DisplayName = Service.DISPLAY_NAME;
                si.Description = Service.DISPLAY_NAME;
                si.StartType = ServiceStartMode.Automatic;
                Installers.Add(spi);
                Installers.Add(si);
            }
            public static void Install()
            {
                string[] s = { Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) + "\\" + Service.NAME + ".exe" };
                ManagedInstallerClass.InstallHelper(s);
                ServiceController sc = new ServiceController(Service.NAME);
                sc.Start();
            }
    
            public static void Uninstall()
            {
                string[] s = { "/u", Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) + "\\" + Service.NAME + ".exe" };
                ManagedInstallerClass.InstallHelper(s);
            }
        }
    };
    
  23. Agora vamos adicionar o código final do #else que nos dará a opção de instalar e desinstalar o serviço através de um MessageBox.Show(). Abra o Service.cs e deixe exatamente como o arquivo abaixo.
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Diagnostics;
    using System.Linq;
    using System.ServiceProcess;
    using System.Text;
    using System.Windows.Forms;
    
    namespace ServiceWSFeixe
    {
        public partial class Service : ServiceBase
        {
            public const string NAME = "ServiceWSFeixe";
            public const string DISPLAY_NAME = "Feixe WSFeixe";
    
            public Service()
            {
                ConsoleTraceListener listener = new ConsoleTraceListener();
                Trace.Listeners.Add(listener);
                InitializeComponent();
            }
    
            static void Main(string[] args)
            {
    #if DEBUG
                try
                {
                    Trace.WriteLine("Iniciando DEBUG Processo");
                    Processo.doWork();
                }
                catch(Exception e)
                {
                    Trace.WriteLine("Erro ao Executar Processo"+e.Message+"\n"+e.StackTrace);
                }
    #else
                //Verifica se a chamada do Serviço foi ou não chamado pelo usuário.
                if (!Environment.UserInteractive) { //Chamada pelo Sistema => Executa o Serviço
        ServiceBase[] ServicesToRun;
        ServicesToRun = new ServiceBase[] { new Service() };
        ServiceBase.Run(ServicesToRun);
       }
       else { //Chamada pelo Usuário => Instala ou Desinstala o Serviço
        ServiceController sc = new ServiceController(NAME);
        if (!ServiceExists()) {
         if (DialogResult.OK == MessageBox.Show("Deseja instalar o serviço " + DISPLAY_NAME + "?", DISPLAY_NAME, MessageBoxButtons.OKCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2)) {
          try {
           Trace.WriteLine("Instalando o serviço \"" + DISPLAY_NAME + "\"...");
                                ProjectInstaller.Install();
          }
          catch (Exception ex) {
           Trace.TraceError(ex.Message);
          }
         }
        }
        else {
         if (DialogResult.OK == MessageBox.Show("Deseja desinstalar o serviço " + DISPLAY_NAME + "?", DISPLAY_NAME, MessageBoxButtons.OKCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2)) {
          try {
           Trace.WriteLine("Desinstalando o serviço \"" + DISPLAY_NAME + "\"...");
                                ProjectInstaller.Uninstall();
          }
          catch (Exception ex) {
           Trace.TraceError(ex.Message);
          }
         }
        }
       }
    #endif
            }
    
            protected override void OnStart(string[] args)
            {
                Processo.Start();
            }
    
            protected override void OnStop()
            {
                Processo.Stop();
            }
    
            private static bool ServiceExists()
            {
                foreach (ServiceController sc in ServiceController.GetServices())
                    if (sc.ServiceName == NAME)
                        return true;
                return false;
            }
        }
    }
    
  24. Como podemos notar eu estou usando Trace, porém eu não ensinei a configuração, e como já instalamos e desinstalamos nosso serviço sem fazer nada, então vamos configurar o Trace.log
  25. Para isso eu tentei criar o app.config na mão de diversas maneiras e não foi, então eu usei o assistente do Windows.
  26. Clique com o botão direto do mouse no nome do projeto
  27. Vá em Properties (Alt+Enter)
  28. Agora vá na aba Settings
  29. Adicione um valor como aplicação e com um nome qualquer só para ele criar e linkar o app.config para você
  30. Agora que o app.config foi criado vamos preencher com o que precisamos para fazer o trace funcionar, com o código abaixo:
    
        
            
                
    c:\log\
  31. Pronto, assim estaremos prontos para fazer os métodos de Thread na classe Processo.cs que criamos anteriormente.
  32. Primeiro irei tranformar nossa classe em uma classe 100% estática (static) pois não precisaremos de instanância para chamar no onStart() e no onStop()
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Diagnostics;
    using System.Threading;
    
    namespace ServiceWSFeixe
    {
        public static class Processo
        {
            static Processo()
            {
            }
    
            //Inicia a Thread e habilita o serviço
            public static void Start()
            {
            }
    
            //Para a Thread em ação e desabilita o serviço
            public static void Stop()
            {
            }
        }
    }
    
    
  33. Agora que nossa classe funciona, vamos colocar as Threads para logar no nosso trace.log e pronto... estamos com um serviço Online agora é só adaptar para sua necessidade
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Diagnostics;
    using System.Threading;
    
    namespace ServiceWSFeixe
    {
        public static class Processo
        {
            static Processo()
            {
            }
    
            //Inicia a Thread e habilita o serviço
            public static void Start()
            {
                Trace.WriteLine("Processo Start()");
                Thread thread = new Thread(new ThreadStart(doWork));
                thread.Start();
                Thread thread2 = new Thread(new ThreadStart(doWorks));
                thread2.Start();
            }
    
            //Para a Thread em ação e desabilita o serviço
            public static void Stop()
            {
            }
    
            //Executa o processo SEM Thread para passar no modo debug
            public static void doWork()
            {
                for (int x = 0; x <= 10000; x++)
                {
                    Trace.WriteLine("x = " + x);
                }
            }
    
            //Executa o processo SEM Thread para passar no modo debug
            public static void doWorks()
            {
                for (int x = 10000; x > 1; x--)
                {
                    Trace.WriteLine("Y = " + x);
                }
            }
        }
    }
    
Espero que vocês tenham gostado do pequeno manual. Abraços a todos... contratem a king host e me ajudem heheheheh... fuiz!!!

8 comentários:

  1. Parabéns cara! Ótimo artigo! Abraços

    ResponderExcluir
  2. Amigo, achei muito interessante!
    Mas não consegui fazer funcionar, teria o fonte pra analizarmos?

    ResponderExcluir
  3. Max System, vou preparar um repositório e colocar o link disponibilizando o Código essa semana, hoje estou meio enrolado então não sei se vai ser possível.

    ResponderExcluir
  4. Parabéns pelo post.

    Consigo fazer executar, aparecendo o alert de instalação, mas não aparece mais nada, o serviço não aparece no services.msc.

    Obrigado!!

    ResponderExcluir
    Respostas
    1. Charles,

      no CS da instalação tem a varável estática NAME = "" ela tem que ser o nome do projeto identico ao que será gerado o executavel.

      Por exemplo projeto "TesteServico" vai gerar um "TesteServico.exe" então o nome á tem que ser "TesteServico".

      Excluir
  5. Aconteceu o seguinte erro ao executar:
    "O sistema de configuração falhou ao inicializar"
    Na linha Trace.WriteLine("Iniciando DEBUG Processo"); da classe Service.cs

    ResponderExcluir