Utilizando CACHE do ASP.NET com MS ACCESS, em C#

Desempenho de software é um assunto que sempre desperta o meu interesse, e foi graças a isso que acabei descobrindo um dos recursos mais fantásticos disponíveis no ASP.NET, o Cache.

Todo desenvolvedor de software sabe que uma das partes mais lentas de uma aplicação é o acesso ao disco rígido, isso porque ainda trabalhamos com um modelo de disco rígido baseado em cabeça mecânica de leitura de dados e outras características que não vem ao caso. O problema é que muitas operações costumeiras durante o desenvolvimento de um software estão diretamente ligadas ao acesso a disco, como leitura e gravação de arquivos, acesso a banco de dados, etc. E foi graças a esse tipo de “gargalo” da computação que um ser iluminado do passado pensou em um recurso que ficou conhecido como Cache. Três vivas para o inventor do Cache: IoI IoI IoI.

A palavra Cache, na computação, diz respeito a uma quantidade de memória utilizada para armazenar dados temporariamente, de modo a fornecê-los rapidamente quando solicitados pelo processador sem a necessidade de acessar sua origem (muitas vezes localizada no disco rígido, que é cerca de dez vezes mais lento que a memória principal).

O Cache do ASP.NET não é diferente, é um recurso que tem como função armazenar objetos na memória principal do computador. Adicionalmente, o Cache permite a adição de dependências ao objeto guardado, de forma que se a dependência sofrer alguma alteração, o objeto contido no Cache é automaticamente destruído para que não se torne inconsistente.

Com ele, podemos criar aplicações mais rápidas, principalmente se tais aplicações necessitam de muitos acessos a um banco de dados, por exemplo.

Para explicar como funciona tudo isso, vou mostrar como desenvolver uma aplicação que carrega um banco de dados MS Access na memória principal do computador, de modo que não seja necessário acessar o disco rígido para ler as informações contidas nele caso já tenha sido carregado para o Cache. Adicionalmente esta aplicação deve manter a consistência dos dados, ou seja, se o banco de dados for atualizado pela própria aplicação ou por terceiros, o sistema deverá reconhecer automaticamente e atualizar também o Cache.

Para esta aplicação utilizei um banco de dados bem simples, criado no MS Access 2007, que consiste em uma única tabela onde será gravado o primeiro e o último nome de algumas pessoas.

accessusertable

Obs.: Nomeie o banco de dados como “database.accdb” e adicione-o à pasta “App_Data” do seu projeto, no Visual Studio, pois o código fonte foi desenvolvido partindo do princípio que o nosso banco está localizado lá dentro.

Primeiramente, vamos criar um método que acessa o banco de dados MS Access 2007 e retorna um DataSet já carregado com as informações que desejamos:


        private System.Data.DataSet GetData(string accessFileName, string query)
        {
            System.Text.StringBuilder connectionString;
            System.Data.DataSet dataSet;

            //Initializes System.Text.StringBuilder.
            connectionString = new System.Text.StringBuilder();

            //Creates connection string based on file name.
            connectionString.AppendFormat(@"Provider=Microsoft.ACE.OLEDB.12.0;");
            connectionString.AppendFormat(@"Data Source= {0};", accessFileName);

            using (System.Data.OleDb.OleDbConnection connection = new System.Data.OleDb.OleDbConnection(connectionString.ToString()))
            {
                //Creates System.Data.OleDb.OleDbCommand.
                System.Data.OleDb.OleDbCommand command = new System.Data.OleDb.OleDbCommand(query, connection);

                //Creates System.Data.OleDb.OleDbDataAdapter.
                System.Data.OleDb.OleDbDataAdapter adapter = new System.Data.OleDb.OleDbDataAdapter(command);

                //Initializes DataSet.
                dataSet = new System.Data.DataSet();

                //Fills DataSet.
                adapter.Fill(dataSet);
            }

            //Returns data.
            return dataSet;
        }

Agora vem a mágica: Um método que recebe e adiciona à memória principal o objeto que será armazenado em Cache, uma chave identificadora e o arquivo físico ao qual o objeto está ligado em uma relação de dependência:


        private void SetCache(string cacheKey, object item, string fileNameDependency)
        {
            System.Web.Caching.CacheDependency dependency;

            //Creates dependency based on received file name.
            dependency = new System.Web.Caching.CacheDependency(fileNameDependency);

            //Stores data in the ASP.NET Cache.
            Cache.Insert(cacheKey, item, dependency);
        }

E finalmente, vamos interligar tudo no manipulador do evento Page_Load da nossa aplicação Web:


        protected void Page_Load(object sender, EventArgs e)
        {
            const string cFileName = @"~/App_Data/database.accdb";
            const string cQuery = @"SELECT [ID], [UserName], [LastName] FROM [User]";
            const string cCacheKey = @"User";
            System.Data.DataSet dsUser;

            //Gets data from the ASP.NET Cache.
            dsUser = (System.Data.DataSet)Cache.Get(cCacheKey);

            //If data are not present...
            if (dsUser == null)
            {
                //Gets data from database.
                dsUser = this.GetData(Server.MapPath(cFileName), cQuery);

                //Saves data in the ASP.NET Cache.
                this.SetCache(cCacheKey, dsUser, Server.MapPath(cFileName));
            }

            //Shows data.
            GridView1.DataSource = dsUser;
            GridView1.DataBind();
        }

Reparem que para simplificar a aplicação, foram criadas três constantes, sendo a primeira o caminho do arquivo de banco de dados, a segunda a query que será executada e a terceira uma chave única para identificar o nosso objeto no Cache do ASP.NET.

Para visualizar o resultado na tela, também foi adicionado um GridView à página, com o ID “GridView1”, onde serão exibidas as informações do banco.

Se preferir, clique aqui para baixar a solução completa (código fonte e banco de dados).

Para testar o funcionamento, insira um breakpoint no início do método Page_Load e rode a aplicação. Executando linha a linha, repare que inicialmente o nosso objeto ainda não existe no Cache e, por isso, retornará um DataSet nulo. Desta forma, a aplicação terá que acessar o banco de dados e trazer, pela primeira vez, as informações para o Cache.

Depois da primeira execução, dê um reload no navegador e o método Page_Load será executado novamente. Desta vez, o objeto estará presente no Cache e não será necessário acessar o banco de dados para exibir as informações na tela.

Depois da segunda execução, insira um novo registro no banco de dados (lembre-se de salvar e fechar a tabela aberta, caso faça a inserção diretamente pelo MS Access 2007). Dê um reload no navegador e note que ao procurar o nosso objeto no Cache ele não existe mais. Isso porque o ASP.NET detectou que o arquivo sofreu alteração e removeu da memória as informações armazenadas. Neste caso, a aplicação irá retornar ao banco de dados para recarregar os dados na memória.

Fantástico, não?!

Essa tecnologia pode ser aplicada a qualquer tipo de banco de dados baseado em arquivo, como Firebird, SQLite ou mesmo XML, arquivos MS Excel ou de texto. Existe também a possibilidade de utilizar este recurso em conjunto com o SQL Server 2005 ou superior, mas isso eu mostro em outro artigo… ;)

Baixar o código fonte completo.

Conversão Hexadecimal, Decimal, Octal e Binária no C# e VB.NET

Muitas vezes encontramos, durante o desenvolvimento de um software, situações onde precisamos realizar conversões de base numérica, como por exemplo, durante operações de criptografia ou simples utilização de cores da linguagem Html. Estas conversões podem abordar várias representações numéricas, mas geralmente envolvem as seguintes bases:

  • Base Binária;
  • Base Octal;
  • Base Decimal;
  • Base Hexadecimal;

No .NET Framework encontramos várias classes e métodos já prontos para trabalharmos com conversões entre as quatro bases citadas anteriormente, eliminando a necessidade de desenvolvermos nossas próprias classes de conversão.

Entre todos os métodos, os mais fáceis de utilizar para conversão entre bases numéricas são Convert.ToInt32 e Convert.ToString, como pode ser verificado nos exemplos abaixo:

Conversão decimal -> hexadecimal e hexadecimal -> decimal em C#.


            string hexadecimalNumber;
            int decimalNumber;

            //Converts 1307 decimal number to hexadecimal base (16).
            hexadecimalNumber = Convert.ToString(1307, 16);

            //Converts hexadecimal number to decimal base.
            decimalNumber = Convert.ToInt32(hexadecimalNumber, 16);

Conversão binário -> decimal e decimal -> binário em C#.


            string binaryNumber;
            int decimalNumber;

            //Converts 1307 decimal number to binary base (2).
            binaryNumber = Convert.ToString(1307, 2);

            //Converts binary number to decimal base.
            decimalNumber = Convert.ToInt32(binaryNumber, 2);

Através destes métodos você poderá realizar conversões entre qualquer uma destas bases.

Adicionalmente, o C#.NET e o Visual Basic.NET oferecem uma forma simples de representar um número na base hexadecimal ao invés de representá-lo na base decimal, como estamos acostumados. Isso pode simplificar significativamente o desenvolvimento de algoritmos que realizam operações com números hexadecimais, permitindo operações diretas sem a necessidade de conversão prévia.

Como representar um número hexadecimal em C#:


            int number;

            //Sets 1307 hexadecimal number (51B) to int variable.
            number = 0x51B;

Como representar um número hexadecimal em VB:


        Dim number As Integer

        'Sets 1307 hexadecimal number (51B) to int variable
        number = &H51B

Como sempre, em caso de dúvida, utilizem os comentários… ;)

Como pegar o endereço URL em Silverlight 2.0

Rapidinha de hoje:

Para capturar o endereço (URL) do site corrente em Silverlight 2.0 basta pegar a propriedade “referrer” do System.Windows.Browser.HtmlPage.Document com o método GetProperty, da seguinte maneira:


   string urlAddress;

   //Gets URL address of current page (referrer property).
   urlAddress = System.Windows.Browser.HtmlPage.Document.GetProperty("referrer").ToString();

Esta abordagem pode não funcionar dependendo do contexto da aplicação, ou seja, dependendo de onde você estiver tentando utilizar. Neste caso, peça ao Silverlight para buscar o endereço via javascript.

Em javascript, para pegar o endereço URL usamos location.href ( ex.:  alert(location.href); ), portanto, fazer o Silverlight recuperar o endereço através do javascript fica assim:


   string urlAddress;

   //Gets URL address of current page (javascript: location.href).
   urlAddress = System.Windows.Browser.HtmlPage.Window.Eval("location.href").ToString();

Qualquer dúvida, use o espaço para comentários! ;)

Como calcular HASH em C#

Durante o desenvolvimento de um software são várias as situações que requerem a utilização de um algoritmo capaz de calcular o código hash de uma informação. Essas situações podem ser, por exemplo:

  • Calcular o hash de um password;
  • Identificar de forma única um arquivo ou documento eletrônico;
  • Encontrar informações duplicadas;
  • Gerar chaves para algoritmos de criptografia;
  • Transformar uma grande quantidade de informações em uma pequena quantidade de informações;

Não pretendo entrar no mérito da questão ou explicar O QUE SÃO, COMO FUNCIONAM ou QUAL O MELHOR algoritmo para calcular hash, afinal, já existem ótimas referências pela Internet, como o portal NumaBoa e o Wikipedia. Ao invés disso, irei mostrar como desenvolver uma classe reutilizável para calcular hash em suas aplicações com qualquer algoritmo hash disponível no .NET Framework.

O segredo para que nossa classe seja genérica e consiga utilizar qualquer algoritmo hash já implementado no .NET Framework ou algum outro que venha a ser acrescentado é empregar, como base, a classe System.Security.Cryptography.HashAlgorithm. Todas as implementações de algoritmos matemáticos/criptográficos de hash do framework da Microsoft devem, obrigatoriamente, utilizar esta classe como base, o que faz com que possuam membros em comum, que são os que utilizaremos para generalizar na nossa classe reutilizável.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

    /// <summary>
    /// Class that calculates hash code.
    /// </summary>
    public static class Fingerprint
    {
    }

Notem que esta é uma classe estática (usamos a palavra static na declaração), portanto não será necessário instanciar a classe para utilizá-la.

Agora vamos criar o método “GetFingerprint” (pegar impressão digital), que irá retornar o código hash de um texto qualquer em formato string:


   public static string GetFingerprint(string text, System.Security.Cryptography.HashAlgorithm hashAlgorithm)
   {
       byte[] byteText;
       StringBuilder hexHash;

       if ((hashAlgorithm != null))
       {
           //Initializes crypto service provider.
           hashAlgorithm.Initialize();

           //Transforms text in array of bytes.
           byteText = text != null ? System.Text.UTF8Encoding.UTF8.GetBytes(text) : new byte[0];

           //Calculates the hash code of bytes.
           hashAlgorithm.ComputeHash(byteText);

           //Transforms hash code in hexadecimal string.
           hexHash = new StringBuilder();
           foreach (byte b in hashAlgorithm.Hash)
               hexHash.Append(b.ToString("x2"));

           //Releases all resources.
           hashAlgorithm.Clear();

           //Returns formatted hash code.
           return hexHash.ToString();
       }
       else
           //Hash code could not be calculated.
           return null;
   }

Este método recebe, como parâmetro, o texto que será codificado, juntamente com o provider do algoritmo que será utilizado para a transformação.

Seu funcionamento é bastante simples: O texto recebido é transformado em um vetor de bytes que, por sua vez, é passado ao algoritmo como a informação que será transformada em um hash. Uma vez calculado o hash, o resultado obtido é colocado no formato hexadecimal, convertido em um texto (string) e retornado.

Mas e se quisermos calcular o hash de um arquivo e não somente de uma informação em formato texto? Para atender esta necessidade, vamos criar uma sobrecarga para o método acima, que ficará assim:


   public static string GetFingerprint(string fileName, System.Security.Cryptography.HashAlgorithm hashAlgorithm, int bufferSize)
   {
       System.IO.FileStream fs;
       byte[] bufferIn = new byte[bufferSize];
       byte[] bufferOut = new byte[bufferSize];
       System.Text.StringBuilder hexHash;
       long finalBlockSize;
       if ((hashAlgorithm != null) && (bufferSize > 0))
       {
           try
           {
               //Open file for read.
               fs = new System.IO.FileStream(fileName, System.IO.FileMode.Open, System.IO.FileAccess.Read,
                   System.IO.FileShare.ReadWrite, bufferSize);
           }
           catch (Exception ex)
           {
               //File could not be opened.
               return null;
           }

           //Initializes crypto service provider.
           hashAlgorithm.Initialize();

           if (fs.Length > 0)
               while (fs.Position < fs.Length)
               {
                   if (fs.Position + bufferSize < fs.Length)
                   {
                       //Read bytes from file.
                       fs.Read(bufferIn, 0, bufferSize);

                       //Calculates the hash code of this bytes.
                       hashAlgorithm.TransformBlock(bufferIn, 0, bufferSize, bufferOut, 0);
                   }
                   else
                   {
                       //Calculates the size of last unread block.
                       finalBlockSize = fs.Length - fs.Position;

                       //Read last bytes from file.
                       fs.Read(bufferIn, 0, (int)finalBlockSize);

                       //Calculates the hash code of last bytes.
                      hashAlgorithm.TransformFinalBlock(bufferIn, 0, (int)finalBlockSize);
                   }
               }
           else
               hashAlgorithm.TransformFinalBlock(bufferIn, 0, 0);

           //Closes file.
           fs.Close();

           //Transforms hash code in hexadecimal string.
           hexHash = new StringBuilder();

           foreach (byte b in hashAlgorithm.Hash)
               hexHash.Append(b.ToString("x2"));

           //Releases all resources.
           hashAlgorithm.Clear();

           //Returns formatted hash code.
           return hexHash.ToString();
       }
       else
           //Hash code could not be calculated.
           return null;
   }

A sobrecarga receberá, como parâmetro, o caminho do arquivo, o algoritmo que fará o cálculo do hash e o tamanho do buffer que será utilizado. A escolha do tamanho do buffer é fundamental para definir a velocidade com que o algoritmo irá calcular a impressão digital do arquivo. A relação é diretamente proporcional: Quanto maior o buffer, mais rápido o cálculo. Porém a utilização de um buffer muito grande pode comprometer o sistema operacional, por isso é aconselhável a utilização de um buffer que seja suficientemente grande para não deixar o cálculo lento, mas que também não consuma muitos recursos do sistema operacional.

Agora a classe está pronta para ser utilizada, basta-nos chamá-la passando as informações desejadas e aguardar o retorno. Alguns exemplos de chamadas são:


   string md5Hash;
   string sha1Hash;
   string sha256Hash;
   string sha512Hash;

   //Calculates MD5 hash of file with 4KBytes buffer.
   md5Hash = GetFingerprint(@"c:\windows\notepad.exe", new
      System.Security.Cryptography.MD5CryptoServiceProvider(), 4 * 1024);

   //Calculates SHA1 hash of file with 8KBytes buffer.
   sha1Hash = GetFingerprint(@"c:\windows\notepad.exe", new
      System.Security.Cryptography.SHA1CryptoServiceProvider(), 8 * 1024);

   //Calculates SHA256 hash of file with 16KBytes buffer.
   sha256Hash = GetFingerprint(@"c:\windows\notepad.exe", new
      System.Security.Cryptography.SHA256CryptoServiceProvider(), 16 * 1024);

   //Calculates SHA512 hash of file with 32KBytes buffer.
   sha512Hash = GetFingerprint(@"c:\windows\notepad.exe", new
      System.Security.Cryptography.SHA512CryptoServiceProvider(),  32 * 1024);

Qualquer dúvida, comentário ou sugestão, utilizem os comentários… :)

Javascript: Área utilizável do navegador X Resolução da tela

Muitas pessoas têm encontrado este blog pesquisando, em ferramentas de busca, os seguintes termos:

  • como pegar o tamanho da tela do navegador em javascript
  • html tamanho da janela do navegador
  • jscript get browser window size
  • js width height size

Geralmente estas pessoas acabam sendo direcionadas para uma matéria que escrevi há algum tempo sobre como reconhecer o tamanho da tela do navegador e ajustar sua aplicação Silverlight 2.0 para ela. Embora essa matéria utilize como fundamento alguns comandos em javascript, nem todo mundo consegue filtrar dali o que realmente procura. E é justamente para estas pessoas que vou escrever esta nova matéria: Como recuperar o tamanho da tela do navegador em javascript.

Primeiramente precisamos entender a diferença entre conseguir a RESOLUÇÃO DA TELA do usuário e a resolução/tamanho da TELA DO NAVEGADOR do usuário.

  • Resolução da tela é, basicamente, a relação que representa a quantidade de pixels exibidos do ponto mais esquerdo do monitor até o ponto mais direito, bem como do ponto mais alto até o ponto mais baixo. Ex.: 800 X 600, 1024 X 768, 1280 X 1024, etc.
  • Tamanho da tela do navegador é também uma relação que representa uma certa quantidade de pixels, porém, da quantidade de pixels exibidos pela área utilizável do navegador.

A imagem abaixo explica melhor esta diferença, onde a seta maior indica a resolução da tela, enquanto a seta menor mostra a resolução da área utilizável pelo navegador:

resolution_browsersize

Notem que a seta menor (área utilizável do browser) também deixa de fora a barra de rolagem vertical (scrollbar). Ou seja, a lei da Impenetrabilidade da Física (dois corpos não podem ocupar o mesmo lugar no espaço) é válida também para nós ;)

Uma vez explicada a diferença entre as duas coisas, deduzimos que, para ajustar o conteúdo de um site ao navegador não iremos utilizar a resolução da tela, mas a área utilizável do navegador, o que deve ter ficado bem claro até aqui, correto? Bom, então vamos ao que interessa:

Para pegar a resolução da tela do usuário, via javascript:

<script type="text/javascript" language="javascript">

      alert('Width: ' + window.screen.width.toString());
      alert('Height: ' + window.screen.height.toString());

</script>

Para pegar o tamanho da área utilizável no navegador do usuário, via javascript:

<script type="text/javascript" language="javascript">

 alert('Width: ' + getWidth().toString());
 alert('Height: ' + getHeight().toString());

 function getWidth()
 {
      // Thiago Marotta Couto
      // thiago@isbyte.com
      // http://thiagocouto.wordpress.com/
      // December, 06 - 2008
      return window.innerWidth ? window.innerWidth : /* For non-IE */
             document.documentElement ? document.documentElement.clientWidth : /* IE 6+ (Standards Compilant Mode) */
             document.body ? document.body.clientWidth : /* IE 4 Compatible */
             window.screen.width; /* Others (It is not browser window size, but screen size) */
 }

 function getHeight()
 {
      // Thiago Marotta Couto
      // thiago@isbyte.com
      // http://thiagocouto.wordpress.com/
      // December, 06 - 2008
      return window.innerHeight ? window.innerHeight : /* For non-IE */
             document.documentElement ? document.documentElement.clientHeight : /* IE 6+ (Standards Compilant Mode) */
             document.body ? document.body.clientHeight : /* IE 4 Compatible */
             window.screen.height; /* Others (It is not browser window size, but screen size) */
 }

 </script>

O script acima foi testado em Internet Explorer 7 e anteriores, bem como em Firefox 3, Google Chrome e Safari. Caso encontrem alguma incompatibilidade, por favor me avisem!

Centralizar tela em Silverlight 2.0

Se você é iniciante no Silverlight 2.0, deve ter se perguntado como ajustar a tela para que fique do tamanho do navegador ou centralizada, correto?

Se você está apenas fazendo um controle em Silverlight e pretende utilizá-lo em uma página HTML, utilize o controle dentro de um elemento DIV centralizado, como explicado aqui e aqui, caso contrário, este post é pra você.

A idéia é simples: Como sempre, consiste em verificar o tamanho da área utilizável no browser do cliente (lembre-se que tamanho da tela é diferente de tamanho utilizável da tela) e redefinir o tamanho da sua aplicação.

Como o Silverlight não possui nenhuma API que retorna o tamanho da tela utilizável pelo cliente, vamos fazer isso pedindo ao Silverlight para executar um código JavaScript e pegar o seu retorno, como segue:

        public Page()
        {
            int screenWidth;
            int screenHeight;

            // Required to initialize variables
            InitializeComponent();

            //Auto adjust screen size.
            if (int.TryParse((System.Windows.Browser.HtmlPage.Window.Eval("document.body.offsetWidth").ToString()), out screenWidth))
                if (int.TryParse((System.Windows.Browser.HtmlPage.Window.Eval("document.body.offsetHeight").ToString()), out screenHeight))
                {
                    this.Width = screenWidth;
                    this.Height = screenHeight;
                }
        }

Notem que o código acima está inserido no handler do evento Page_Load para que a tela seja reajustada assim que a página carregar.

Agora é só criar um elemento Grid envolvendo o conteúdo que deseja centralizar e ajustar manualmente sua referência, para que fique centralizado na tela, como nas imagens abaixo (através da aba Layout do Expression Blend ou diretamente no XAML):

Silverlight 2 versão final!

Microsoft libera versão final do Silverlight 2.0. Para saber mais, acesse:
http://silverlight.net/GetStarted/

Como bloquear o teclado no navegador?

Durante o desenvolvimento de um sistema encomendado pelo jornal Estado de São Paulo, surgiu a necessidade de bloquear (cancelar) as teclas [TAB] e [F5] do browser em momentos onde uma determinada requisição demorasse excessivamente. A idéia inicial era, ajudado pela classe System.Web.UI.UpdateProgress do .NET Framework, exibir uma mensagem do tipo “Aguarde, carregando…”, como uma forma de dizer ao usuário para não clicar mais de uma vez no mesmo botão ficar tranqüilo, pois a requisição já estava sendo processada.

A solução, portanto, foi criar um elemento <div> com uma cor translúcida que ocupasse a tela inteira e estivesse sobre os demais elementos da página, dando a impressão de que os objetos HTML atrás dele estavam desabilitados. Como na imagem:

O elemento <div> impede, automaticamente, que os elementos do fundo sejam clicados, porém não impede que um usuário maldoso curioso utilize a tecla [TAB] para “andar” pelos controles do fundo e selecionar o botão clicado novamente e, em seguida, pressionar [ENTER], reativando o evento Click do ASP.NET ou OnClick do JavaScript.

Dessa forma era necessário impedir que estas teclas respondessem ao comando do usuário no momento em que o <div> estava sendo exibido. Para realizar tal feito desenvolvi uma função JavaScript que foi adicionada à página em questão. Segue a função para quem quiser utilizar:


<script type="text/javascript">

    document.onkeypress = CancelKeys;   /* For Opera, Firefox and Chrome */
    document.onkeydown = CancelKeys;    /* For IE */

    function CancelKeys(e)
    {
        // Thiago Marotta Couto
        // thiago@isbyte.com
        // http://thiagocouto.wordpress.com
        // September, 27 - 2008

        var e = e ? e : window.event;
        var key = (e.keyCode) ? e.keyCode : e.which;
        var keys = new Array(
                                116,    /* F5 */
                                9       /* Tab */
                            );

        for (i = 0 ; i < keys.length ; i++)
            if (key == keys[i])
            {
                try { e.preventDefault(); }
                catch(ex)
                {
                    e.returnValue=false;
                    e.keyCode=0;
                }
                return false;
            }
    }

</script>

Observações:

Esta função é cross-browser, ou seja, funciona independente do navegador. Foram testados: Internet Explorer 6 e 7, Firefox 3, Google Chrome, Opera e Safari. Caso encontrem alguma incompatibilidade, por favor me avise. ;)

Para funcionar com outros propósitos apenas removi a verificação que checa se a <div> está sendo exibida.

Além das teclas [F5] (116) e [TAB] (9), que estão sendo bloqueadas na função acima, você também pode adicionar outras teclas, bastando inserir no array keys (linha 10) o respectivo código ASCII (ex.: 13 para [ENTER]).

Edição (06/Dez/2008)

O leitor Cláudio apontou uma incompatibilidade neste script. Ele está bloqueando, adicionalmente, a tecla “t” (minúscula). Isso se dá porque o código ASCII da tecla “t” (minúscula) é 116, exatamente o mesmo da tecla F5. Todavia, a tecla “T” (maiúscula) funciona perfeitamente. Existem, além dessa, outras incompatibilidades, não do script, mas graças às colisões da tabela ASCII, como a tecla F6 e “u” (minúsculo), F7 e “v” (minúsculo) e assim sucessivamente… Vale lembrar que este problema é naturalmente corrigido pelo Internet Explorer, uma vez que ele não diferencia minúsculas de maiúsculas, enviando sempre o código ASCII das teclas maiúsculas, evitando assim qualquer tipo de colisão. Para verificar, basta remover a linha 02 do script, que passará a funcionar exclusivamente no browser da Microsoft.

GROUP BY com duas ou mais colunas no LINQ

Uma situação muito comum em sistemas que manipulam informações em bancos de dados é a necessidade de realizar uma consulta trazendo as informações agrupadas por mais de uma coluna ao mesmo tempo.

Mas… Você já tentou fazer isso com o LINQ? Bom, assim como quase tudo no nosso querido LINQ, embora a sintaxe seja parecida, não é idêntica à sintaxe SQL tradicional. Então aqui vai a dica para quem não quer gastar seu precioso tempo de desenvolvimento tentando descobrir como fazer (se é que já não gastou bastante tempo para chegar até aqui):

SINTAXE SQL TRADICIONAL:


SELECT
    Country,
    State,
    SUM(Population) Population
FROM Cities
GROUP BY County, State;


SINTAXE LINQ (C#.NET):


var Result = from c in Cities
             group c by new { c.Country, c.State } into g
             select new { Country = g.Key.Country,
                          State = g.Key.State,
                          Population = g.Sum(c => c.Population)
                          };

Criptoanálise: Uma arte ou um crime?

Muito provavelmente você já ouviu falar sobre Direito Autoral, isto é, direitos concedidos à autores de obras literárias, artísticas ou científicas, certo? Pois bem, o problema é que apenas ouvir falar sobre Direito Autoral não garante que você RESPEITE os tais direitos do autor, não é mesmo?

Calma! Não quero tornar esse blog um ponto de discussão sobre ética, moral e bons costumes, embora não tenha nada contra nenhum destes assuntos.

A questão aqui é que somente o conhecimento da existência de tais direitos dos autores não faz com que as pessoas deixem de baixar músicas em formato mp3 através da Internet, tirar foto-cópia de livros, imagens, fotos e outros documentos protegidos por direitos autorais. Por esta razão, empresas do ramo fonográfico, por exemplo, procuram descobrir, cada vez mais, técnicas que possam GARANTIR que os tais Direitos Autorais serão respeitados, isso é, que ninguém irá copiar (de forma não autorizada) uma de suas músicas protegidas pela lei, já que não é possível identificar e prender todos os criminosos.

A poderosa indústria cinematográfica, por sua vez, investiu milhões de dólares em um sistema de proteção antipirataria para o DVD, o disco digital criado para substituir o videocassete. Este sistema de proteção, assim como todos do ramo fonográfico, utiliza, dentre outras ferramentas, a criptografia. Considerando que tudo aquilo que for codificado pela criptografia não poderá ser decodificado por alguém sem autorização expressa, os sistemas de proteção antipirataria são um sucesso, certo?

Errado. O problema é que a coisa não é tão simples assim. Da mesma forma que existem indivíduos buscando o desenvolvimento contínuo da criptografia, existem, por outro lado, pessoas que buscam o desenvolvimento de algo chamado criptologia, uma disciplina científica que reúne e estuda os conhecimentos (matemáticos, computacionais, psicológicos, filológicos, etc.) e técnicas necessárias à criptoanálise, que, por sua vez, é a técnica de “quebrar” (decifrar) mensagens codificadas sem a autorização expressa do remetente. Resumindo: Existe a “polícia” e existe o “ladrão”. Existem pessoas que estão interessadas em desenvolver um método infalível para codificar qualquer informação, e existem pessoas que estão interessadas em desenvolver um método infalível para decodificar qualquer informação.

Bom, você ainda se lembra da poderosa indústria cinematográfica que investiu milhões de dólares em um sistema de proteção antipirataria para o DVD? Todo esse dinheiro foi para o lixo quando um garoto desenvolveu - e distribuiu pela Internet - um programa capaz de decifrar os tais códigos de segurança. Ele conseguiu copiar o conteúdo de um DVD para a memória de um computador, sem precisar comprar o disco nem pagar pelos direitos autorais. E como o garoto foi pego? Por uma batida policial realizada em 25 de janeiro, que transformou o norueguês Jon Johansen, de 16 anos, numa celebridade. Seus computadores e telefones celulares foram apreendidos e Johansen foi levado, junto com o pai, para interrogatório. O motivo: uma denúncia internacional vinda diretamente de Hollywood.

Se você estivesse trabalhando duro para desenvolver um sistema infalível de segurança, para que nenhum ladrão entrasse em sua casa, como você iria testá-lo? Ia esperar que um ladrão tentasse entrar? Se ele conseguisse entrar, você o mandaria para a prisão ou perguntaria como foi que ele conseguiu entrar, pra que você pudesse melhorar o seu sistema de segurança? E se o ladrão tivesse divulgado o segredo de como desarmar seu sistema de segurança na “Associação dos Ladrões de Casas”?

Dois meses depois da prisão de Johansen, em 4 de abril, um grupo de pesquisadores liderado por Robert Harley, do Instituto Nacional de Informática da França, anunciou ter decodificado uma mensagem cifrada em ECC - técnica testada para tornar invioláveis as ligações na telefonia celular digital. Só que a empresa responsável pelo ECC, a canadense Certicom, não pediu nenhuma prisão. Em vez disso, pagou um prêmio de 10 000 dólares aos autores da façanha.

Honrando a minha promessa de não tornar este blog um ponto de discussão ética, não cabe a mim realizar uma análise de qual foi a diferença que motivou atitudes tão diferentes entre os dois casos. O meu intuito foi apenas apresentar alguns conceitos e terminologias, além de demonstrar como é atual, importante e crescente o interesse na criptografia, a ciência inventada pelos chineses, empregada ao longo da história para proteger segredos políticos e militares. A velha disciplina, que quer dizer em grego “escrita escondida”, tornou-se um elemento crucial do cotidiano numa era em que um número crescente de indivíduos tem pelo menos duas senhas: uma para acessar sua conta bancária em caixas eletrônicos e outra para abrir seu e-mail.