Neste post vou demonstrar como gerenciar serviços do Windows (Windows Services) a partir do banco de dados SQL Server, utilizando o SQLCLR para realizar essas operações. Isso pode ser especialmente útil na criação de monitoramentos de determinados serviços.
Atualmente onde eu trabalho, criei uma tabela de configuração do monitoramento de serviços, onde determino os dados dos serviços que pretendo monitorar, como nome do servidor, nome do serviço, se ao falhar precisa enviar sms e se ao falhar a própria rotina deve se encarregar de iniciar o serviço automaticamente. O e-mail com a lista dos serviços que tiveram alteração de situação é sempre enviado. Uma vez configurado, são realizadas coletas via job a cada X minutos da situação desses serviços e esse dados são armazenados em uma tabela de histórico.
Com isso, é possível realizar medidores de disponibilidade dos serviços e utilizando o Reporting Services 2016 para criar relatórios lendo da tabela de histórico, pode-se recuperar os dados atuais dessa tabela e acompanhar a situação de diversos serviços, em diversos servidores, de qualquer lugar: Seja pelo Browser (acessando o caminho do Reporting Services) ou pelo celular (utilizando o aplicativo do PowerBI), sem a necessidade de ter um computador por perto, conectar na VPN da empresa ou comprar um sistema de monitoramento, que além de ter altos custos, nem sempre oferece essa flexibilidade.
Para realizar essas operações com o CLR, vou listar duas maneiras diferentes de se obter essas informações, e depois de ler, vocês vão entender o porque. No código-fonte que vocês vão ler abaixo, utilizei a classe Retorno para gerenciar avisos e mensagens de erro que envio para o banco de dados e ela é pré-requisito para a utilização das Stored Proecedures de gerenciamento de serviços. Você pode encontrar o código-fonte da classe Retorno no post SQL Server – Como enviar avisos e mensagens de erro para o banco pelo CLR (C#).
Vou demonstrar duas formas distintas de fazer a mesma coisa. A primeira delas, utiliza binários nativos do próprio sistema operacional e, principalmente na parte de listagem de serviços, existem manipulações manuais de informações, que no caso da outra classe que vou apresentar, isso já está encapsulado no framework .NET. O que mais me motiva a utilizar essa primeira alternativa é que ela não possui nenhuma dependência de DLL fora das DLL’s padrão suportadas e recomendadas pela Microsoft. Já a biblioteca System.ServiceProcess requer várias outras DLL’s não suportadas que ela possui dependências.
SQL Server - CLR Object Dependencies System.ServiceProcess ServiceController Class
Quando seu banco apresenta algum problema e você envia o DUMP do erro pra Microsoft, a primeira coisa que o consultor irá apontar como problema são DLL’s do CLR não suportadas pela Microsoft, como a System.ServiceProcess, então prefiro evitar. Essas DLL’s não suportadas são DLL’s que podem ser registradas manualmente, mas não no modo SAFE, inclusive, já havia falado sobre isso no post Introdução ao SQL CLR (Common Language Runtime) no SQL Server.
Utilizando a classe Process da biblioteca System.Diagnostics
Uma das formas de se conseguir realizar essas atividades pelo CLR, é utilizando a classe Process, que nos permite executar comandos do Prompt-DOS dentro do C#. Com isso, pode-se utilizar o binário sc.exe (saiba mais sobre ele clicando neste link) para parar e iniciar os serviços, e o binário wmic.exe (saiba mais sobre ele clicando neste link) para listar os serviços.
Como listar os serviços de um servidor:
using System.Collections;
using System.Data.SqlTypes;
using System.Diagnostics;
using System.Linq;
using System.Text;
public partial class UserDefinedFunctions
{
private class ServiceProperties
{
public SqlString AcceptPause;
public SqlString AcceptStop;
public SqlString Caption;
public SqlString Description;
public SqlString DesktopInteract;
public SqlString ErrorControl;
public SqlString ExitCode;
public SqlString Name;
public SqlString PathName;
public SqlString ProcessID;
public SqlString ServiceType;
public SqlString StartMode;
public SqlString StartName;
public SqlString Started;
public SqlString State;
public SqlString Status;
public ServiceProperties(SqlString acceptPause, SqlString acceptStop, SqlString caption, SqlString description, SqlString desktopInteract, SqlString errorControl,
SqlString exitCode, SqlString name, SqlString pathName, SqlString processID, SqlString serviceType, SqlString startMode, SqlString startName,
SqlString started, SqlString state, SqlString status)
{
AcceptPause = acceptPause;
AcceptStop = acceptStop;
Caption = caption;
Description = description;
DesktopInteract = desktopInteract;
ErrorControl = errorControl;
ExitCode = exitCode;
Name = name;
PathName = pathName;
ProcessID = processID;
ServiceType = serviceType;
StartMode = startMode;
StartName = startName;
Started = started;
State = state;
Status = status;
}
}
[Microsoft.SqlServer.Server.SqlFunction(
FillRowMethodName = "ListarServicos",
TableDefinition = "AcceptPause nvarchar(20), AcceptStop nvarchar(20), Caption nvarchar(2048), Description nvarchar(4000), DesktopInteract nvarchar(30), " +
"ErrorControl nvarchar(60), ExitCode nvarchar(30), Name nvarchar(1024), PathName nvarchar(1024), ProcessID nvarchar(500), " +
"ServiceType nvarchar(30), StartMode nvarchar(30), StartName nvarchar(60), Started nvarchar(256), State nvarchar(60), " +
"Status nvarchar(60)"
)]
public static IEnumerable fncServicos_Listar(string Ds_Servidor)
{
var scriptProc = new Process
{
StartInfo =
{
FileName = @"wmic",
Arguments = "/node:\"" + Ds_Servidor + "\" service get Name,Caption,Started,Status,StartMode,StartName,PathName,AcceptPause,AcceptStop,DesktopInteract,ErrorControl,ExitCode,ProcessID,ServiceType,Description,State /FORMAT:list",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
StandardOutputEncoding = Encoding.GetEncoding(850),
CreateNoWindow = true
}
};
scriptProc.Start();
var output = scriptProc.StandardOutput.ReadToEnd();
var linhas = output.Split('\n');
var Qt_Linhas = (linhas.Length - 1);
var contador = 0;
var palavra = new ArrayList();
var ServicePropertiesCollection = new ArrayList();
for (var i = 0; i < Qt_Linhas; i++)
{
var linha = linhas[i];
if (linha.Trim().Length <= 0) continue;
contador++;
palavra.Add(linha.Split('=').Last().Trim());
if (contador != 16) continue;
ServicePropertiesCollection.Add(new ServiceProperties(
palavra[0].ToString(),
palavra[1].ToString(),
palavra[2].ToString(),
palavra[3].ToString(),
palavra[4].ToString(),
palavra[5].ToString(),
palavra[6].ToString(),
palavra[7].ToString(),
palavra[8].ToString(),
palavra[9].ToString(),
palavra[10].ToString(),
palavra[11].ToString(),
palavra[12].ToString(),
palavra[13].ToString(),
palavra[14].ToString(),
palavra[15].ToString()
));
palavra.RemoveRange(0, 16);
contador = 0;
}
return ServicePropertiesCollection;
}
public static void ListarServicos(object objServiceProperties, out SqlString acceptPause, out SqlString acceptStop, out SqlString caption, out SqlString description,
out SqlString desktopInteract, out SqlString errorControl, out SqlString exitCode, out SqlString name, out SqlString pathName, out SqlString processID,
out SqlString serviceType, out SqlString startMode, out SqlString startName, out SqlString started, out SqlString state, out SqlString status)
{
var serviceProperties = (ServiceProperties) objServiceProperties;
acceptPause = serviceProperties.AcceptPause;
acceptStop = serviceProperties.AcceptStop;
caption = serviceProperties.Caption;
description = serviceProperties.Description;
desktopInteract = serviceProperties.DesktopInteract;
errorControl = serviceProperties.ErrorControl;
exitCode = serviceProperties.ExitCode;
name = serviceProperties.Name;
pathName = serviceProperties.PathName;
processID = serviceProperties.ProcessID;
serviceType = serviceProperties.ServiceType;
startMode = serviceProperties.StartMode;
startName = serviceProperties.StartName;
started = serviceProperties.Started;
state = serviceProperties.State;
status = serviceProperties.Status;
}
};
SQL Server - SC binary CLR list windows services Windows Service Manager
SQL Server - SC binary CLR list windows services
Como iniciar um serviço:
using System;
using System.Data.SqlTypes;
using System.Diagnostics;
using System.Text;
using Bibliotecas.Model;
public partial class StoredProcedures
{
[Microsoft.SqlServer.Server.SqlProcedure]
public static void stpServicos_Iniciar(SqlString Ds_Servidor, SqlString Ds_Servico)
{
try
{
var scriptProc = new Process
{
StartInfo =
{
FileName = @"sc.exe",
UseShellExecute = false,
Arguments = "\\\\" + Ds_Servidor.Value + " start \"" + Ds_Servico.Value + "\"",
RedirectStandardOutput = true,
RedirectStandardError = true,
StandardOutputEncoding = Encoding.GetEncoding(850),
StandardErrorEncoding = Encoding.GetEncoding(850),
CreateNoWindow = true
}
};
scriptProc.Start();
var output = scriptProc.StandardOutput.ReadToEnd();
var erro = scriptProc.StandardError.ReadToEnd();
Retorno.Mensagem("Iniciando o serviço " + Ds_Servico.Value + " no servidor " + Ds_Servidor.Value + "...");
if (output.Length > 0)
{
Retorno.Mensagem(output);
}
if (erro.Length > 0)
{
Retorno.Erro(erro);
}
}
catch (Exception e)
{
Retorno.Erro("Erro : " + e.Message);
}
}
};
SQL Server - SC binary CLR start windows services2
Como parar um serviço:
using System;
using System.Data.SqlTypes;
using System.Diagnostics;
using Bibliotecas.Model;
using System.Text;
public partial class StoredProcedures
{
[Microsoft.SqlServer.Server.SqlProcedure]
public static void stpServicos_Parar(SqlString Ds_Servidor, SqlString Ds_Servico)
{
try
{
var scriptProc = new Process
{
StartInfo =
{
FileName = @"sc.exe",
UseShellExecute = false,
Arguments = "\\\\" + Ds_Servidor.Value + " stop \"" + Ds_Servico.Value + "\"",
RedirectStandardOutput = true,
RedirectStandardError = true,
StandardOutputEncoding = Encoding.GetEncoding(850),
StandardErrorEncoding = Encoding.GetEncoding(850),
CreateNoWindow = true
}
};
scriptProc.Start();
var output = scriptProc.StandardOutput.ReadToEnd();
var erro = scriptProc.StandardError.ReadToEnd();
Retorno.Mensagem("Parando o serviço " + Ds_Servico.Value + " no servidor " + Ds_Servidor.Value + "...");
if (output.Length > 0)
{
Retorno.Mensagem(output);
}
if (erro.Length > 0)
{
Retorno.Erro(erro);
}
}
catch (Exception e)
{
Retorno.Erro("Erro : " + e.Message);
}
}
};
SQL Server - SC binary CLR stop windows services
Utilizando a classe ServiceController da biblioteca System.ServiceProcess
Como listar os serviços de um servidor:
using System;
using System.Data;
using System.Data.SqlTypes;
using System.ServiceProcess;
using System.Text;
using Bibliotecas.Model;
using Microsoft.SqlServer.Server;
public partial class StoredProcedures
{
[Microsoft.SqlServer.Server.SqlProcedure]
public static void stpServicos_Listar(SqlString Ds_Servidor, SqlString Ds_Servico)
{
string servico = (Ds_Servico.IsNull) ? "" : Ds_Servico.Value;
SqlMetaData[] colunas = new SqlMetaData[7];
colunas[0] = new SqlMetaData("DisplayName", SqlDbType.VarChar, 1024);
colunas[1] = new SqlMetaData("CanPauseAndContinue", SqlDbType.Int);
colunas[2] = new SqlMetaData("CanShutdown", SqlDbType.Int);
colunas[3] = new SqlMetaData("CanStop", SqlDbType.Int);
colunas[4] = new SqlMetaData("ServicesDependedOn", SqlDbType.VarChar, 1024);
colunas[5] = new SqlMetaData("ServiceType", SqlDbType.VarChar, 1024);
colunas[6] = new SqlMetaData("Status", SqlDbType.VarChar, 1024);
SqlDataRecord linhaSQL = new SqlDataRecord(colunas);
SqlPipe pipe = SqlContext.Pipe;
pipe.SendResultsStart(linhaSQL);
ServiceController[] services = ServiceController.GetServices(Ds_Servidor.Value);
foreach (ServiceController sc in services)
{
try
{
if (sc.ServiceName == servico || string.IsNullOrEmpty(servico))
{
var CanPauseAndContinue = (sc.CanPauseAndContinue) ? 1 : 0;
var CanShutdown = (sc.CanShutdown) ? 1 : 0;
var CanStop = (sc.CanStop) ? 1 : 0;
linhaSQL.SetSqlString(0, new SqlString(sc.ServiceName));
linhaSQL.SetSqlInt32(1, new SqlInt32(CanPauseAndContinue));
linhaSQL.SetSqlInt32(2, new SqlInt32(CanShutdown));
linhaSQL.SetSqlInt32(3, new SqlInt32(CanStop));
StringBuilder Ds_Dependencias = new StringBuilder();
foreach (ServiceController dependencia in sc.ServicesDependedOn)
{
Ds_Dependencias.Append(dependencia.DisplayName);
Ds_Dependencias.Append(";");
}
linhaSQL.SetSqlString(4, new SqlString(Ds_Dependencias.ToString()));
linhaSQL.SetSqlString(5, new SqlString(sc.ServiceType.ToString()));
linhaSQL.SetSqlString(6, new SqlString(sc.Status.ToString()));
pipe.SendResultsRow(linhaSQL);
}
}
catch (InvalidOperationException e)
{
//Retorno.Erro("Erro : " + e.Message);
}
}
pipe.SendResultsEnd();
}
};
SQL Server - SC binary CLR list windows services Windows Service Manager
SQL Server - SC ServiceController Class CLR list windows services
Como iniciar um serviço:
using System;
using System.Data.SqlTypes;
using System.ServiceProcess;
using Bibliotecas.Model;
public partial class StoredProcedures
{
[Microsoft.SqlServer.Server.SqlProcedure]
public static void stpServicos_Iniciar(SqlString Ds_Servidor, SqlString Ds_Servico)
{
try
{
ServiceController sc = new ServiceController(Ds_Servico.Value, Ds_Servidor.Value);
sc.Start();
}
catch (Exception e)
{
Retorno.Erro("Erro : " + e.Message);
}
}
};
SQL Server - SC ServiceController Class CLR start windows services
Como parar um serviço:
using System;
using System.Data.SqlTypes;
using System.ServiceProcess;
using Bibliotecas.Model;
public partial class StoredProcedures
{
[Microsoft.SqlServer.Server.SqlProcedure]
public static void stpServicos_Parar(SqlString Ds_Servidor, SqlString Ds_Servico)
{
try
{
ServiceController sc = new ServiceController(Ds_Servico.Value, Ds_Servidor.Value);
sc.Stop();
}
catch (Exception e)
{
Retorno.Erro("Erro : " + e.Message);
}
}
};
SQL Server - SC ServiceController Class CLR stop windows services
É isso aí, pessoal!
Espero que tenham gostado do post. É muito legal criar coisas diferentes utilizando o banco de dados.
Qualquer dúvida, deixem aqui nos comentários.
Abraço!
sql server clr sqlclr service controller sc wmic windows services serviços windows listar iniciar parar list view start stop
sql server clr sqlclr service controller sc wmic windows services serviços windows listar iniciar parar list view start stop
Dirceu Resende
Arquiteto de Banco de Dados e BI · Microsoft MVP · MCSE, MCSA, MCT, MTA, MCP.
Comentários (0)
Carregando comentários…