quarta-feira, 18 de abril de 2007

Aplicações Robustas II: Desenvolvendo a técnica


Esse artigo é continuação do post “Robustez no Delphi

Dando continuidade a seqüência de artigos sobre tratamento de exceções.....

A Estrutura “Try…Finally…End”:

Bloco protegido:
É impressindível garantir que, em tempo de execução, os recursos alocados por vc, na codificação de um módulo, sejam desalocados, mesmo ocorrendo um erro. Que recursos me refiro? Arquivos, memória (alocação dinâmica), recursos do Windows, objetos (instâncias de classes, que entram tb no quisito memória).
Para garantir a desalocação dos recursos, usamos a estrutura abaixo:


{Aqui vc aloca os recursos}
try
{Aqui temos comandos que usam os recursos}
finally
{Libera os recursos de forma gsarantida}
end;



Como já falamos antes, a aplicação sempre executará os comandos inseridos na parte Finally do bloco, mesmo que uma execeção ocorra. Quando um erro ocorre no bloco protegido, o programa pula para a parte “finally”, chamada de código limpo, que é executado. Mesmo que não ocorra um erro, estes comandos são executados.
No código a seguir, alocamos memória e geramos um erro, ao tentarmos uma divisão por 0. Apesar do erro o programa libera a memória alocada:


procedure TForm1.Button1Click(Sender:TObject);
var
ZnMyPointer : Pointer;
ZnInteger, ZnDividend : Integer;
begin
ZnDividend := 0;
GetMem(ZnMypointer, 512);
try
ZnInteger := 10 Div ZnDividend;
finally
FreeMem(ZnMypointer, 512);
end;
end;


A Estrutura ‘Try…Except…End”:

Com ela podemos tratar erros que ocorrem dentro de blocos protegidos. Para definir um tratamento de exceção, utilize a seguinte construção:


try
{Comandos que você deseja proteger}
except
{Comandos de tratamento de erros}
end;

Mais uma vez repassandoo que vimos no post anterior, o progrma irá executar os comandos na parte “except” somente se ocorrer uma exceção. Se na parte “try” você chamar uma rotina que não trata erros, e uma exeção ocorrer, em decorrencia de algum comando dentro do escopo dela, ao voltar para este bloco a parte “except” será executada. Uma vez que a aplicação localiza um tratamento para exceção ocorrida, os comandos são executados, e o objeto exceção é destruído e o fluxo do programa sai do bloco protegido. A execução continua se houver mais comandos após o bloco protegido até o fim do módulo.
Dentro da parte “except” definimos um código a ser executado para manipular tipos específicos de exceção. Um exemplo mais que batido, contudo didático, o código abaixo trata o erro de divisão por zero, através da exceção EDivByZero:


function ZnDivisao (const ZnSoma, ZnNum : Integer) : Integer;
begin
try
Result := ZnSoma div ZnNum;
except
on EDivByZero do Result := 0;
end;
end;

Falamos ainda a respeito da palavra reservada “on”. Ela define respostas para uma exceção. “on” está sempre junto de “do”; eu chamo de estrutura “on ..do, para manipular a exceção.
Para recuperar informações específicas sobre a exceção ocorrida, você usa uma variação da estrutura “on…do”, que provê uma variável temporária cujo o tipo pode ser quaisquer uma das classes derivadas de Exception, inclusive a própria. Nesse caso, você poderá personalizar tanto a string da mensagem da exceção, quanto a janela que exibirá a mensagem:


try
{* Comandos * }
except
on E: EInvalidOperation do
MessageDlg(‘Personalizando a exceção – Minha mensagem é ... a da exceção é:‘+E.Message,
mtinformation,[mbOk],0);
end;


Você deve cuidar para que, havendo mais de uma, as estruturas “on..do” estejam dispostas do tipo de exceção mais específico para o mais genérico como foi exemplificado no post anterior.



Técnicas avançadas

Tipo genéricos de Execções:
1° Caso: você pode utilizar as classes de exceções genéricas para tratar um erro, em vez de uma específica. Por exemplo, se você quer tratar um erro relacionado a uma operação com inteiros, mas não sabe exatamente o erro, poderá utilizar a exceção EIntError, que é a exceção genérica da qual derivam outras exceções relacionadas a inteiros:

function Divisao (const ZnSoma, ZnNum : Integer) : Integer;
begin
try
Result := ZnSoma div ZnNum;
except
on EDivByZero do Result 0;
on EIntError do
MessageDlg('Outro erro com inteiros aconteceu', mtError, [mbOk],0);
end;
end;


2° Caso: Você pode estruturar ainda um tratamento padrão de erro para tratar exceções que não tenham tratamentos especificados. Para isto, adicione uma parte “else” na parte “except” do bloco:



try
{Comandos}
except
on EintOverFlow do
{Código especifico para o primeiro tipo de erro]
on EintError do
{Código específico para o segundo tipo de erro}
else
MessageDlg('Um erro aconteceu, mas com certeza está relacionada com inteiros',
mtError, [mbOk],0);

end;


Toda técnica está fundamentada num conhecimento específico. No que tange o objetivo do artigo em questão, a pesquisa que apresento a seguir, considero ser alicerce para o refinamento da técnica de tratamento de exceções:


Exceções da RTL:
run-time library, as exceções geradas pela RTL estão definidas na unit SysUtils. Há sete tipos de exceções geradas pela RTL:


Exceções de conversão.
Exceções de entrada e saída.
Exceções de Hardware.
Exceções de memória heap.
Exceções matemáticas envolvendo inteiros.
Exceções matemáticas envolvendo pontos flutuantes.
Exceções de typecast.


Exceções de Conversão

Podem ocorrer quando usamos funções de conversão de um tipo para outro. A exceção chamada EConvertError é lançada quando isto ocorre, avisando que a conversão não pode ser feita. Exemplo:


var
ZnNumInteriro: Integer;
begin
ZnNumInteriro := StrToInt(Edit1.text);
end;


Dependendo do valor que possuir a propriedade “Text” do Edit1, poderá ocorrer uma exceção de conversão. Na vida real, posso garantir que o código exemplificado acima certamente irá, em algum momento, sofrer uma. Basta que não seja digitado nada no “Edit”.


Exceções de Entrada e Saída

Quando seu código tenta acessar arquivos ou dispositivos de entrada e saída.
A exceção genérica de entrada e saída, EinOutError, que contém um atributo, ErrorCode que indica o erro ocorrido.


Exceções de Hardware

Ocorrem em dois tipos de situação: quando o processador detecta uma falha, ou quando a aplicação gera uma interrupção intencional.
A Unit SysUtils define uma exceção genérica chamada EprocessorException, com as exceções derivadas:

 
Efault (Exceção Base),
EGPFault (Falha Geral de Proteção),
EstackedFault (acesso ilegal ao seguimento stack do processador),
EpageFault (O windows não está conseguindo realizar o swap),
EinvalidOpCode (O processador encontra uma instrução indefinida),
EbreakPoint (A aplicação gerou uma interrupção breakpoint),
Esinglestep (A aplicação gera uma interrupção single-step).


Exceções de Memória Heap

Quando alocamos memória dinamicamente podem ocorrer exceções. Para essa situação existem dois tipos de exceção: EoutofMemory (indica que não há memória suficiente) e EinvalidePointer (Ponteiro Inválido).


Exceções Matemáticas para Inteiros

Podem ocorrer quando se realiza operações com números inteiros. Está definida na SysUtils a exceção genérica EintError, com três tipos de exceção derivadas: EdividByZero (Divisão por zero), ErangeError (Número fora do intervalo, fora de faixa), e EintOverFlow (Estouro na operação com inteiro).


Exceções de Pontos Flutuantes

Quando se realiza operações com dados do tipo real. A SysUtils define uma exceção genérica chamada EMathError, e as seguintes exceções derivadas: EinvalidOp , EZeroDivide, EOverFlow , EUnderFlow.


Exceção de TypeCast

Para proteger seu código quando em tempo de execução vc precisa assumir que um determinado objeto possa ser de um tipo de classe. Caso ocorra um cast de tipo, ou seja, conversão de tipo, inválido, usando o operador de typecast “as” é gerada a exceção EinvalidCast. Exemplo:


(* para esse exemplo coloquei um edit e um button, palheta standard, num form. *)
procedure TForm1.Button1Click(Sender: TObject);
begin
(Sender as TBitBtn).Glyph.LoadFromFile('C:\Img.bmp');
end;


Basta que vc confunda a classe do botão, faça um cast de um TButton para um TBitBtn, pronto: “Invalid typecast”.



Exceções da VCL

Alguns exemplos de exceções definidas na VCL:

EDBEditError: Definida na unit “Mask”. Diz respeito a erros ocorridos com o uso do componente TDBEdit.


EDataBaseError: Gerado sempre que ocorre um erro no Banco de Dados

EDBEngineError: Gerado sempre ocorre um erro no BDE.

EPrinter: Gerado quando ocorre um erro com a impressora.

EDDEError: Gerado a aplicação não consegue encontrar um servidor DDE.

EMCIDeviceError: Gerado quando ocorre um erro ao tentarmos
acessar um dispositivo multimídia.

Exceções Silenciosas

A procedure Abort gera uma exceção do tipo Eabort. Isso, é claro, “abortará” a operação, sem exibir mensagem alguma.
O exemplo abaixo aborta a operação de inclusão de itens em um Listbox:
Inicie uma nova aplicação no Delphi:
Adicione os seguintes componentes da palheta standard: um Button (Button1: TButton) e um ListBox (ListBox1: TListBox).
Na seção “private” do Form1 declare um procedimento como no trecho de código abaixo:


type
TForm1 = class(TForm)
Button1: TButton;
ListBox1: TListBox;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
procedure ExcecaoSilenciosa;// Ao terminar de digitar, pressione “Ctrl+ Shift + C”
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.dfm}


A seção “type” do seu forme deve estar igual ao trecho acima. Conforme o comentário ao lado da procedure “ExcecaoSilenciosa”, quando vc pressionou “Ctrl+ Shift + C”, em resposta, o Delphi automaticamente criou o corpo da procedure. Portanto, vamos codificar, continue conforme o trecho a seguir:


procedure TForm1.ExcecaoSilenciosa;
const
ATexto = '%d° Elemetnto';
begin
Listbox1.Items.Add(‘Preparando para adicionar’);
//Estrutura de repetição, utilizada para adicionar os itens
Repeat
// adicionado os elementos
Listbox1.Items.Add(Format(ATexto, [ListBox1.Items.Count]));
// um teste louco pra forçar a exceção silenciosa
if (ListBox1.Items.Count mod 21 = 0) then Abort;
until ListBox1.Items.Count > 50;
end;


Cada vez que um item é adicionado na ListBox, a propriedade Count, da propriedade itens é incrementada. Ou seja, a propriedade “Count” retorna quantidade de itens que existem na ListBox.
Agora vamos codificar a chamada a esse procedimento e obviamente fazer o tratamento de exceção que vai dar o groove no nosso exemplo.

 
procedure TForm1.Button1Click(Sender: TObject);
begin
try
ExcecaoSilenciosa;
except
on Exception do
begin
Self.Color := clRed;
Self.Caption := 'Exceção Silenciosa!'
end;
end;

end;




Continuamos falando sobre este assunto no Estação ZN.

Nenhum comentário:

Postar um comentário

 
BlogBlogs.Com.Br