Delphi - Segredos e Solues

CAPTULO 7
 
Tratamento de Erros e Excees

Independentemente do cuidado que voc tem para depurar seu prprio
programa, na maioria das vezes ser impossvel antecipar os infortnios
comuns ou as coisas incrveis  que um usurio sem experincia far. Um
programa que vira de lado e morre quando a impressora fica sem papel ou
quando o usurio tenta abrir um arquivo inexistente  no ser muito
til. Se voc quer que seu programa seja robusto (algumas vezes chamado
"ter aptido para se degradar graciosamente") e no vire simplesmente de
lado,  voc vai querer evitar erros fatais. O Delphi utiliza a idia
natural de "proteger" blocos de cdigo. Isso significa que se um erro
resultar do processamento de  comandos em um bloco de cdigo, o programa
pode cuidar dele. Sempre que resultar um erro ao se processar uma linha
de cdigo, o Delphi cria ("gera"  o termo tcnico)  um objeto chamado
objeto de Exceo. Como voc vai ver mais adiante, neste captulo, 
possvel analisar propriedades desse objeto para determinar o que
provocou  o erro. (Essa informao pode dar a voc a aptido para
continuar onde o cdigo entrou em pane - sem acionar o erro novamente.
Por exemplo, voc pode dizer ao usurio  para colocar papel na
impressora ou alguma coisa do tipo!)

Blocos Protegidos

A maneira para ativar o sistema captura de exceo/erro dentro de um
dado bloco de cdigo  envolver o bloco com a combinao das
palavras-chave try/except. O cdigo  geral  algo assim:
        
        try
          {cdigo para fazer alguma coisa}
        except
        on ... do{cuida de excees especificadas};
        else {cdigo de tratamento padro};
        end;

        Uma vez que voc no quer que o Delphi "caia inadvertidamente"
no cdigo de captura de erro, o bloco try/except ajuda a manter um
cdigo mais limpo mantendo  o sistema de tratamento de exceo fora do
fluxo normal do programa.
        Ocasionalmente,  preciso ter certeza de que o Delphi processe
determinado cdigo independentemente do bloco que est protegido. (Isso
 bastante comum ao  tratar com cdigo que aloca memria ou recursos do
Windows - voc realmente quer recuperar 
         
        try
           {cdigo de alocao de recursos ir aqui}
        try
               {cdigo para tazer alguma coisa}
           except
              on ... do {trata exceo especificada};
              else {cdigo de tratamento padro};
           end;
         finally
                 {cdigo para liberar recursos e qualquer outra coisa
que precise finalmente ser arrumada}
         end;

        Aqui a palavra-chave finally inicia um bloco de cdigo que faz
uma limpeza final em si mesmo.
        Voc pode aninhar manipuladores de exceo envolvendo blocos de
cdigo cada vez maiores com mais combinaes try/except ou try/finally.
Se voc no tem um  manipulador de excees em um bloco de cdigo, ento
o Delphi olhar blocos das redondezas  no trata o erro que causou a
exceo, ento o Delphi eventualmente utilizar o manipulador de
excees padro - que apenas apresenta uma caixa de mensagem com o  que
ele acha ser o erro, antes que o programa morra.

NOTA: No ambiente de desenvolvimento, depois que um programa morre, voc
pode ter que utilizar Run(Program Reset. Em um programa independente, o
usurio volta ao  prprio Windows.

        Assim que voc inicia a captura de erro com um bloco try/except,
os erros no bombardearo mais o programa. De qualquer forma, o bloco
try/except deveria  incluir (ou transferir o controle para) um cdigo
que identifica o problema e, se possvel,  manipulador de erros padro
na parte else do bloco except. Quando o Delphi trata a exceo, o bloco
protegido termina. (Ao contrrio do Visual Basic, o Delphi no  devolve
o controle  prxima declarao que segue a declarao do cdigo
ofensor.)
        Entretanto, voc no pode corrigir um erro se no souber por que
ele aconteceu. Voc identifica o problema por meio da declarao on...do
do manipulador  de excees. Isso lhe d aptido para tratar de excees
especficas. Por exemplo, se voc es
        
        
        except
          on EDivByZero do Ret := 0;

ento voc tratou o problema do cdigo que tenta dividir por zero
estabelecendo em zero o valor da varivel Ret.

Excees da Biblioteca de Tempo de Execuo

Ao escrever um cdigo, freqentemente voc chamar rotinas na biblioteca
de tempo de execuo (RTL), como funes de converso de tipo ou
tratamento de arquivos.  Se alguma coisa d errado, a biblioteca de
tempo de execuo devolve um relatrio de erro ao seu aplicativo na
forma de excees. Voc pode definir seus prprios  manipuladores de
excees para que trate as excees RTL ou deixe a mensagem padro ser
exibida pelo seu aplicativo. Para tratar as excees RTL, voc tem de
compreender  o que so - de modo que veremos isso em seguida.


As Excees RTL (Biblioteca de Tempo de Execuo)

O Delphi pode identificar sete tipos de erros da biblioteca de tempo de
execuo (RTL). O tpico "What Are the RTL Exceptions?" ("O que So as
Excees RTL?") disponvel  no menu Contents na ajuda on-line
fornece-lhe a lista atual de erros, e voc pode at ir para explicaes
curtas do que poderia ter provocado um determinado erro.
       Os componentes promovero excees para indicar um problema  o
desafio consiste em identificar a causa. A maioria das excees dos
componentes resulta de  um erro de programao - como acessar um ndice
no-vlido em um vetor. O que segue  uma di

Excees Input/Output

As excees Input/Output (E/S) normalmente ocorrem quando a biblioteca
de tempo de execuo tenta acessar arquivos ou dispositivos de E/S. A
caixa de "I/O checking"  na pgina Compiler (ou equivalentemente, a
opo I+) tem de estar ativada para que o Delphi seja capaz de gerar
essa exceo. (O padro  estar ativada.)
        A maioria das excees de E/S deriva de problemas encontrados
pelo DOS e Windows ao tentar acessar um arquivo ou dispositivo externo.
A exceo E/S genrica  no Delphi  chamada EInOutError. Voc analisa o
campo ErrorCode desse objeto para determi

Excees do Heap

Excees Heap podem ocorrer quando voc tenta alocar ou acessar memria
dinmica. A prxima tabela mostra duas excees de heap possveis.

        Exceo - Significado

        EoutfMemory - No havia espao suficiente no heap para completar
a operao solicitada.
        ElnvalidPointer - O aplicativo tentou dispor de um ponteiro que
aponta para fora do heap. Normalmente, isso significa que o ponteiro j
foi desalocado.

Excees Matemticas de Inteiros

Excees matemticas de inteiros podem ocorrer quando voc realiza
operaes sobre expresses do tipo inteiro. A exceo matemtica
genrica de inteiro, chamada  EIntError, nunca  gerada pela biblioteca
de tempo de execuo, mas fornece uma base de onde derivam ("descendem"
 o termo tcnico) todas as excees matemticas  especficas inteiras.
A tabela que segue mostra as excees matemticas especficas inteiras,
cada uma das quais descende diretamente de EIntError.

        Exceo - Significado

        EdivByZero - Voc tentou dividir por zero. 
        ErangeError - O nmero ou a expresso estava fora de faixa. 
        EintOverFlow - A operao envolvendo inteiros extravasou.

Excees Matemticas de Ponto Flutuante

As excees matemticas de ponto flutuante podem ocorrer quando voc
realiza operaes sobre expresses que envolvem tipos de ponto flutuante
como Real. A exceo  matemtica genrica de ponto flutuante  chamada
de EMathError. Da mesma forma que com EIntError, essa exceo fornece
uma base de onde descendem todas as excees  matemticas especficas de
ponto flutuante. A tabela a seguir mostra as excees matemticas
especficas de ponto flutuante, cada uma das quais descende diretamente
de EMathError.

        Exceo - Significado

         ElnvalidOp - O processador encontrou uma instruo
no-definida.
        EzeroDivide - Voc tentou uma diviso por zero
        EOverflow - Aoperao de ponto flutuante extravasou.
         Eunderflow - A operao em ponto flutuante apresentou resultado
muito pequeno.

Excees de Converso de Tipo (TypeCast)

As excees Typecast podem ocorrer quando voc tenta converter um objeto
em um outro tipo utilizando o operador as. H apenas uma exceo
(EInvalid-Cast) para a  converso de tipos.

Excees de Converso

As excees de converso podem ocorrer quando voc converte dados de uma
forma para outra utilizando funes como IntToStr, StrToInt, StrToFloat
e assim por diante.  A nica exceo  chamada EConvertError.

Excees de Hardware

As excees de hardware podem ocorrer em dois tipos de situaes: ou o
processador detecta uma falha que no pode tratar, ou o aplicativo gera
intencionalmente uma  interrupo para interromper a execuo. O
tratamento de excees de hardware no  compilado em DLLs - apenas em
aplicativos independentes. A exceo genrica de  hardware  chamada
EprocessorException mas nunca  gerada pela biblioteca de tempo de
execuo. A EprocessorException fornece o objeto base de onde descendem
todas  as excees de hardware especficas. A tabela a seguir mostra as
sete excees de hardware especficas.

        Exceo - Significado

        Efault - Esse  o objeto Exception bsico de onde descendem
todos os objetos de falha.
        EGPFault - Esse  um defeito de proteo geral, normalmente
provocado por um ponteiro no-inicializado ou objeto.
        EstackFault - O programa fez um acesso ilegal ao segmento de
pilha do processador.
        EPageFault - O gerenciador de memria do Windows foi incapaz de
utilizar corretamente o arquivo de troca.
        ElnvalidOpCode - O processador encontrou uma instruo
no-definida. Isso normalmente significa que o processador estava
tentando executar dados ou memria  no-inicializada.
        Ebreakpoint - O aplicativo gerou uma interrupo de ponto de
parada.
        EsingIeStep - O aplicativo gerou uma interrupo passo simples.

Manuseando Classes de Excees

Conforme mencionado no comeo deste captulo, voc pode especificar
manipuladores de excees em torno de qualquer bloco de cdigo ou at
aninh-los dentro de outros  manipuladores de excees. Voc tambm pode
especificar um manipulador de excees para uma determinada classe de
exceo. Por exemplo:
        
        try
          (declaraes realizando operaes matemticas inteiras}
        except
          on ERangeError do {tratamento fora de faixa};
          on EIntError do (tratamento para outros erros matemticos de
inteiros};
        end;

        O cdigo anterior vai cuidar de qualquer erro matemtico de
inteiro ou de intervalo. Lembre-se de que se o Delphi aciona o
manipulador de excees para EIntError  antes da ocorrncia de um
ERangeError; o manipulador ERangeError nunca  acionado.

Regerando uma Exceo

Algumas vezes, ao tratar uma exceo em um bloco protegido, voc quer
tratar da exceo e depois pass-la para um bloco protegido maior. O
problema  que quando  seu manipulador local termina a manipulao da
exceo, ele destri a instncia de exceo, de modo que o manipulador
de exceo do bloco envolvente no tem nada  com que trabalhar. Voc
pode, porm, evitar que um manipulador de excees destrua a exceo.
Isso d ao manipulador envolvente oportunidade para responder. Para
isso voc utiliza a palavra-chave raise.
       Como exemplo de onde voc pode querer fazer isso, caso ocorra uma
exceo, voc pode querer exibir algum tipo de mensagem ao usurio e
depois deixar o manipulador  de excees envolvente tratar do problema.
A sintaxe  assim:
        
        try
          {declaraes iniciais}
          try
             (declaraes especiais}
          except
             on ESomeError do
             begin
               {tratamento apenas para as declaraes especiais}
               raise; {isto regerar novamente a exceo}
             end;
          end;
        except
          on ESomeError do ...; {tratamento que voc quer em todos os
casos}
        end;

        Se o cdigo na primeira parte ("declaraes iniciais") provoca
uma exceo, o Delphi executa apenas o manipulador de exceo da parte
externa (aquela marcada  "tratamento que voc quer em todos os casos").
Entretanto, se o cdigo no bloco indicado executar o tratamento de
exceo interior. Mas, em virtude da utilizao de raise no manipulador
de excees interior, o Delphi executar tambm o manipulador de
excees exterior.

DICA: Ao regerar excees, voc pode facilmente fornecer um tratamento
especial para casos especiais sem perder (ou duplicar) os manipuladores
existentes.

Protegendo a Alocao de Recursos

Qualquer aplicativo bem comportado deveria incluir o tratamento de erros
que libera recursos do sistema.

NOTA: Em qualquer momento em que voc aloca memria utilizando
ponteiros, ao criar um objeto ou utilizar arquivos, voc provavelmente
est utilizando recursos escassos  do Windows.

        Se o seu aplicativo tiver encontrado um erro grave e seu cdigo
tem de fechar o aplicativo,  uma boa idia liberar os manipuladores de
arquivo e qualquer  memria alocada dentro de seu aplicativo. (A
alternativa  que seu usurio ficar frustrado de Windows desaparecem -
de fato, o Windows pode at sofrer uma quebra. )
        Como exemplo, o cdigo que segue aloca um pouco de memria para
um ponteiro e uma lista de strings, pega um erro de diviso por zero, e
libera a memria  anteriormente alocada.

        procedure TForml,ButtonlCIickISender: TComponent);
            var
                 APtr: PChar;
           Anlnt, BadToDivide: Integer;
           MyStringList: TStrings;
        begin
            BadToDivide := 0;
          MyStringList := TStringList.Create;
          GetMem(APtr, 50000); ialoca 50000 bytes de memria}
             try
             Anlnt :=10 div BadToDivide; Iisto gera um erro)
             finally
                FreeMem(APtr, 50000); Ieste cdigo sempre  processado2
                MyString List.Free;
            end;

Excees Definidas pelo Usurio

O Delphi permite-lhe declarar um objeto do tipo Exception. Isso 
prtico especialmente para criar as suas prprias excees. Voc vai
utilizar o mesmo esquema de  tratamento de excees que aquele utilizado
pelo manipulador de excees da biblioteca de tempo de execuo. Duas
coisas precisam ser feitas para utilizar as suas  prprias excees -
primeiro voc tem de declarar um objeto de exceo e depois voc tem de
gerar a exceo.

Declarando um Objeto de Execuo (Exception)

Uma exceo  como qualquer outro objeto Delphi e dessa forma tem de ser
declarado antes de poder ser utilizado. Apenas um objeto do tipo objeto
Exception ser tratado  pelos manipuladores de excees padro. Por
exemplo:

        type
          EMyError = class(Exception);

        Se voc agora gera sua exceo EMyError configurada sem ter
criado um manipulador de excees especfico para EMyError, ento o
manipulador de excees padro  do Delphi cuidar de suas excees. Tudo
o que esse manipulador de exceo (padro) faz
        Por outro lado, toda a motivao para criar a sua prpria
exceo  utilizar essa informao dentro de seu aplicativo. Assim que
voc tiver declarado a sua  prpria exceo, utilize a palavra-chave
raise para gerar sua exceo, como no exemplo que

        type
          EMyError = class(Exception);

        if Algumacoisa <> Outracoisa then
          raise EMyError.Create('Gerando minha exceo';

        Quando o Delphi termina com um manipulador de excees, a
instncia de exceo  automaticamente destruda. Isso significa que
voc no precisa destruir  seu objeto de exceo para liberar a memria
que ele utilizou.

NOTA: A unidade System contm uma varivel chamada ErrorAddr que,
conforme implicado pelo seu nome, contm o endereo onde seu aplicativo
gerou a exceo.

Excees Silenciosas

Como voc j viu, o Delphi trata qualquer exceo para a qual voc no
escreve cdigo gerando o manipulador de excees padro - o que apenas
faz com que o Delphi  exiba uma caixa de mensagem. Ocasionalmente (por
exemplo, com um programa para usurios novos), voc pode querer criar
uma exceo que no exiba uma caixa de mensagem.  Isso  chamado de
exceo silenciosa. O procedimento Abort  utilizado com essa
finalidade. O objeto Abort descende do tipo de exceo EAbort.

NOTA: Exceto por EAbort, todos os outros tipos de exceo exibem uma
caixa de mensagem.

        Utilizar o procedimento Abort obriga o Delphi a sair do bloco
atual, mas no interrompe a execuo de seu cdigo.
