segunda-feira, 22 de junho de 2015

Capturando eventos de Logon e Logoff através de uma aplicação WindowsService

Olá Pessoal,

Estou tirando a poeira do blog trazendo um assunto que me queimou uns bons neurônios nestes últimos dias.

Estou desenvolvendo uma aplicação em C# com .NET4 do tipo WindowsService que irá realizar diversas funções em um computador rodando Windows. Uma destas funções é registrar os logons e logoffs dos usuários que utilizarem tais computadores em um banco de dados SQL Server remoto para posterior consulta e auditoria.

Pesquisando na web, encontrei diversas formas de codificar a captura destes eventos. Enquanto algumas se mostraram muito simples, outras eram bem complexas e cheias de código. Por fim, elas não apresentaram o resultado como eu gostaria de visualizar.

Depois de alguns dias escovando bit, cheguei a uma solução utilizando um pouco de tudo que li nesses dias. O resultado eu gostaria de compartilhar com vocês e, caso conheçam uma forma mais simples, não deixem de postar.Afinal de contas, o blog foi criado para compartilharmos informações.

Vamos então à minha solução:

Toda classe derivada de ServiceBase possui a propriedade CanHandleSessionChangeEvent que quando definida como true, faz com que a classe derivada seja notificada caso haja alterações nas sessões do Windows.

Para tratar as notificações, devemos implementar o seguinte evento:






A estrutura SessionChangeDescription possui 2 propriedades:

  • Reason: descreve o tipo do evento (SessionLogon, SessionLogoff, RemoteConnect, RemoteDisconnect, SessionLock, SessionUnlock, dentre outros);
  • SessionId: contém o número da sessão em que o evento ocorreu (1, 2, 3, ...).
Até aqui foi sopinha no mel...

A coisa começou a complicar quando tentei descobrir qual era o usuário conectado na sessão do evento e descobri que no .NET não há uma forma "nativa" de fazer isso. Nem uma classe, método, evento, nada... Pelo menos, eu não encontrei. E olha que revirei vários links do Google.

O máximo que encontrei, foi o pessoal utilizando APIs de Terminal Services com códigos extremamente grandes fazendo chamadas a DLLs externas e que ainda sim, não traziam o resultado que eu queria: Listavam os usuários conectados, mas não as sessões. 

Ou eu não fiz a codificação direita ou deixei algo passar despercebido ou eu sou muito burro mesmo, mas enfim... Não fui muito feliz com o lance das APIs. Além do que, parece que essas APIs são compatíveis apenas com o Vista ou superior e eu preciso que o código rode também no XP. É o XP ainda não morreu por aqui...

Foi então que voltei minha atenção a uma tecnologia que tenho utilizado nos últimos anos e que tem me ajudado bastante: Windows Management Instrumentation ou simplesmente WMI.

Pesquisando as classes do namespace root\CIMv2 encontrei a classe Win32_Process que lista os processos atualmente em execução no computador e, ao selecionar um processo específico, eu tenho a propriedade SessionId que mostra em qual sessão o processo está sendo executado. E para minha alegria esta mesmíssima classe possui um método chamado GetOwner que retorna o nome do usuário que está executando aquele processo... Bingo! Bastava agora encontrar uma forma de filtrar os processos. E minha solução para todo este caos foi a seguinte:

Antes de mais nada, adicione System.Management à lista de referências do seu projeto.

Criei uma estrutura simples para armazenar as sessões de usuário e declarei-a no escopo do meu serviço:



Criei um método para atualizar as sessões:

















E um método para esperar por uma sessão:











No OnStart do serviço, chamei o método RefreshUserSessions() para que o serviço saiba quais sessões já estão ativas no computador. E reescrevi o evento OnSessionChange da seguinte forma:



















Na parte de //Código... entra a minha codificação para o banco de dados. Mas para saber qual é o usuário da sessão basta usar, por exemplo:
string userName = UserSessions[changeDescription.SessionId];

Testei esta solução no XP, 7 e 8.1 e funcionou perfeitamente.

Desculpem por não postar o código como um todo, mas a verdade que o meu código original é bem diferente do postado aqui, pois algumas coisas eu transformei em classes distintas para reusabilidade em outras partes do meu projeto. Mas a essência da minha solução é esta.

Desculpem também por postar os códigos como imagem, mas a formatação do Blogspot para a endentações e cores não ajuda muito.

Espero que minha solução sirva para mais alguém ou que alguém apresente algo mais "requintado". Comentários são sempre bem-vindos.

Até o próximo post.

sexta-feira, 1 de novembro de 2013

Falha no logon do Serviço de Perfil de Usuário

Hoje pela manhã atendi um notebook com Windows 7 Home Premium que apresentava a mensagem acima quando o usuário tentava logar.

Resolvi o problema seguindo as orientações neste site: http://support.microsoft.com/kb/947215/pt-br

Outro site que me ajudou foi: http://www.sevenforums.com/tutorials/507-built-administrator-account-enable-disable.html

Até a próxima.

quinta-feira, 25 de julho de 2013

DFSR: Como determinar o tamanho mínimo de Quota para o Staging?

Para quem já trabalha com a tecnologia do DFSR (Distributed File System Replication), sabe que uma configuração errada de quota do staging pode ocasionar problemas de performance na replicação.

Encontrei este artigo fantástico que trás a fórmula para o cálculo da quota do staging.

Basicamente, somamos o tamanho dos maiores arquivos da pasta que será replicada e dividimos o resultado por 1 gigabyte. A quantidade de arquivos a serem somados varia de acordo com a versão do Windows Server:
  • Windows Server 2003 R2: 9 arquivos
  • Windows Server 2008 e 2008 R2: 32 arquivos
Os comandos a seguir, devem ser executados dentro do ambiente do PowerShell.

Para somar o tamanho dos maiores arquivos, utilizamos o seguinte comando:

$big32 = Get-ChildItem c:\temp -recurse | Sort-Object length -descending | select-object -first 32 | measure-object -property length –sum

Substitua a pasta c:\temp pela sua pasta de replicação. Note que foi passado o parâmetro -first 32 que indica que este comando está sendo executado em um computador rodando o Windows Server 2008 ou 2008 R2.

Aguarde até que o comando conclua sua execução, o que pode levar um certo tempo dependendo do tamanho da pasta de replicação.

Após a conclusão, divida o valor obtido por 1 gigabyte com o seguinte comando:

$big32.sum / 1gb

Do valor obtido, arredonde a parte fracionada para cima.

Por exemplo: se o valor obtido foi 10,1; arredonde para 11.

Basta agora multiplicar o valor obtido por 1024 e digitar o resultado no campo Quota da guia Staging na janela de Propriedades.

 
 
Até a próxima.

quinta-feira, 18 de julho de 2013

MDT: Tentando se conectar ao antigo servidor

Quando utilizamos o MDT para a implantação automatizada do Windows, ele cria uma pasta na raiz da unidade C: chamada MININT.

"Teoricamente", essa pasta deveria ser excluída ao término do processo. Mas em 99,99% dos casos que presenciei, isso não acontece.

Em geral, não há nenhum problema com a permanência desta pasta no HD. A exceção é quando trocamos o nome do servidor onde reside o DeploymentShare do MDT.

O cliente do MDT, quando encontra a pasta MININT, usa suas informações para se conectar no DeploymentShare. Se o nome do servidor tiver mudado, teremos um erro de conexão com o DeploymentShare. Para resolver isto é muito simples:

Pressionando a tecla F8 (dentro do ambiente do WinPE) será aberto o Prompt de Comando. Basta excluir a pasta com o comando:

rd c:\minint /s

Reinicie o computador para reiniciar o processo:

wpeutil reboot

Caso você queira apagar todo o conteúdo do HD, utilize os seguintes comandos:

diskpart
select disk 0
clean
exit
wpeutil reboot

Até a próxima.