O tratamento de exceção é um mecanismo capaz de dar robustez a uma aplicação, permitindo que os erros sejam manipulados de uma maneira consistente e fazendo com que a aplicação possa se recuperar de erros, se possível, ou finalizar a execução quando necessário, sem perda de dados ou recursos. Para que uma aplicação seja segura, seu código necessita reconhecer uma exceção quando ela ocorrer e responder adequadamente a esta. Se não houver tratamento consistente para uma exceção, será exibida uma mensagem padrão descrevendo o erro e todos os processamentos pendentes não serão executados. Uma exceção deve ser respondida sempre que houver perigo de perda de dados ou de recursos do sistema.
![]() No exemplo acima, poderia-se clicar no botão OK e continuar a executar a aplicação. Mas muitas vezes o erro ocorrido impede a continuação da operação, ou leva a perda de informações valiosas. O Ideal seria que se pudesse tratar estes erros ocorridos, evitando a perda de dados ou a necessidade de encerrar a aplicação. Além de tratar o erro, a rotina de tratamento de erros poderia enviar ao usuário uma mensagem em português , mais significativa. A forma mais simples para responder a uma exceção é garantir que algum código limpo é executado. Este tipo de resposta não corrige o erro, mas garante que sua aplicação não termine de forma instável. Normalmente, usa-se este tipo de resposta para garantir a liberação de recursos alocados, mesmo que ocorra um erro. O tratamento mais simples seria uma simples mensagem ao usuário com a proposta dele tentar novamente a execução da operação que tenha causado o erro, conforme podemos ver no exemplo abaixo:
![]()
Outra forma de se responder a uma exceção é tratando o erro , ou seja, oferecendo uma resposta específica para um erro específico. Ao tratar o erro destrui-se a exceção , permitindo que a aplicação continue a rodar, maas o mais ideal mesmo é que além da mensagem, coloque os procedimentos devidos que sua aplicação deverá fazer caso ocorra uma exceção. Um exemplo disto é uma operação de gravação em uma tabela, caso ocorra um erro, ela deverá ser cancelada. Existe também a prática da geração de logs que gravam no disco, um arquivo texto contendo todos os dados possíveis do erro ocorrido de forma que possa chegar a causa o mais rapido possível.
Exceções são classes definidas pelo Delphi para o tratamento de erros. Quando uma exceção é criada, todos os procedimentos pendentes são cancelados e, geralmente é mostrada uma mensagem de erro para o usuário. As mensagens padrão nem sempre são claras, por isso é indicado criar seus próprios blocos protegidos.
Bloco protegido é um área em seu código que é encapsulada em uma instrução que ao invés de executar a linha de código ele tentará executá-la. Se conseguir beleza, prossegue com o programa, se não conseguir então ele cria uma resposta a este insucesso em um bloco de código resguardando de erros que podem parar a aplicação ou perder dados. Um bloco protegido começa com a palavra reservada try e termina com a palavra reservada end. Entre essas palavras determina-se os comandos protegidos e a forma de reação aos erros. Quando se define um bloco protegido, especifica-se respostas a exceções que podem ocorrer dentro deste bloco. Se a exceção ocorrer, o fluxo do programa pula para a resposta definida, e após executá-la, abandona o bloco. Um bloco protegido é um grupo de comandos com uma seção de tratamento de exceções.
try
A aplicação irá executar os comandos na parte except somente se ocorrer um erro. Se na parte try chamar uma rotina que não trata erros, e um erro ocorrer, ao voltar para este bloco a parte except será executada. Uma vez que a aplicação localiza um tratamento para a exceção ocorrida, os comandos são executados, e o objeto exceção é destruído. A execução continua até o fim do bloco. Dentro da parte except define-se um código a ser executado para manipular tipos específicos de exceção. Algumas vezes você pode precisar especificar quais exceções quer tratar, como mostrado abaixo.
try
O Delphi define muitas exceções, para cada erro existe uma exceção correspondente.
Blocos de finalização são executados sempre, haja ou não uma exceção. Geralmente os blocos de finalização são usados para liberar recursos. Entre estes recursos estão : arquivos,
memória, recursos do windows, objetos.
FrmSobre := TFrmSobre.Create(Application);
Você pode usar blocos de proteção e finalização aninhados
FrmOptions := TFrmOptions.Create(Application);
A aplicação sempre executará os comandos inseridos na parte finally do bloco, mesmo que uma exceçã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, foi alocada memória e gerado um erro, ao tentar-se a divisão por 0 (zero). Apesar do erro, o programa libera a memória alocada:
Procedure Tform1.Button1click (Sender : Tcomponent );
Você pode provocar uma exceção usando a cláusula raise.
raise EDatabaseError.Create('Erro ao alterar registro.');
Também é possível criar seus próprios tipos de exceções.
type
raise EInvalidUser.Create('Você não tem acesso a essa operação.');
Se você quiser que uma exceção continue ativa, mesmo depois de tratada, use a cláusula raise dentro do bloco de tratamento da exceção. Geralmente isso é feito com exceções aninhadas.
A exceção EDBEngineError permite a identificação de erros de bancos de dados gerados pela BDE.
Note que a variável E, que vai identificar o erro, só precisa ser declarada no bloco de tratamento da exceção. No help você pode consultar outras propriedades de EDBEngineError que podem ser importantes. Você também pode usar os eventos de erro do componente Table, sem precisar de blocos de tratamento.
procedure TFrmCadCli.TblCliPostError(DataSet: TDataSet; E: EDatabaseError; var Action: TDataAction);
Alguns códigos de erro da BDE estão listados abaixo. Todas as constantes e funções relacionadas à API da BDE no Delphi 3 estão na Unit BDE, que deve ser adicionada à cláusula uses. No BDE API Help você pode encontrar referência sobre as funções nativas da BDE, como também alguns exemplos em Delphi.
Se você quiser mais informações a respeito do erro pode usar o procedimento DBIGetErrorContext, como na função mostrada abaixo que retorna determinadas informações sobre o erro.
function GetErrorInfo(Context: SmallInt): string;
No evento OnEditError, usado no exemplo abaixo, se ocorrer um erro ao tentar alterar um registro, podemos identificar o usuário da rede que está alterando esse registro usando a função criada anteriormente.
if Pos('locked', E.Message) > 0 then
Note que foi usada uma outra técnica de identificação do erro, usando a própria mensagem de erro e não o código, como mostrado anteriormente. Você pode usar a função criada acima mandando como parâmetro os valores mostrados abaixo, que podem ser encontrados no help da BDE.
Para desenvolver um sistema genérico de tratamento de erros, considere a opção de criar esse tratamento em um DataModule genérico para ser usado como ancestral por todos os DataModules do sistema, utilizando a herança visual. Se o único problema for traduzir as mensagens, localize os arquivos CONSTS.INT e DBCONSTS.INT e crie uma nova Unit de definição de strings com uma estrutura semelhante a mostrada abaixo e juntando todas as definições das constantes das duas Units devidamente traduzidas. Depois, basta usar essa Unit em seus projetos que as novas mensagens irão sobrepor as anteriores.
unit NewConsts;
interface
resourcestring
SAssignError = 'Não é possível atribuir %s a %s';
implementation
end.
Uma outra opção seria criar um método para o evento OnException do objeto Application, esse método seria chamado sempre que houvesse uma exceção em qualquer parte do sistema.
Quando se escreve código chama rotinas da biblioteca de run-time ( RTL, run-time library), como funções matemáticas ou de manipulação de arquivos, os erros aparecem na forma de exceções. Por padrão, a RTL manda uma mensagem para o usuário, mas pode-se tratar estes erros de outra forma. As exceções geradas pela RTL são definidas na unit SysUtils, e todas descendem da classe mais geral Exception, que provê a string que aparece no quando de mensagem da exceção. Há sete tipos de exceções geradas pela RTL : - Exceções de entrada e saída;
Observa-se estas exceções com mais detalhes através do OBJECTBROWSER, ativado através do comando View/Browser: Exceções de Entrada e Saída Ocorrem quando RTL tenta acessar arquivos ou dispositivos de entrada e saída. A Unit SysUtils define uma exceção genérica de entrada e saída chama EinOutError, que contém um atributo chamado ErrorCode que indica o erro ocorrido. Exceções de Memória Ocorrem quando se tenta alocar ou acessar memória dinâmica. São definidos 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 Ocorrem quando se realiza operações com números inteiros. A Unit SysUtils define uma exceção genérica chamada EintError, com três tipos de exceção derivadas : EdividByZero (Divisão por zero), ErangeError (Número fora da região), e EintOverFlow (Estouro na operação com inteiro). Exceções de Pontos Flutuantes Ocorrem quando se realiza operações com dados do tipo real. A Unit bSysUtils define uma exceção genérica chamada EMathError, com as seguintes exceções derivadas : EinvalidOp ( Processador encontrou uma instrução indefinida), EZeroDivide (Divisão por zero), EOverFlow, EUnderFlow. Exceções de TypeCast Ocorrem quando você tenta atribuir um objeto a um tipo inválido usando o operados as. Gera-se a exceção EinvalidCast quando isto ocorre. Exceções de Conversão Ocorrem quando se convertem dados de um tipo para outro. A Unit SysUtils define uma exceção chamada EconvertError quando isto ocorre, avisando que a conversão não pode ser feita. Exceções de Hardware Podem ocorrer 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 derivada: Efault (Exceção Base), EGPFault (Falha Geral de Proteção), EstackedFault (acesso ilegal ao seguimento stack do processador), EpageFault (o gerenciador de memória do windows não está conseguindo realizar o arquivo de 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).
Os componentes da VCL (Visual Component Library) também geram exceções diversas relacionadas com cada controle em particular. Como a VCL é constituída de componentes que nada mais são do que classes, a exceção mais comun nos VCL's é a famosa Access Violation, que nada mais é do que uma exceção ocorrida quando a sua aplicação tenta acessar ou usar o objeto inválido. Mas este "objeto inválido" pode ser muitas coisas: Classe inválida Ocorre quando você destroi um objeto, mas no fluxo em sua aplicação ele será acessado posteriormente. Ai ocorre o erro que costuma abortar a aplicação. Ponteiro inválido Ocorre quando você cria uma variável do tipo ponteiro e que está apontando para um endereço referente à uma área protegida da memoria ou um endereço inválido. Esta invalidez do endereço pode ser causada pelo fato de outra aplicação estar usando-o ou então pelo fato dele não existir mesmo. A mensagem de erro da Access Violation é mais detalhada pois além de trazer a mensagem ainda traz tanto o endereço lido como o endereço onde ocorreu a falha, como podemos ver na figura abaixo:
![]()
Convem salientar também que a VCL faz uso das Exceções da RTL. Sempre que uma exceção em um componente ocorrer e esta for catalogada na RTL ela automaticamente será chamada. Você pode tratar as exceções da VCL da mesma forma que as demais exceções, e seu programa não precisa ser abortado para tal. Basta usar os blocos protegidos nos pontos críticos. Normalmente estes pontos costumam ser operações que envolvam mudanças em estados de componentes em Run-Time, ou mudança ou passagem de valores em variáveis do tipo ponteiro. Ex.1 - Tratando uma mudança no estado de um componente em Run-Time
procedure TForm1.Button2Click(Sender: TObject);
Ex.2 - Tratando uma passagem de valor em um ponteiro
procedure TForm1.Button2Click(Sender: TObject);
Pode-se definir exceções que não mostrem um quadro de mensagem para o usuário quando aparecem. São chamadas exceções Sileciosas. O caminho mais curto para criar esta exceção é através da procedure Abort. Esta procedure automaticamente gera uma exceção do tipo Eabort, que abortará a operação sem mostrar uma mensagem. O exemplo abaixo aborta a operação de inclusão de itens em um Listbox quando tentamos inserir o terceiro elemento:
Begin
O Delphi oferece um evento chamado OnException, ligado à a classe Tapplication, que permite manipular qualquer exceção que ocorra em seu programa, mesmo que não sabendo em que parte do programa ela ocorreu. Inicialmente, deve-se criar manualmente, como um método de Tform1, a chamada para o método que tratará os erros :
Tform1 = Class (Tform)
Depois de declarar a procedure, deve-se atribuí-la ao evento OnException de Tapplication. A atribuição é feita no evento OnCreate do formulário principal :
Procedure Tform1.FormCreate ( Sender : Tobject );
O método Trata_Erros será agora chamado quando qualquer exceção ocorrer. Pode-se determinar mensagens para erros específicos, ou mensagens gerais :
Procedure Tform1.Trata_Erros (Sender : Tobject; E : Exception);
A bem da verdade as exceções não tratadas ou mal tratadas em uma aplicação são as uma das causas do GPF (Falha geral de proteção) que ocorrem no Windows e que geralmente o derrubam. Como isto acontece? Se seu programa conseguir sobreviver à uma exceção sem que ele tenha caído (O que não é muito difícil em se tratando de exceções silenciosas), ele pode perder o seu endereçamento da memória. Daí começar a acessar uma área protegida, ou uma área que está sendo usada por outro programa, é questão de tempo. Um problema destes, normalmente acaba com uma mensagem de erro do Windows que fecharia tanto este aplicativo como os outros envolvidos no problema e em seguida, uma simples reinicialização da máquina volta ao normal. Mas podem ocorrer problemas mais sérios como uma colizão com a FAT, o que resultaria na perda de todo ou parte do conteúdo do HD. Obviamente que um caso destes é muito raro de acontecer, mas que existe a probabilidade existe! O GPF (General Protection Faults) é um evento de hardware, causado pela própria CPU. No Windows ocorrem basicamente por culpa dos aplicativos, que não possuem um tratamento consistente de suas exceções internas. Como consequência deste problema pode ocorrer os seguintes eventos: - Tentativa, pela aplicação,de acessar memória fora da área determinada pelo sistema operacional;
Depois de ler este artigo, voce pode concluir que não deve, sob nenhuma circunstância, negligenciar o tratamento de exceções dentro de sua aplicação uma vez que que sempre que elas ocorrem, acabam afetando não só a esta referida aplicação mas como outras ainda. Um exemplo disto é o caso de uma aplicação que dá um lock em uma tabela no banco de dados e em seguida tentará abrí-la. Caso ocorra uma exceção que aborte o programa, a aplicação fecha sem que nada ocorra com o Windows. Mas a Tabela está lá no banco bloqueada e, conforme for o SGDB, nem um Restart ou na maquina ou no banco conseguirão desbloqueá-la, causando transtornos e perda de tempo tentando solucionar um problema que poderia ter sido evitado perfeitamente. Outro exemplo disto, é o caso de exceções silenciosas que foram criadas e consistidas incorretamente. Aí elas se tornarão uma mina enterrada dentro de seu executável. Uma exceção destas pode danificar um banco de dados inteiro ou uma partição do Disco Rígido, conforme a operação que se tentou fazer mas que não foi bem sucedida e foi mal consistida. Pior do que isto, sem que o usuário perceba o que está acontecendo. Se o autor deste software tivesse olhado com mais atenção as possíveis exceções que esta aplicação poderia causar, talvez a incidência destes imprevistos seriam bem menores ou quase que nulos. |
E-Mail: walterchagas@yahoo.com |
![]() |