C# - Parando, Iniciando e Reiniciando os serviços do Windows - III


 O artigo de hoje vamos recordar como tratar com os serviços do Windows em uma aplicação C# mostrando como parar, iniciar e reiniciar serviços do Windows.

Na primeira parte do artigo eu mostrei como exibir todos os serviços do Windows e na segunda parte como listar os processos ativos em execução.

Para concluir esta série de artigo vamos mostrar como parar, iniciar e reiniciar serviços do Windows.

Implementar o código para realizar essa tarefa não é muito complexo, mas realizar a tarefa em si é outra história, pois o usuário deverá ter direitos de administrador do sistema para isso.

Então se você testar o código e obter a mensagem de acesso negado é por que você não tem direito de administrador.

Para entender como isso funciona saiba que a conta IUSER_MachineName (IUSER daqui em diante) é, por boas razões, uma conta relativamente limitada, com privilégios apenas acima da conta de convidado. Assim essa conta não tem permissão para iniciar e parar serviços do Windows, ou mesmo para interrogá-los (para obter seu status etc.).

Quando executado no contexto de um exe autônomo, a lógica acima é bem-sucedida porque a conta subjacente é com certeza um membro do grupo Administradores.

A maneira mais fácil, mas não recomendada, para sair desta situação, é dar à conta IUSER mais privilégios tentando adicionar esta conta ao grupo de administradores. Isso vai funcionar (mas também vai lançar alguns falha de segurança potencialmente perigoso).

Uma abordagem melhor é fazer um lista explícita dos serviços específicos do Windows que serão autorizados por meio do IIS, e definir o seu descritor de segurança de serviço individual de modo que a conta IUSER (ou outra conta/grupo criado para a ocasião) seja autorizada a iniciar e/ou parar os serviços como desejado.

A dificuldade na implementação desta abordagem é que, pelo menos que eu saiba, não há nenhuma GUI ou ferramenta intuitiva de administração para inspecionar e modificar o descritor de segurança dos serviços: você precisa usar sd e "aprender" a linguagem SDDL.

(http://stackoverflow.com/questions/6624892/access-denied-while-trying-to-stop-a-c-sharp-windows-service)

Recursos usados :

Incluindo um novo projeto na solução

Abra a solução Tratando_Servicos,  criada no VS 2013 Express for Windows desktop, no primeiro artigo anterior.

A seguir clique em FILE -> Add -> New Project;

Selecione o template  Visual C# -> Class Library;

Informe o nome ServicosWin e clique no botão OK;

Renomeie a classe Class1.cs criada por padrão para ServicosWin.cs e a seguir inclua nesta classe o código abaixo:

using System;
using System.ServiceProcess;
namespace ServicosWin
{
    public class ServicosWin
    {
        public static void StartService(string serviceName, int timeoutMilliseconds = 10000)
        {
            ServiceController service = new ServiceController(serviceName);
            try
            {
                TimeSpan timeout = TimeSpan.FromMilliseconds(timeoutMilliseconds);
                service.Refresh();
                if (service.Status == ServiceControllerStatus.Stopped)
                {
                    service.Start();
                    service.WaitForStatus(ServiceControllerStatus.Running, timeout);
                }
                else
                {
                    throw new Exception(string.Format("{0} --> já esta iniciado.", service.DisplayName));
                }
            }
            catch
            {
                throw; 
            }
        }

        public static void StopService(string serviceName, int timeoutMilliseconds = 10000)
        {
            ServiceController service = new ServiceController(serviceName);
            try
            {
                TimeSpan timeout = TimeSpan.FromMilliseconds(timeoutMilliseconds);
                service.Refresh();
                if (service.Status == ServiceControllerStatus.Running)
                {
                    service.Stop();
                    service.WaitForStatus(ServiceControllerStatus.Stopped, timeout);
                }
                else
                {
                    throw new Exception(string.Format("{0} --> já esta parado.", service.DisplayName));
                }
            }
            catch
            {
                throw; 
            }
        }
        public static void RestartService(string serviceName, int timeoutMilliseconds=10000)
        {
            ServiceController service = new ServiceController(serviceName);
            try
            {
                int millisec1 = Environment.TickCount;
                TimeSpan timeout = TimeSpan.FromMilliseconds(timeoutMilliseconds);
                service.Refresh();
                if (service.Status != ServiceControllerStatus.Stopped)
                {
                    service.Stop();
                    service.WaitForStatus(ServiceControllerStatus.Stopped, timeout);
                    // conta o resto do timeout
                    int millisec2 = Environment.TickCount;
                    timeout = TimeSpan.FromMilliseconds(timeoutMilliseconds - (millisec2 - millisec1));
                    service.Start();
                    service.WaitForStatus(ServiceControllerStatus.Running, timeout);
                }
                else
                {
                    service.Start();
                    throw new Exception(string.Format("{0} --> foi parado e a seguir iniciado", service.DisplayName));
                }
            }
            catch
            {
                throw; 
            }
        }
    }
}

Esta classe possui 3 métodos estáticos que recebem como parâmetro o nome do serviço e o tempo em milisegundos para timeout (esse parâmetro é opcional e possui o valor padrão igual a 10 segundos)

  1. StartService()

  2. StopService()

  3. RestartService()

Para testar essa classe vamos abrir a solução Tratando_Servicos,  criada no VS 2013 Express for Windows desktop, na primeira parte do artigo.

A seguir vamos incluir uma referência ao projeto Class Library ServicosWin no projeto Windows Forms Tratando_Servicos;

Clique com o botão direito sobre o projeto Tratando_Servicos e a seguir em Add Reference;

Selecione o projeto ServicosWin e clique em OK;

Agora vamos incluir no formulário do projeto 3 botões de comando a partir da ToolBox:

Disponha os controles no formulário conforme o leiaute da figura abaixo:

No início do formulário teremos que ter definidos os seguintes namespaces:

using System;
using
System.Windows.Forms;
using
System.ServiceProcess;
using
Microsoft.Win32;
using
System.Security.Principal;
using
System.Diagnostics;
using
System.Reflection;

No formulário Form1.cs vamos incluir um método rodarComoAdmin() no construtor do formulário e a seguir vamos definir uma variável chamada nomeServico para armazenar o nome do serviço selecionado:

 public Form1()
 {
       InitializeComponent();
       rodarComoAdmin();
 }

 string nomeServico = "";

O código do método rodarComoAdmin() é dado a seguir:

   private void rodarComoAdmin()
   {
            WindowsPrincipal principal = new WindowsPrincipal(WindowsIdentity.GetCurrent());
            bool administrativeMode = principal.IsInRole(WindowsBuiltInRole.Administrator);
            if (!administrativeMode)
            {
                ProcessStartInfo startInfo = new ProcessStartInfo();
                startInfo.Verb = "runas";
                startInfo.FileName = Assembly.GetExecutingAssembly().CodeBase;
                try
                {
                    Process.Start(startInfo);
                    MessageBox.Show("Você esta executando o projeto com nível de Administrador !", "Admin", MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
                catch
                {
                    throw new Exception("Não foi possível conceder acesso como Admin" + Environment.NewLine + "As operações realizadas poderão ter Acesso Negado !");
                }
            }
   }

Este código é uma tentativa de fazer com que o usuário que esta executando o projeto tenha direito de administrador. Devido às particularidades de cada Sistema operacional ele pode ou não funcionar.

Nota: Se este código fizer o programa travar encerre e desabilite o código comentando a sua chamada no construtor.

Agora vamos implementar em cada um dos botões de comando o código para realizar o respectivo serviço:

1- Parar Serviço

  private void btnPararServico_Click(object sender, EventArgs e)
  {
            if (string.IsNullOrEmpty(nomeServico))
            {
                MessageBox.Show("Informe o nome do serviço", "Nome do Servico", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            else
            {
                try
                {
                    DialogResult resultado = MessageBox.Show("Deseja parar o serviço : " + nomeServico + " ?", "Parar", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
                    if (resultado == DialogResult.Yes)
                    {
                        ServicosWin.ServicosWin.StopService(nomeServico);
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Erro : " + ex.Message + Environment.NewLine + ex.InnerException, "Parar Serviço", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
 }

2- Iniciar Serviço

  private void btnIniciar_Click(object sender, EventArgs e)
  {
            if (string.IsNullOrEmpty(nomeServico))
            {
                MessageBox.Show("Informe o nome do serviço", "Nome do Servico", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            else
            {
                try
                {
                    DialogResult resultado = MessageBox.Show("Deseja iniciar o serviço : " + nomeServico + " ?", "Iniciar", MessageBoxButtons.YesNo, MessageBoxIcon.Question);

                    if (resultado == DialogResult.Yes)
                    {
                        ServicosWin.ServicosWin.StartService(nomeServico);
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Erro : " + ex.Message + Environment.NewLine + ex.InnerException, "Iniciar Serviço", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
          }
   }

3- Reiniciar Serviço

       private void btnReiniciar_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrEmpty(nomeServico))
            {
                MessageBox.Show("Informe o nome do serviço", "Nome do Servico", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            else
            {
                try
                {
                    DialogResult resultado = MessageBox.Show("Deseja reiniciar o serviço : " + nomeServico + " ?", "Reiniciar", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
                    if (resultado == DialogResult.Yes)
                    {
                        ServicosWin.ServicosWin.RestartService(nomeServico);
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Erro : " + ex.Message + Environment.NewLine + ex.InnerException, "Reiniciar Serviço", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
        }

Finalmente vamos implementar no evento SelectedIndexChanged do controle ListView o código que obtém o nome do serviço e o atribui à variável nomeServico:

 private void lvServicos_SelectedIndexChanged(object sender, EventArgs e)
  {
            try
            {
                nomeServico = lvServicos.SelectedItems[0].SubItems[0].Text;
                
            }
            catch
            {
                nomeServico = "";
            }
 }

Executando o projeto e selecionando um serviço da lista de serviços apresentada no ListView iremos obter o seguinte resultado:

Pegue o projeto completo aqui :  Tratando_Servicos_3.zip

Jesus lhes respondeu, e disse: A minha doutrina não é minha, mas daquele que me enviou.
Se alguém quiser fazer a vontade dele, pela mesma doutrina conhecerá se ela é de Deus, ou se eu falo de mim mesmo.

João 7:16,17

Veja os Destaques e novidades do SUPER DVD Visual Basic (sempre atualizado) : clique e confira !

Quer migrar para o VB .NET ?

Quer aprender C# ??

Quer aprender os conceitos da Programação Orientada a objetos ?

Quer aprender o gerar relatórios com o ReportViewer no VS 2013 ?

  Gostou ?   Compartilhe no Facebook   Compartilhe no Twitter

Referências:


José Carlos Macoratti