ILSpy: Ferramenta para “decompilar” Assemblies .NET
11/01/12
Olá meu caro leitor! Faz um (bom) tempo que não posto um artigo aqui, né? Como foi a sua passagem de ano? A minha foi “male mal”… Quem lê o NM Light vai saber o motivo
.
Vamos começar 2012 falando sobre uma ferramenta que pode salvar vidas (tá, estou exagerando, mas pode salvar carreiras hehe) de quem desenvolve em .NET. Quando você pega um projeto que usa algumas bibliotecas feitas pelo desenvolvedor anterior, você PRECISA fazer algumas alterações nela ou, no meu caso, compilar o projeto com um componente mais atual porque se for usar a biblioteca original ele OBRIGA A USAR a versão antiga e que o fonte foi pras cucuias?
De posse da biblioteca original você poderá ver o código-fonte usando uma ferramenta oferecida pelo próprio Visual Studio: IL Disassembler, ou IL DASM para os mais chegados. Mas tem um porém: ela apresenta o código na Common Intermediate Language (CIL), o código que o compilador JIT do .NET Framework traduz para código de máquina e por fim é executado. Bem, não é nada produtivo ficar tentando entender a IL, certo?
Há algum tempo atrás, existia uma ferramenta gratuita chamada .NET Reflector. Esta ferramenta traduzia a IL para C# ou outras linguagens da plataforma .NET e exportava para um projeto, prontinho para compilar. Um porém desta ferramenta é que ela forçava os updates automáticos, de forma que se a rotina de atualização fosse cancelada o programa “matava a si próprio”, ou seja, era apagado do disco. E não adiantava reinstalar. No fim, o desenvolvedor vendeu o .NET Reflector para a empresa Red Gate, que continua o seu desenvolvimento porém o aplicativo agora é pago e a versão Standard custa US$ 35,00 (fonte: http://www.reflector.net/).
Dando uma pesquisada por aí, achei um substituto legal e que cumpre ao meu propósito: o IL Spy. Ele também é um “disassembler” para .NET, que traduz o código em IL para C# e permite salvar o projeto. Só que com um detalhe: é de código-aberto e grátis (lembre-se que uma característica não implica em ter a outra
).
Com ele, consegui pegar todas as classes que precisava, colar em um novo projeto, referenciar as bibliotecas mais atuais e compilar. Mas nem tudo são flores: Ele traduz o código de algumas propriedades, principalmente indexadas, colocando no lugar da propriedade a chamada para seu método ancestral, “getter” ou “setter”, por exemplo:
//Como a propriedade é colocada no código original:
MeuDataSet.Tables[i];
MeuArrayList.Count;
MeuDataRow["CAMPO"] = valor;
//Como ela é traduzida pelo IL Spy
MeuDataSet.get_Tables(i);
MeuArrayList.get_Count();
MeuDataRow.set_Row("CAMPO",valor);
Quando compilava sem fazer o devido ajuste ocorria um erro de compilação, pois os getters e setters das propriedades exemplificadas acima não são métodos públicos, portanto não poderiam ser acessados de fora da classe onde foram declarados.
Tirando esse pequeno atormento (que dependendo do código se torna um grande atormento…), repito que a ferramenta atendeu os meus objetivos e a recomendo em casos de emergência ou até mesmo para estudo de fontes (desde que não estejam ofuscados, claro!).
Onde baixar? Aqui: http://wiki.sharpdevelop.net/ILSpy.ashx
Votação Bebê Hipoglós Amêndoas 2011 – Analisando o sistema de votação
10/11/11
Olá caro leitor do NM Tech! Você, como desenvolvedor de sistemas, já precisou desenvolver um sistema de votação eletrônica pela Web? Se sim, você já parou para pensar qual seria a carga de acessos deste sistema e uma forma de permitir uma votação segura e confiável?
Algumas amigas minhas inscreveram seus filhos para participarem do Concurso “Bebê Hipoglós 2011”, onde a criança irá ser o “bebê-propaganda” do referido produto. Para votar basta entrar no site http://bebehipoglos2011.com.br/, escolher o bebê, clicar no ícone “Votar”, preencher um captcha e dar OK. Sim, é só isso mesmo.
Ah, Léo.. você fazendo jabá da promoção aqui no NM Tech? Epa, calma lá, rapá! Agora vou responder as duas perguntas que fiz acima: Sim, eu já desenvolvi um sistema de votação eletrônica que era aberto ao público e que a carga era moderada. E a votação era segura e confiável graças a alguns mecanismos que foram implementados.
Continuando no caso do site Bebê Hipoglós 2011, a página da campanha no Facebook (http://www.facebook.com/bebehipoglos) está cheia de comentários negativos à respeito do sistema de votação. Muitas pessoas estão reclamando que o site cai toda hora e que em pouco tempo de promoção existem crianças com mais de 10 mil votos.
Sim, constatei por mim mesmo que o sistema está sobrecarregado, e uma das coisas que me chamaram a atenção nos meus comentários foi:
“Olá pessoal estou desde as 07:15 da manhã consegui votar 511 x na minha filha mas realmente esta muito complicado votar”
Isso mesmo, a MESMA pessoa votando várias vezes em seu bebê. Então COMO as pessoas querem que a votação seja confiável e que o sistema não tenha sobrecarga? Não foi só essa pessoa que teve essa ideota de jênio*, né?
E para votar, como eu disse anteriormente, o sistema pede para preencher um captcha, muito tosco, facilmente decodificável por ferramentas de OCR, e como sabemos, já existem robôs que possuem esta funcionalidade. Com um (um não, vários… muitas pessoas podem ter tido essa idéia) robô desses fazendo várias requisições ao servidor Web, não há estrutura que aguente.
Nenhuma forma de identificação do votante é requerida.
No sistema de eleição que fiz, antes de votar o usuário tinha que preencher seus dados, confirmá-los no sistema e aí sim escolher seu candidato. Tal sistema NÃO permite que o mesmo usuário vote novamente. E mesmo com o cadastro, o voto era computado de forma totalmente anônima, sendo IMPOSSÍVEL saber quem votou em quem.
Por que não implementar, como disse um comentarista no Facebook, um passo onde o usuário registraria alguns dados pessoais e o CPF (obrigatório) antes de votar e permitisse apenas UM voto por CPF? Aí sim aumentaria e muito a confiabilidade do sistema e a farra dos robôs diminuiria bastante. Bloqueio de voto por IP não resolveria, já que muitas casas hoje em dia já contam com uma pequena rede onde a conexão à Internet é compartilhada.
Quer ver como isso é uma coisa SIMPLES de ser implementada? Vou mostrar um modelo básico, com 2 tabelas e os passos necessários (não código) para realizar a votação:![]()
Veja só: apenas duas tabelas, e não há ligação nenhuma entre elas. Para gravar um voto, crie uma StoredProcedure no banco de dados com os parâmetros de entrada do eleitor (Nome, CPF, Email e o ID do candidato que ele votou) Coloque o modo de isolação de transação no modo Repeatable Read (recomendado), abra uma transação, faça um select no eleitor baseando-se no CPF para verificar se ele já já existe na tabela “Eleitor” (se sim, dispare uma exceção informando a interface desta situação), dê um select no candidato baseando-se no ID passado como entrada (para travar o registro, já que isolamos a transação como Repeatable Read), faça um Update no Candidato, incrementando 1 no campo “QtdVotos”, faça o Insert na tabela Eleitor e dê commit na transação.
Simples assim, não? Ah, e antes que venham com o “Ah, mas o DBA ou outra pessoa pode alterar o campo QtdVotos e tal…”, não estou contando que esta intervenção humana venha a acontecer, por que daí já é caso de Justiça e tal, são outros quinhentos.
Isso vai burocratizar o processo de votação? SIM. E este é o preço a pagar por uma maior confiabilidade no sistema de votação. O que é mais vantajoso para a sua empresa: Um sistema que é um pouco “chato” para votar porém os usuários falam que é confiável ou um totalmente escancarado dando margem para inúmeras reclamações quanto a idoneidade do concurso? Lembrem-se que um comentário negativo viaja milhares de vezes mais rápido que um positivo! E se não fossem pelos comentários negativos (que minhas amigas que inscreveram seus bebês fizeram e fui conferir o sistema), nem escreveria este post!
Eu não me conformo como um sistema que dá uma premiação em dinheiro, mexe com as emoções de milhares de mães/pais que cadastraram seus filhos (sim, porque as crianças ainda não entendem) na expectativa de vê-los em um comercial de TV e se tornarem famosos, ser modelado de uma forma TÃO TOSCA???
Um abraço!!!
*com “J” de JUMENTO
Calculando idade via SQL Server (UDF)
04/11/11
Em nossos sistemas muitas vezes precisamos consultar a idade de uma pessoa para as mais diversas finalidades. A conta é simples: diferença de anos entre a data atual e a data de nascimento, certo?
Eu diria que em partes, dependendo de como o cálculo é feito. Em se tratando apenas de SQL Server o nosso primeiro impulso é calcular essa diferença usando a função datediff(), passando como argumentos que queremos o resultado em anos e as datas envolvidas.
Calcular idade NÃO é apenas uma simples diferença entre o componente ANO das datas, que é o que a função datediff faz se passarmos como primeiro argumento que queremos a diferença em anos. Nesse caso o cálculo da diferença não leva em consideração os dias e os meses, fundamentais para o cálculo de idade. A questão que devemos ter em mente é: “A pessoa já completou determinada idade no dia atual?”
Para facilitar este cálculo montei uma UDF em Transact-SQL, que retorna um valor escalar que é a idade baseada na data de nascimento informada e no dia atual, que leva em consideração que tal data de aniversário foi atingida ou não. Vamos a ela:
create function [dbo].[CalculaIdade](@DataNascimento Datetime) returns int AS BEGIN declare @idade int; --Coloca o dia e mês da data de nascimento no ano atual declare @DataNormalizada datetime; --Volta a data em 01/01 do ano atual sem utilizar concatenação de strings (evita lembrar usar o SET DATEFORMAT) set @DataNormalizada = dateadd(dd,0,datediff(dd,0,dateadd(dd,(-1)*day(getdate()) + 1,dateadd(mm,(-1)*month(getdate()) + 1,getdate())))); --Agora sim, calcula a data de aniversário no ano atual set @DataNormalizada = dateadd(dd,day(@DataNascimento) -1,dateadd(mm,month(@DataNascimento) -1,@DataNormalizada)); --Calcula a idade em anos (o SQL Server irá apenas subtrair o componente ANO, sem considerar os dias) set @idade = datediff(yy,@DataNascimento,getdate()); --se ainda não chegou no dia de nascimento, subtrai 1 da idade, já que ainda não estará completa. --O cálculo acima com datediff não leva isto em consideração. if( @DataNormalizada > getdate()) begin set @idade = @idade - 1; end return @idade; END GO
Primeiramente declaramos uma variável @DataNormalizada, que nada mais é que a data de aniversário no ano corrente e para calculá-la sem concatenar strings (para não nos preocuparmos com formatação de data) “voltamos” o ano atual para 01/01 e em seguida acrescentamos o mês e o dia da data de aniversário. Nessa adição, necessitamos subtrair 1 do dia e do mês porque como começamos o valor em “1” e não em “0”, necessitamos compensar essa diferença.
Em seguida fazemos o que o nosso impulso manda: calculamos a diferença em anos utilizando a função datediff().
Agora que vem o pulo do gato: Lembra que calculamos a data de aniversário da pessoa no ano corrente? Pois bem: Verificamos se a data de aniversário é maior que a data atual. Se sim, significa que a pessoa ainda não fez aniversário e que a idade em anos calculada acima está incompleta, portanto, temos que subtrair 1 ano da idade.
Esse pequeno detalhe do cálculo da idade é crucial (sim, dependendo da área de negócio, exemplos como sistemas de gestão de saúde, a idade correta de uma pessoa é muito importante e é um dado dinâmico sempre calculado a partir da data de nascimento), portanto, preste MUITA atenção e bora utilizar a função acima para calcular!
DateTime, Serialização de DataSets em WCF, Horário de Verão… uma combinação com muitas surpresas!
17/08/11
Veja a seguinte situação: Você possui um serviço WCF que está hospedado em um servidor e vários clientes que acessam este serviço. Este serviço possui um método que retorna um DataSet, serializando-o através do WCF e a classe cliente exibe o resultado em tela.
Pois bem, você possui neste DataSet uma (ou mais) tabela com um campo DateTime, e no seu banco de dados está gravado o valor, por exemplo, 17/10/2011 08:00, mas a sua aplicação exibe o valor 17/10/2011 09:00.
O que!!! Quer dizer que a aplicação está exibindo este valor uma hora ADIANTADA? Exatamente isso, meu querido! Se fomos extrair este campo para uma variável DateTime e verificarmos o resultado do método IsDayLightSavingTime(), o mesmo retornará True, indicando que a data em questão cai no horário de verão.
Se fomos olhar isso mais a fundo na aplicação cliente, veremos que o valor já considerando o horário de verão (no nosso caso, 1 hora adiantada) já vem do próprio DataSet que recuperamos via WCF do servidor. Se a aplicação cliente extraísse esse valor diretamente do banco de dados ele seria exibido corretamente. E se quisermos exibir o valor do banco de dados, como faz?
Na máquina que executa a aplicação cliente, caso você esteja utilizando o Windows XP, dê um duplo clique no relógio, acesse a guia “Fuso Horário” e desmarque a opção “Ajustar automaticamente para o horário de verão” e o seu problema estará resolvido e a aplicação passará a exibir as horas conforme estão gravadas no banco de dados.
Maaaaaaaas… tem um pequeno grande porém nesta “solução”: E se este flag do horário de verão não puder ser desabilitado pelo usuário (permissões no sistema, entre outros) e não acho um meio na aplicação cliente de fazer as horas serem exibidas corretamente, sento e choro?
Sentar e chorar é uma hipótese dependendo de como você modelou a parte servidora do seu sistema. Mas a solução definitiva é a seguinte: Ao obter um DataSet do banco de dados, devemos modificar uma propriedade chamada DateTimeMode das colunas DateTime das tabelas deste DataSet para DataSetDateTime.Unespecified, que informa para o WCF não recalcular as datas para a zona de horário das aplicações clientes na hora da serialização.
Vamos ao código!
public DataSet RetornaDataSet()
{
DataSet ds = new DataSet();
//Preencher o DataSet com os valores do banco de dados aqui
//Agora, iremos modidicar as colunas DateTime das tabelas para ignorarem o recálculo de data/hora no momento da serialização WCF
foreach (DataTable tabela in ds.Tables)
{
foreach (DataColumn coluna in tabela.Columns)
{
if ((coluna.DataType == typeof(DateTime)) || (coluna.DataType == typeof(DateTime?)))
{
coluna.DateTimeMode = DataSetDateTime.Unspecified;
}
}
}
return ds;
}
Com este código modificamos todas as colunas com o tipo DateTime para que quando o DataSet for serializado via WCF, as datas não sejam recalculadas conforme a zona de tempo do usuário cliente (horário de verão, fuso-horáro, etc.).
É isso aí!
Rapidinha: CustomValidators não disparam o ServerValidate quando está vazio? Veja o que pode ser aqui!
12/08/11
Veja a seguinte situação: Você tem um campo em um WebForm que é obrigatório apenas em uma determinada condição. O mesmo campo, em outra condição NÃO é de preenchimento obrigatório. Você poderia usar um RequiredFieldValidator nele, certo?
Errado! Um RequiredFieldValidator apenas irá disparar se o campo NÃO estiver preenchido. O que queremos é que numa condição X em que um textbox esteja vazio, o validator execute, e em outra não.
Para isso podemos utilizar um CustomValidator e atribuir a propriedade de argumento de evento IsValid no evento ServerValidate para que seja True se as nossas condições forem obedecidas.
Porém (sempre tem um porém!) justamente quando você quer validar quando o campo está vazio utilizando um CustomValidator o evento ServerValidate não dispara nem a pau, mesmo você chamando diretamente o método Validate() do mesmo. E você não pode usar um RequiredFieldValidator. O que você faz, ajoelha e chora?
Com exceção do RequiredFieldValidator, TODOS os demais validators não executam quando um textbox na propriedade ControlToValidate está vazio. Este é um comportamento padrão. Vamos dar uma olhada na caixa de propriedades de um CompareValidator e um CustomValidator:
Além das propriedades inerentes a cada tipo de Validator, temos uma propriedade curiosa no CustomValidator: ValidateEmptyText. Se colocarmos ela em True, o evento ServerValidate irá ser executado ao verificar a propriedade IsValid da página ou outra rotina de validação. Os outros tipos de validator não validam quando o texto está vazio. Com isso podemos testar, por exemplo, a obrigatoriedade de um campo em determinada condição em nosso sistema.
Dica fácil essa, né? Quando pesquisei sobre o assunto, vi uma sugestão em que era necessário derivar uma classe do “pai” dos validators!
Espero que esta dica seja de bom proveito





