19
May 08

Delphi: CreateMutex

:: articles :: by Gilberto Saraiva

Camaradas,

O CreateMutex é uma função disponibilizada pela plataforma Windows ( API ) que cria uma sinalização a nível de processo na Kernel32, ou seja, você poderá criar uma sinalização mutex que lhe servirá para várias finalidades e uma das principais é impedir que o mesmo aplicativo seja executado duas vezes.

 Delphi |  copy code |? 
1
function CreateMutex(lpMutexAttributes: PSecurityAttributes; bInitialOwner: BOOL; lpName: PChar): THandle; stdcall;
2
{$EXTERNALSYM CreateMutex}

O exemplo abaixo impede a execução do mesmo aplicativo por mais de uma vez no mesmo computador:

 Delphi |  copy code |? 
01
program Project1;
02
 
03
uses
04
  Forms,
05
  Windows,
06
  Dialogs,
07
  Unit1 in 'Unit1.pas' {Form1};
08
 
09
{$R *.res}
10
 
11
var
12
  hMutex: THandle;
13
begin
14
  hMutex := CreateMutex(nil, true, PChar('MeuNomeUnico'));
15
  if (hMutex <> 0) and (GetLastError = 0) then
16
  begin
17
    Application.Initialize;
18
    Application.CreateForm(TForm1, Form1);
19
    Application.Run;
20
  end else
21
    ShowMessage('Aplicativo já está rodando.');
22
end.
23

Utilizando basicamente, o 3º parametro do CreateMutex é aonde informamos qual o nome único utilizaremos, para que na tentativa de criação da sinalização, a Kernel32 verifique se há alguma sinalização de nome igual ( A verificação é em case-sensitive ).

Já que vimos a utilização básica de uma sinalização a nível de kernel podemos entender como o sistema funciona.

  • Ao utilizar o CreateMutex, criamos uma sinalização mutex controlada e gerenciada pela Kernel32, ou seja, seu aplicativo em nada muda ao chamar esse API.
  • Como o controle de todos os processos é feito pela Kernel32, em hipótese de travamento de seu programa não há possibilidade da sinalização Mutex ficar retida, pois ao detectar que o processo foi extinto a Kernel32 retira a sinalização automaticamente.
  • Após criada uma sinalização Mutex, enquanto ela não for fechado ( Utiliza-se CloseHandle para essa finalidade ), a sinalização ficará de posse da primeira execução do aplicativo. Então quando executado pela segunda vez, ao tentar criar uma sinalização com o mesmo nome, a Kernel32 retorna um erro, aonde se é interpretado que a sinalização já existe e assim indicando que o processo já está executado.
  • A sinalização mutex ( Mutex Object ) poderá ser utilizada de várias formas, compreende-se em sinalização de controle de acessos à algum ponto ou objeto comum a mais de um processo ( Threads ).
  • A matéria é muito extensa, engloba Threads, Processos, Sinalização e Semaforização de acessos além da vasta acessibilidade de Eventos Kernel32, área que já implica no entendimento de Tokens Privileges. Ou seja, assunto para mais de um artigo :D .

    Abraço a todos



    19
    May 08

    Delphi: RegisterHotkey

    :: articles :: by Gilberto Saraiva

    Camaradas,

    O RegisterHotkey e uma função disponibilizada pela plataforma Windows ( API ) que cria uma interação com o objeto passado como parametro no momento que o usuário faz uma combinação de teclas, exemplo: CTRL+X. Resumindo, ele registra um objeto para ser notificado quando uma combinação de teclas é acionada.

     Delphi |  copy code |? 
    1
    {$EXTERNALSYM RegisterHotKey}
    2
    function RegisterHotKey(hWnd: HWND; id: Integer; fsModifiers, vk: UINT): BOOL; stdcall;

    O 1º parametro é o Handle do objeto que receberá a notificação quando a combinação de teclas selecionada for feita.
    O 2º parametro é um indice controlado pelo programador afim de possibilitar mais de um registro de combinação de teclas.
    O 3º e o 4º parametro são aonde o programador passa qual a combinação de teclas ele quer monitorar.

    Exemplo:

     Delphi |  copy code |? 
    1
    2
      RegisterHotkey(form1.Handle, 1, MOD_CONTROL or MOD_SHIFT, Byte(Ord('Q')));
    3
      RegisterHotkey(form1.Handle, 2, MOD_CONTROL or MOD_SHIFT, Byte(Ord('A')));
    4

    Aqui eu registro para o TForm form1 o recebimento da notificação no caso de CTRL+SHIFT+Q e CTRL+SHIFT+A.

    Mas ainda falta uma coisa, como já sabemos objetos Windows são notificados através de mensagens ( Message ) então temos que gerenciar a mensagem certa que o Windows utiliza para notificar o objeto quando a combinação de teclas é feita. Essa mensagem é a WM_HOTKEY, uma mensagem que o Delphi já mapeia para você como TWMHotKey, então é só escrever o seguinte código para gerenciar as notificações:

     Delphi |  copy code |? 
    1
    2
    type
    3
      TForm1 = class(TForm)
    4
      private
    5
        { Private declarations }
    6
      public
    7
        procedure WMHotKey(var Msg: TWMHotKey); message WM_HOTKEY;
    8
      end;
    9

    E no procedimento separa através da propriedade HotKey da var Msg

    Exemplo:

     Delphi |  copy code |? 
    01
    procedure TForm1.WMHotKey(var Msg: TWMHotKey);
    02
    begin
    03
      case Msg.HotKey of
    04
        1: begin
    05
          // Aqui eu recebi CTRL+SHIFT+Q
    06
        end;
    07
        2: begin
    08
          // Aqui eu recebi CTRL+SHIFT+A
    09
        end;
    10
      end;
    11
    end;

    É muito simples a utilização da API RegisterHotkey, outra parte interessante é a possibilidade de desregistrar a notificação, através da contra-API UnRegisterHotkey.

     Delphi |  copy code |? 
    1
    {$EXTERNALSYM UnregisterHotKey}
    2
    function UnregisterHotKey(hWnd: HWND; id: Integer): BOOL; stdcall;

    Baseando no nosso exemplo, desregistramos as 2 notificações assim:

     Delphi |  copy code |? 
    1
    2
      UnRegisterHotkey(Handle, 1);
    3
      UnRegisterHotkey(Handle, 2);
    4

    Simples não é?
    Qualquer dúvida fique avontade em perguntar.

    Abraço a todos



    19
    May 08

    Delphi: SendMessage

    :: articles :: by Gilberto Saraiva

    Camaradas,

    O SendMessage é uma função disponibilizada pela plataforma windows (API) que nos permite interagir sobre objetos Window, ou seja, todos os objetos que possuem Handle, seja esse handle de qual tipo for: HWND, HFILE, HMENU, HHOOK, HGLOBAL, HLOCAL, HINST e outros vários. Diante isso podemos entender que o acesso a certos objetos pode ser feito através da utilização dessa API.

     Delphi |  copy code |? 
    1
    {$EXTERNALSYM SendMessage}
    2
    function SendMessage(hWnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;

    Basicamente o SendMessage necessita apenas do Handle e da Mensagem ( Message ), os outros 2 parametros serão dependentes da Mensagem enviada.

    Um exemplo:

     Delphi |  copy code |? 
    1
      SendMessage(btn2.Handle, CN_COMMAND, BN_CLICKED, 0);

    Envia uma mensagem do tipo CN_COMMAND para o btn2 (no nosso caso esse é um TButton) com o parametro BN_CLICKED que faz o TButton btn2 entender que se deve executar o procedimento de click.

    Outro exemplo interessante:

     Delphi |  copy code |? 
    1
      SendMessage(form1.Handle, WM_SETTEXT, 0, Integer(PChar('Novo Título')));

    O código acima modifica o título do TForm form1 para o valor “Novo Título”.

    A infinidade de coisas que dá pra se fazer com essa pequena API é incalculável.
    Se quiser ver como é vasta a capacidade proponha-me um problema! Eu ficarei grato pelo desafio.

    Abraço a todos



    19
    May 08

    Delphi: FormatDateTime

    :: articles :: by Gilberto Saraiva

    Camaradas,

    FormatDateTime é um procedimento que retorna uma string de acordo com o formato passado como 1º parametro usando o 2º parametro que é a data.

     Delphi |  copy code |? 
    1
    2
    // SysUtils.pas [Delphi 7]:
    3
    function FormatDateTime(const Format: string; DateTime: TDateTime): string;
    4
    begin
    5
      DateTimeToString(Result, Format, DateTime);
    6
    end;

    Exemplo:

     Delphi |  copy code |? 
    1
    2
    begin
    3
      ShowMessage(FormatDateTime('dd/mm/yyyy', (Now + 1)));
    4
    end;
    5

    Resultará em uma mensagem contendo:
    ‘19/05/2008′

    Uma das grandes facilidade que ele traz é durante a interação com um banco de dados:
    Exemplo para o mySQL:

     Delphi |  copy code |? 
    01
    02
    begin
    03
      MyQuery.SQL.Text := Format(
    04
        'SELECT * FROM `log` WHERE `date` BETWEEN ''%s'' AND ''%s''',
    05
        [
    06
          // Data inicial
    07
          FormatDateTime('yyyy-mm-dd', (Now)),
    08
          // Data final
    09
          FormatDateTime('yyyy-mm-dd', (Now + 1))
    10
        ]);
    11
    end;
    12

    Sintaxes para formatação:

    Sintax Finalidade Resultado
    c Resulta na Data e hora com o formato ShortDateFormat + LongTimeFormat. (Se for exatamente meia-noite não irá mostrar a hora) 02/09/2008 07:04:01
    d Resulta no valor número que representa o Dia do mês ( sem 0 na frente ) 2
    dd Resulta no valor número que representa o Dia do mês ( com 0 na frente [00] ) 02
    ddd Resulta na abraviação do nome do dia da semana ter
    dddd Resulta no nome do dia da semana terça-feira
    ddddd Resulta na data formatada usando ShortDateFormat 02/09/2008
    dddddd Resulta na data formatada usando LongDateFormat terça-feira, 2 de setembro de 2008
    m Resulta no valor número que representa o mês ( sem 0 na frente ) 9
    mm Resulta no valor número que representa o mês ( com 0 na frente [00] ) 09
    mmm Resulta na abreviação do nome do mês set
    mmm Resulta no nome do mês setembro
    y Resulta no ano em dois digitos 08
    yyyy Resulta no ano em quatro digitos 2008
    h Resulta nas horas ( sem 0 na frente ) 7
    hh Resulta nas horas ( com 0 na frente [00] ) 07
    n Resulta nos minutos ( sem 0 na frente ) 4
    nn Resulta nos minutos ( com 0 na frente [00] ) 04
    s Resulta nos segundos ( sem 0 na frente ) 1
    ss Resulta nos segundos ( com 0 na frente [00] ) 01
    z Resulta nos milisegundos ( sem 0 na frente ) 31
    zzz Resulta nos milisegundos ( com 0 na frente [000] ) 031
    t Resulta na hora formatada usando ShortTimeFormat 07:04
    tt Resulta na hora formatada usando LongTimeFormat 07:04:01
    am/pm Reajusta a hora resultada para horários de 12 horas (manha e tarde) 07:04 am

    Abraço a todos



    19
    May 08

    Novo: http://gsaraiva.projects.pro.br

    :: news :: by Gilberto Saraiva

    Camaradas,

    Fiz a atualização para a versão 2.5 do WordPress, demorou um pouco para reescrever os plugins que fazem o que eu preciso, mas agora está tudo ok, alem de estar em português na área administrativa :) .

    Disponibilizei o RSS, o link está na lateral esquerda, alem do feedburner que facilita a vida de todo mundo.

    Então é isso, novas opções para quem quiser acompanhar os artigos e as novidades.

    Abraço a todos!



    19
    May 08

    jQuery: Os Seletores (Selectors)

    :: articles :: by Gilberto Saraiva

    Camaradas,

    Seguindo uma ordem mais amigável sobre o assunto, neste artigo vou falar sobre o seletores(Selectors) do jQuery, o responsável pelo funcionamento das requisições. Como disse no artigo passado fazer uma requisição objetiva resultará em um melhor resultado, pra cada caso você poderá escrever diversos algoritmos para obter o mesmo resultado.

    Antes de começarmos, complementando as informações do artigo anterior, preciso lembrar-lhes que HTML é escrito com uma hierarquia, ou seja, os objetos podem ter Pai e podem ter Filhos. Basicamente a única TAG que não tem Pai é a < HTML >, pois ela inicia o documento HTML. Mas então, seguindo esse raciocínio podemos entender que as possibilidades de requisições objetivas são grandes, não dependendo apenas das propriedades que a TAG possuí.

    Um exemplo comum, os valiosos formulários:

     HTML |  copy code |? 
    01
    02
    <form action="#" method="POST" id="CadNews" onSubmit="return CheckForm()">
    03
     <label for="UserName">Seu nome</label><input type="text" id="UserName"><br>
    04
     <label for="UserEmail">Seu e-mail</label><input type="text" id="UserEmail"><br>
    05
     <input type="submit" value="Cadastrar">
    06
    </form>
    07
    <script language="JavaScript">
    08
    <!--
    09
      CheckForm = function(){
    10
        UserName = $("form#CadNews input#UserName").val();
    11
        UserEmail = $("form#CadNews input#UserEmail").val();
    12
        if((UserName != '') && (UserEmail != '')){
    13
          return true;
    14
        }else{
    15
          alert("Dados incompletos");
    16
          return false;
    17
        }
    18
      }
    19
    //-->
    20
    </script>

    Aqui fazemos a checagem dos campos “Seu nome” e “Seu -email” para que se eles contiverem um valor diferente de vazio o form poderá ser submetido, caso contrário o form não efetuará processo algum além do usuário visualizar a mensagem: “Dados incompletos”.

    Veja que utilizamos alem do ID da Tag, fazemos uso também da estrutura, ou seja, além de procurarmos pelo ID, indicado pelo #, fazemos uso também da idenficação sequêncial, primeiro achamos o Form com o ID = CadNews e depois procuramos Input ID = UserName.

    Agora entendendo um pouco sobre os 3 Seletores (Selectors):

    Seletor de TagName [Ex: jQuery("DIV")]
    O Seletor de TagName consiste em definir o tipo de requisição como busca por Elementos que são da Tag especificada. Exemplo:
     HTML |  copy code |? 
    1
    2
    <div>Texto</div>
    3
    <script language="JavaScript">
    4
    <!--
    5
      jQuery("div").html("Texto alterado.");
    6
    //-->
    7
    </script>

    Então basicamente ele atua no TagName do elemento HTML.
    Seletor de ID [Ex: jQuery("#Status")]
    O Seletor de ID consiste em definir o tipo de requisição como busca por Elementos que contenham o ID igual ao especificado. Exemplo:
     HTML |  copy code |? 
    1
    2
    <div id="Status">Não modificado.</div>
    3
    <script language="JavaScript">
    4
    <!--
    5
      jQuery("#Status").html("Modificado.");
    6
    //-->
    7
    </script>

    Então basicamente ele atua nos Elementos que informam o ID, os que não informam não têm possibilidade de entrar no resultado da requisição por esse Seletor.
    Seletor de Classe [Ex: jQuery(".InputText")]
    O Seletor de Classe consiste em definir o tipo de requisição como busca por Elementos que são da classe informada. Exemplo:
     HTML |  copy code |? 
    1
    2
    <input type="text" class="InputText" name="Date" value="Sem valor definido">
    3
    <script language="JavaScript">
    4
    <!--
    5
      jQuery(".InputText").val("18-05-2008");
    6
    //-->
    7
    </script>

    Basicamente ele atua nos Elementos que pertencem a classe da requisição. Lembrando que um Elemento pode pertencer a várias classes.

    Agora uma pequena notação:

    Não é de hoje que, pra quem já desenvolve soluções para internet sabem, em CSS podemos selecionar todos os elementos atravez do * (asterisco), pois bem, no jQuery não é diferente. Como você devem ter notado ele atua atravez de requisições que por sua vez usa os seletores para os filtros, e que esse 3 seletores são identicos aos utilizados no CSS. O * (asterisco) não foge a regra do CSS, o uso é idéntico. Exemplo:

     HTML |  copy code |? 
    1
    2
    <div><span></span><div></div><div></div><span></span></div>
    3
    <script language="JavaScript">
    4
    <!--
    5
      jQuery("div *").html("Filho");
    6
    //-->
    7
    </script>

    O resultado será o preenchimento com a palavra “Filho” de todos os elementos pertencentes ao DIV.

    Espero que tenham entendido essa parte, pois ela é de suma importância no bom entendimento e utilização do jQuery.

    Próximo artigo:

  • Criando códigos facilitadores
  • Artigos jQuery relacionados:

  • Como iniciar a utilização do jQuery
  • Tudo que você precisa é o link
  • Os Seletores (Selectors)
  • Criando códigos facilitadores
  • Abraços a todos.



    17
    May 08

    jQuery: Tudo que você precisa é o link

    :: articles :: by Gilberto Saraiva

    Camaradas,

    Como tenho visto muita gente com problemas em relação a compatibilidade e outras coisas,
    decidi manter a minha estrutura em relação ao jQuery fixa, como vai funcionar?
    Vou manter o link baixo fixo:

     HTML |  copy code |? 
    1
    <script type="text/javascript" src="http://projects.pro.br/javascripts/jquery/"></script>

    Então pra você que está tendo dificuldade com o jQuery, você precisa somente de inserir esse código html na sua página e esquecer que ele existe, é só utilizar e ser feliz.

    Um exemplo:

     HTML |  copy code |? 
    1
    2
    <html>
    3
    <head>
    4
    <title></title>
    5
    <script type="text/javascript" src="http://projects.pro.br/javascripts/jquery/"></script>
    6
    <script language="JavaScript">
     Javascript |  copy code |? 
    01
    02
    <!--
    03
      jQuery(function($){
    04
        background = $.tocolor("#f99");
    05
        $.cssRule({"body":[
    06
          "{background:" + background.hexHTML() + "}",
    07
          {"span": [
    08
            "color",
    09
            {".italic": ["font-style","italic"]}
    10
          ]}
    11
        ]});
    12
     
    13
        $("span").css("position", "absolute");
    14
        $("span").css("left", "50%");
    15
      });
    16
    //-->
     HTML |  copy code |? 
    1
    2
    </script>
    3
    <body>
    4
    <span>texto <div class="italic">italic</div></span>
    5
    <div class="italic">no italic</div>
    6
    </body>
    7
    </html>
    8

    Veja que este código vai funcionar utilizando o jquery que estou disponibilizando.

    Vou mante-lo atualizado, e funcional, sempre, pois utilizo ele em outros lugares.

    Como este link possui além do jQuery alguns plugins utéis para mim e possivelmente para muitos de vocês, deixei uma forma para que você possa filtrar os plugins carregados:

    A lista de plugins (Vou manter essa lista sempre em atualizada)

    1. flydom
      Carrega o plugin FlyDOM.
    2. dimensions
      Carrrega o plugin dimensions.
    3. easing
      Carrrega o plugin jQuery Easing.
    4. cssRule
      Carrrega o plugin jQuery.CssRule.
    5. colors
      Carrrega o plugin jQuery.Colors.
    6. animator
      Carrrega o plugin jQuery.Animator.
    7. dynface
      Carrrega o plugin jQuery.dynface.

    Então para você obter o jQuery e algum desses plugins, a sintax abaixo podera ser alterada a seu modo:
    http://projects.pro.br/javascripts/jquery/?cssRule=1&colors=1
    Essa sintax carrega o jQuery e os plugins jQuery.CssRule e o jQuery.Colors.

    Outra maneira de não se perder com o passar do tempo é:

     Bash |  copy code |? 
    1
    http://projects.pro.br/javascripts/jquery/?flydom=0&dimensions=0&rule=0&easing=0&cssRule=1&colors=1&animator=0&dynface=0

    Essa sintax faz a mesma coisa da anterior, mas mantem os plugins que você tem disponíveis para uso mas sem carrega-los, sendo indicado por =0.

    Artigos jQuery relacionados:

  • Como iniciar a utilização do jQuery
  • Tudo que você precisa é o link
  • Os Seletores (Selectors)
  • Criando códigos facilitadores
  • Abraços a todos.



    16
    May 08

    jQuery: Gradient element focus

    :: articles :: by Gilberto Saraiva

    Folks,

    How to create a gradient effect on a focused control?
    Simple, the code below show you how to do that without troubles.

    OBS: I do use of jQuery.Colors and jQuery.CssRule plugins, that you can download at
    my open projects area

     Javascript |  copy code |? 
    01
    <script language="JavaScript">
    02
    <!--
    03
      NormClr = $.tocolor("#dddddd");
    04
      LightClr = $.tocolor("#ffa500");
    05
      GradFocusNormal = $.colorgrad(LightClr, NormClr, 10);
    06
     
    07
      $.cssRule({
    08
        "input, textarea, select": ["background", NormClr.hexHTML()],
    09
        ".gradfocus": ["background", NormClr.hexHTML()]
    10
      });
    11
     
    12
      // Gradiator class :: Create the gradient effect
    13
      function Gradiator(){
    14
        this.GradientFocusTimer = 0;
    15
        this.GradientFocusObj = -1;
    16
        this.work = function(AObj){
    17
          clearTimeout(this.GradientFocusTimer);
    18
          if(this.GradientFocusObj == -1){
    19
            this.GradientFocusObj = Math.random();
    20
            AObj.data("mem")[3] = this.GradientFocusObj;
    21
          }
    22
     
    23
          if(this.GradientFocusObj == AObj.data("mem")[3]){
    24
            $.cssRule(".gradfocus", [
    25
              "background", GradFocusNormal.grad(AObj.data("mem")[2]).hexHTML()
    26
            ]);
    27
     
    28
            if(AObj.data("mem")[2] < AObj.data("mem")[1]){
    29
              AObj.data("mem")[2]++;
    30
     
    31
              Self = this;
    32
              this.GradientFocusTimer = setTimeout(
    33
                function(){
    34
                  Self.work(AObj);
    35
                }, AObj.data("mem")[0]
    36
              );
    37
            }else{
    38
              this.reset();
    39
              AObj.removeClass("gradfocus");
    40
            }
    41
          }
    42
        }
    43
        this.reset = function(){
    44
          this.GradientFocusObj = -1;
    45
        }
    46
      }
    47
     
    48
      TheGradiator = new Gradiator();
    49
      $(function(){
    50
        $("#Demo").find("input, textarea, select").focus(function(){
    51
          Obj = $(this);
    52
          Obj.data("mem", []);
    53
          Obj.data("mem").push(1000 / GradFocusNormal.size())
    54
          Obj.data("mem").push(GradFocusNormal.size())
    55
          Obj.data("mem").push(0);
    56
          Obj.addClass("gradfocus");
    57
          TheGradiator.work(Obj);
    58
        });
    59
        $("#Demo").find("input, textarea, select").blur(function(){
    60
          $(this).removeClass("gradfocus");
    61
          TheGradiator.reset();
    62
        });
    63
      });
    64
    //-->
    65
    </script>

    The code can produce the same effect I create below:

    Hugs for all.



    16
    May 08

    Arte em cédulas?!?!

    :: news :: by Gilberto Saraiva

    Camaradas,

    Navegando aqui no tempo vago que tive aqui na empresa, achei uma coisa muito interessante,
    talvez uma falta de ter o que fazer, como eu estou aqui no momento, mas vamos lá:

    Algumas que eu mais gostei:
    Sparta:
    Spartan

    Banana macarena (Quem não lembra dessa banana nos primórdios da internet?):
    Banana macarena

    Tartaruga ninja:
    Tartaruga ninja

    Super Mario:
    Super Mario

    Veja mais em:
    http://www.flickr.com/photos/joefxd/sets/72157604423778692/

    Tem doido pra tudo nesse mundo!

    Abraços a todos!



    15
    May 08

    Participe do BHCode

    :: news :: by Gilberto Saraiva

    Camaradas,

    Não é de hoje que venho tendo idéias sobre reunir comunidades de desenvolvedores, minha proposta inicial foi o DevPartners mas ainda não deslanchou em matéria de participantes desenvolvedores, só tem usuários por enquanto, o único desenvolvedor lá sou eu, mas isso não vem ao caso, o importante aqui é divulgar a outra parte da história.

    Pra quem não sabe, em alguns lugares do mundo temos grupos de programadores que se reunem através do Coding Dojo, aqui em Belo Horizonte não foi diferente, tivemos o Coding Dojo, muito animador por sinal, mas uma inesperada baixa no time tirou a possibilidade de continuação(Vou deixar bem claro que a baixa não representa morte, e sim a mudança de cidade de um dos idealizadores). Pois bem, muitos, como eu, gostaram da proposta e aprenderam muito, além da diversão, e assim decidimos criar o BHCode. O nome meio que saltou da minha cabeça pro msn quando estava falando com o Lucas, ai o Marcelo aprovando também o nome iniciamos a divulgação. O Blog já existia, e tinha sido criado das idéias das primeiras reuniões administrativas sobre o assunto.
    Eu desenvolvi um timeline que vai servir para descrever os passos do BHCode em carater de documentação da história do mesmo.

    Depois dessa história longa sem mais delongas, acesse:

    BHCode Logo
    http://www.portalprogramacao.org/blog/

    Abraços a todos.