quarta-feira, 6 de maio de 2015

AccessViolation em Interface

Olá pessoal.

Vamos falar de alguns cuidados que temos que ter ao usar interface para não deixarmos acontecer AccessViolation em rotinas que utilizem Interface.

Imaginamos a seguinte situação. Você tem uma interface que implementa uma rotina básica para salvar informações do banco de dados. No entanto você está salvando duas classes, TNotaFiscal e TItemNF, cada uma herdando diretamente de IClasseBase(Interface que implementa a rotina de salvar). Abaixo, o código fonte da Unit com a Interface e as classes  montadas.

unit UInterface;

interface

uses

  Vcl.Dialogs;

type

  // Interface que implementa o método Salvar que será usado nas demais classes.

  IClasseBase = Interface
  ['{7BAE5AE6-E856-4977-B156-71614AD72528}']
    procedure MetodoSalvarNaInterface;
  end;

  TNotaFiscal = class(TInterfacedObject,  IClasseBase)
    procedure MetodoSalvarNaInterface;
    function MetodoDaClasseNotaFiscal: string;
  end;


  TItemNF = class(TInterfacedObject,  IClasseBase)
    procedure MetodoSalvarNaInterface;
    function MetodoDaClasseItemNF: string;
  end;

implementation

{ TNotaFiscal }

function TNotaFiscal.MetodoDaClasseNotaFiscal: string;
begin
  Result := 'SalvarClasseNotaFiscal';
end;

procedure TNotaFiscal.MetodoSalvarNaInterface;
begin
  ShowMessage(MetodoDaClasseNotaFiscal);
end;

{ TItemNF }

function TItemNF.MetodoDaClasseItemNF: string;
begin
  Result := 'SalvarClasseItemNF';
end;

procedure TItemNF.MetodoSalvarNaInterface;
begin
  ShowMessage(MetodoDaClasseItemNF);
end;

end.

Agora no form principal, coloque um botão e deixe a Unit com o código o mais parecido com o da listagem abaixo:
unit fPrincipal;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, UInterface;

type
  TfrmPrincipal = class(TForm)
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    Nota: TNotaFiscal;
    Item: TItemNF;
    procedure ChamadaDeMetodoDaInterface(pClasseBase: IClasseBase);
    { Private declarations }
  public
    { Public declarations }
  end;

var
  frmPrincipal: TfrmPrincipal;

implementation

{$R *.dfm}

procedure TfrmPrincipal.Button1Click(Sender: TObject);
begin
  ChamadaDeMetodoDaInterface(Nota);
  ChamadaDeMetodoDaInterface(Item);
end;

procedure TfrmPrincipal.ChamadaDeMetodoDaInterface(
  pClasseBase: IClasseBase);
begin
  pClasseBase.MetodoEspecificoInterface;
end;

procedure TfrmPrincipal.FormCreate(Sender: TObject);
begin
  Nota := TNotaFiscal.Create;
  Item := TItemNF.Create;
end;

end.

Execute a aplicação, clique no botão, verifique as duas mensagens. Clique no botão novamente: BINGO!!!!. Um AccessViolation ocorreu. Correto?!

Entendendo o problema:
Um dos conceitos de interfaces é garantir o gerenciamento de memória dos objetos que os implementam. Os métodos _AddRef e _Release implementam esse gerenciamento, incrementam a contagem de referência sobre o objeto e destroem o mesmo quando o contador de referência é zero.

O erro ocorre porque ao final da execução do método a Interface automaticamente zera esse contador destruindo a referência sobre o objeto criado(somente a referência), no caso TNotaFiscal e TItemNF, e quando executamos novamente a referência não existe mais e então ocorre o AccessVIolation.

Contornando o problema de duas formas diferentes:
Exemplo 1:
Veja a rotina abaixo:
procedure ChamadaDeMetodoDaInterface(pClasseBase: IClasseBase);
Utilizando a reservada const na declaração do método, contornamos esse problema
procedure ChamadaDeMetodoDaInterface(const pClasseBase: IClasseBase);

O que é isso?
Quando passamos o parâmetro com a reservada const, o mesmo é passado por valor e não por referência, o que tira das mãos da Interface a destruição da referência deixando por conta do usuário implementar a rotina de liberação como na rotina abaixo:

procedure TfrmPrincipal.FormDestroy(Sender: TObject);
begin
  Nota.Free;
end;
Exemplo 2
Para não precisarmos nos preocupar com o const nem com o Free, declaramos as variáveis com o tipo da nossa Interface: IClasseBase. Com isso, deixamos a Interface gerenciar a destruição da referência e dos objetos relacionados. Lembrando que a criação das variáveis deve continuar sendo dos seus tipos específicos, ou seja:

Nota := TNotaFiscal.Create;
Item := TItemNF.Create;

Era isso o que tínhamos para o momento. Abraço a todos, dúvidas no final do post e até a próxima.

terça-feira, 28 de abril de 2015

FixInsight - Ferramenta para análise de código Delphi

Lambda, lambda, lambda Delpheiros!!!

Quando falamos em análise de código qual a ferramenta que vem a sua cabeça?
Existem várias ferramentas disponíveis  no mercado para análise de código fonte e para todos os tipos de linguagem, porém, temos uma falta delas para Delphi.

Vamos avaliar de forma simples e funcional uma ferramenta muito interessante para análise de código Delphi: FixInsight. Esta ferramenta é desenvolvida e mantida por  RomanYankovsky, MVP Embarcadero(Obrigado, Roman,  por ceder uma licença para uso), e pode ser encontrado na comunidade do G+ ou no seu perfil RomanYankovsky G+.

O que é FixInsight?
FixInsight é uma ferramenta para validação de sintaxeé capturar de forma clara e simplista warnings que podem vir a ser problemas nos seus métodos além de analisar convenção de tipo conforme o usuário tenha configurado além de ter alguns itens configurados conforme o padrão "Delphi" de desenvolvimento.

Após a instalação, que pode ser feita aqui, a ferramenta pode ser acessada utilizando o menu Project >> Run FixInsight.

Tela de configuração.


Podemos ver nessa janela duas divisões distintas: 

Conventions: Hoje temos um grande problema quando trabalhamos com diversos desenvolvedores pois cada um está adaptado a um tipo de escrita o que pode ser problemático para futuras manutenções e entendimento da equipe.
No meu ponto de vista, esse grupo de configuração é essencial e vem para diminuir essa disparidade entre os diversos perfis que encontramos em um mesmo código fonte.

Na minha opinião:

Ponto positivo: Podemos avaliar quase tudo o que está fora do padrão de desenvolvimento da empresa(métodos longos, muitas variáveis ou parâmetros, class fields, interfaces, Poiners , nome de classes e sentença with aninhada) impedindo até mesmo que o código seja submetido em um ambiente de versionamento caso esteja fora das regras estipuladas. 

Ponto negativo: Gostaria de ver outros itens nessa lista além de poder configurar todos ou a maioria dos seus itens. Um exemplo é iniciar um class field com a letra "F". Se a minha empresa como padrão por exemplo "FD", já não será possível mapear de forma completa o código fonte. Alguns itens ainda podem vir a ser melhorados em uma versão futura, e novos serem incorporados.


Warnings: Nesse tópico encontraremos erros ou possíveis erros em nosso código fonte. Eu me surpreendi quando vi a ferramenta rodar em cima de uma aplicação de legado. Existem coisas que a gente está habituado a fazer que atrapalham no desempenho e manutenção do software. A missão dessa divisão é encontrá-los, e isso ela faz muito bem.

Exemplo de uso:

Criei um projeto vazio e acrescentei alguns procedimentos conforme código abaixo:

unit Unit3;

  interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs;

type

  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
  procedure Teste(p1, p2, p3, p4, p5, p6, p7: string);
  function Get: string;
    { Public declarations }
  end;

var
  Form3: TForm3;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
var
  F: TForm;
begin
  F := TForm.Create(Self);
  F.Caption := 'Test';
  F.Tile;
  F.Scaled := True;
  F.ShowModal;
  FreeAndNil(F);
end;

function TForm1.Get: string;
var
  p1, p2, p3, p4, p5: string;
begin
  Result := p1 + p2 + p3 + p4 + p5;
end;

procedure TForm1.Teste(p1, p2, p3, p4, p5, p6, p7: string);
begin
// teste
end;
end.


Acesse o menu Project >> Run FixInsight. Clique no botão "Run".

O botão "Run" possui duas opções:
Analyze the entire project - Verifica as mensagens em todo o projeto.
Analyze current Unit only - Verifica as mensagens somente na unit ativa no momento.

Nota: Todos os resultados encontrados pela ferramenta são criados na lista de mensagens do Delphi, criando uma nova aba chamada FixInsight.

Resultado após rodar a ferramenta:


Entendendo o output:

C101 - A ferramenta estava configurada para ter no máximo 4 linhas de código e como estava com 6 linhas, a mensagem foi exibida.
C102 - Poderia ter no máximo 4 parâmetros em uma procedure e function.
C103 - Poderia ter no máximo 4 variáveis em uma procedure e function.
W519 - Método em branco. Mesmo tendo um comentário na procedure ele reconheceu como método em branco porque não tinha nenhuma linha compilável dentro da função.

Clique duplo em um dos itens e a IDE direciona para a linha referenciada na mensagem.

Linha de comando:

A ferramenta conta ainda com uma opção de linha de comando para processo de build ou integração contínua. Basta configurar alguns parâmetros para gerar um arquivo com todas as mensagens conforme a execução da ferramenta pela IDE. A diferença é que a saída vai ser em um arquivo.

Parâmetros para uso em linha de comando(CMD):
  --project=XXX.dpr (projeto a ser analisado)
  --defines=XXX;YYY;ZZZ (definições de compilação, Delphi)
  --output=XXX (resultado de saída em formato de arquivo)
  --settings=XXX.ficfg (sobrescreve as configurações do projeto)
  --xml (formatar o arquivo de saída como um arquivo .xml)


O parâmetro project é obrigatório, os demais são opcionais.
O parâmetro Defines, utiliza os mesmos parâmetros que o compilador do Delphi suporta, vide Docwiki.
Para usar a ferramenta por linha de comando basta utilzar a seguinte sintaxe:
 --project=c:\source\example.dpr


Ou ainda acessar o menu Iniciar >> FixInsight >> FixInsight Command Line. Informar os parâmetros e executar.




Considerações:
Em um modo geral, muito útil, fácil de usar e intuitiva.

Senti falta de alguns recursos. Gostaria de poder configurar mais opções da sessão Conventions como por exemplo a opção C107 Class field name should start with "F" que não pode ser alterada. Como a sessão é destinada a conversão de tipos, gostaria de colocar aqui a minha identidade para uso com o meu padrão de desenvolvimento.

Na sessão warnings gostaria de ver coisas como duplicidade de código ou itens de performance como duplicidade de Type Cast e uso do operador "as".

Não temos aqui todos os recursos do AQ Audits por exemplo, mas temos outras opções interessantes encontradas somente aqui.

Recomendado!!!

Bom pessoal, era isso por agora. Espero em um futuro próximo fazer um novo reviw com as novidades da ferramenta. Abraços e até a próxima.





quarta-feira, 25 de março de 2015

Postagem Tauras do Delphi

Boa tarde, pessoal.

Como eu já havia mencionado anteriormente, estamos postando artigos em um outro blog: Tauras do Delphi.

Segue link do artigo sobre Interface que acabei de publicar por lá.

Abraço a todos.

quinta-feira, 5 de março de 2015

Tauras do Delphi

Olá pessoal...

Fiquei afastado um tempo pois estava focado nos estudos para a certificação Delphi Master Developer, e isso toma um pouco do nosso tempo pois é muita coisa mesmo para estudar.

Outro fator foi que estamos trabalhando em um novo blog, eu e mais dois parceiros de Delphi(Alan e Maico), o blog se chama Tauras do Delphi...

Nesse blog iremos postar tudo de Delphi, desde coisas básicas até as novidades das versões atuais e também as demandas dos leitores.

Espero que vcs gostem desse novo blog. Estamos realmente focados em fazer ele andar.

Esse meu(100% Delphiano), irá continuar existindo para postar os links que publicaremos no Tauras do Delphi, então acostumem-se a usar esse novo link.

Nesse link postado por nosso amigo Alan, podemos ver como funciona os Padrões de Desenvolvimento(Design Patterns).

Grande abraço a todos e espero os comentários de todos lá no Tauras. Valeuuuuuu!!!!

terça-feira, 21 de maio de 2013

Webinar Firedac

Pessoal, estou postando um link com o webinar sobre Firedac, a nova tecnologia de acesso a dados utilizada pela Embarcadero.

https://www.embarcadero.com/br/products/rad-studio/firedac

Não deixem de assistir a esse vídeo, em breve estarei postando alguns artigos sobre o assunto. Abraços e até lá.

terça-feira, 2 de abril de 2013

Code Template



E ai galera... nesse artigo vou mostrar como criar templates para utilizar dentro de sua IDE.

Já experimentou escrever a palavra "For" no editor de código do Delphi e depois pressionar a tecla TAB, SPACE ou ENTER ??
Exatamente. O Delphi completa a estrutura de um loop "for I := 0 to List.Count - 1 do". Chamamos esse recurso de CodeTemplate.
Templates servem para agilizar a escrita de códigos corriqueiros em nossos sistemas. Quando você escreve Begin e pressiona ENTER, a palavra End é escrita automaticamente e o cursor do mouse fica entre o Begin..End, também temos aqui o uso de templates. O mais legal nisso tudo é que podemos escrever nossos próprios templates ou alterar os já existentes utilizando notação XML.
Normalmente os arquivos de template ficam salvos na pasta: 
C:\Program Files\Embarcadero\RAD Studio\7.0\ObjRepos\en\Code_Templates\Delphi 
O caminho pode variar de acordo com a sua versão do Delphi. O caminho exibido nesse artigo refere-se ao caminho encontrado no Delphi 2010.

Criando CodeTemplates para integrar com a IDE do Delphi.

Quantas vezes você precisa apresentar algum tipo de mensagem para o usuário do seu sistema? 
Quase que toda hora né... e ai o que tem que ser feito? Escrever e escrever repetidamente essas mensagens.      
Pois bem, vamos agilizar a escrita das mensagens que serão exibidas em nosso sistema:
Crie um novo documento texto no Windows (Todos sabemos como fazer isso).
Copie o conteúdo XML abaixo para esse documento.


<?xml version="1.0" encoding="utf-8" ?>

<codetemplate xmlns="http://schemas.borland.com/Delphi/2005/codetemplates"
version="1.0.0">
<template name="mbox" invoke="auto">
<point name="expr">
<script language="Delphi">
InvokeCodeCompletion;
</script>
<text>
Informe a Mensagem
</text>
<hint>
Conteúdo da Mensagem
</hint>
</point>
<point name="Caption">
<script language="Delphi">
InvokeCodeCompletion;
</script>
<text>
Título da Mensagem
</text>
<hint>
Informe o Título
</hint>
</point>
<description>
Application.MessageBox
</description>
<author>
Rafael
</author>
<code language="Delphi" context="methodbody" delimiter="|"><![CDATA[Application.MessageBox('|expr|', '|Caption|', MB_OK + MB_ICONINFORMATION);]]>
</code>
</template>
</codetemplate>



Salve o documento dentro da pasta de templates do Delphi com a extensão (.xml). Lembrando que o caminho varia de acordo com a sua versão do Delphi.

Abra o Delphi e no editor de código digite mbox e pressione TAB, veja o resultado. Se tudo deu certo algo como a linha abaixo deve ser visualizada:
Application.MessageBox('Informe a Mensagem', 'Título da Mensagem', MB_OK + MB_ICONINFORMATION);

Bem galera, com isso vimos um pouco sobre como utilizar templates para agilizar o nosso trabalho (não é preguiça de escrever é o jeito RAD de ser).
O arquivo é auto explicativo na sua grande parte, mas se precisarem de alguma ajuda para interpretar, entrem em contato que terei prazer em deixar as dicas. Grato a todos e bom uso.

quarta-feira, 13 de março de 2013

Debug remoto


   Quem nunca passou por uma situação em que na sua máquina a rotina executa normalmente e na máquina do cliente da erro? E ai? O que fazer? Bem, temos diversas "táticas" para validar essa situação como por exemplo fazer logs dentro das rotinas, ShowMessage e por ai vai. Agora, algo incrivelmente prático e correto é o Debug Remoto, isso mesmo que vocês estão pensando, acessar remotamente a máquina do cliente e debugar o sistema que está rodando na máquina dele. Como isso é possível? O Delphi, que é uma mãe pra nós, nos permite fazer isso de forma elegante e muito profissional utilizando ferrametas que acompanham o pacote de instalação.

   Primeiramente precisamos separar alguas DLLs que devem ficar na máquina remota em especial na pasta onde encontra-se o executável que será depurado remotamente. Essas DLLs podem ser encontradas na pasta Bin da instalação do Delphi normalmente em: C:\Program Files\Embarcadero\RAD Studio\7.0\bin. São os arquivos:


  • rmtdbg<nnn>.exe
  • bccide.dll
  • bordbk<nnn>.dll
  • bordbk<nnn>N.dll
  • comp32x.dll
  • dcc32<nnn>.dll - Win32
  • dcc64<nnn>.dll - Win64 
  • dccosx<nnn>.dll - Mac OS X


   <nnn> é o número da versão que vc está utilizando do Delphi. Para o RAD Studio XE3 por exemplo, o valor de nnn é 170.
Depois de adicionar na pasta os arquivos, precisamos registrar duas DLLs usando o regsvr32, bordbk<nnn>.dll e bordbk<nnn>n.dll.

   Agora vamos preparar nosso projeto para ser debugado remotamente.
   Acesse o menu Project --> Options --> Linking e altere a propriedade "Include remote debug symbols" para "True" conforme imagem.



   No Delphi pressione Shift+F9 para dar Build na sua aplicação. Nesse instante será gerado um arquivo com a extensão .rsm com o mesmo nome do executável. Esse arquivo nos possibilitará realizar o debug remoto. Agora envie para a máquina remota mais esses arquivos: o executável do sistema e o arquivo .rsm gerado após o Build.

   Na máquina remota, execute o arquivo rmtdbg<nnn>.exe. Será exibida uma tela do Prompt, deixe ela aberta.
Na sua máquina faça os seguintes passos:

1. Acesse o menu Run --> Load Process.
2. Selecione a opção "Remote".
3. Em "Remote Path" informe o caminho físico do executável na máquina remota, o Full Path mesmo: C:\Sistema\Sistema.exe, por exemplo.
4. Em "Remote Host" informe o IP da máquina remota.
5. Clique em "Load".

   Se tudo estiver configurado como informado nos passos acima a aplicação remota será executada e você poderá colocar seus Break Points a vontade no Delphi e verificar o que quiser e ainda com um plus: o ambiente testado é o ambiente real do cliente.

   Outra forma para realizar o Debug Remoto é acessando o menu Run --> Attach to Process, a diferença aqui é que você vai atachar um processo que já esteja executando na máquina remota. Para isso basta informar o IP da máquina remota, selecionar o processo e clicar em "Attach". O processo deve ocorrer da mesma forma porém nesse caso o aplicativo a ser Debugado já deve estar rodando.

Boa sorte na caça aos Bugs.