Criação de Componentes

Veja neste artigo de Fabio Francelino, como criar um componente para rastrear erros.

Rastreando Erros com TExErrorDialog

Como sabemos é muito importante o modo como tratamos os erros ocorridos nos sistemas, tanto os esperados quanto os inesperados, isso pode fazer a diferença na hora da manutenção de um sistema complexo e também transmite confiabilidade ao usuário final. Um programa é considerado robusto quando tem a capacidade de rastrear todos os seus erros e apresentar uma informação plausível para o usuário tomar alguma decisão ou entrar em contato com o setor de desenvolvimento do sistema.

Criando o pacote

Vamos começar criando um pacote para o componente TExErrorDialog. Vá no menu File|New > Other e escolha a opção Package. Salve o pacote com o nome sugerido em um diretório e clique na opção Options, na descrição do pacote digite o nome que irá aparecer na lista de pacotes instalados (Component|Install Packges). Certifique-se que as opções Designtime and runtime e Explicit rebuild estejam marcadas, caso queira, acesse a aba Version Info e configure as descrições do arquivo.

Criando a unit do componente

Vá ao menu Component > New Component, na caixa de diálogo crie um descendente (Ancestor Type) de TComponent com o nome (Class Name) de “TExErrorDialog” na paleta ExControls e o nome da unit como “untExErrorDialog”, não se esqueça de mudar o diretório da unit, clique em OK. Vamos codificar as propriedades, eventos e tipos do componente conforme a Listagem 1.

interface uses Windows, Messages, SysUtils, Classes, Forms; type { Tipos de Erro } TErrorType = (etNone = 0, etRunTime = 1, etSQL = 2, etValidation = 3, etLogic = 4); { Status do Erro } TErrorStatus = (esNone = 0, esFatal = 1, esNormal = 2); {Tipo de Erro} TErrorInfo = record ErrorStatus: TErrorStatus; ErrorType: TErrorType; ErrorSource: string; ErrorMsgUser: string; ErrorMsgSys: string; ErrorClass: string; ErrorMethod: string; ErrorCode: string; end; { Componente de Erro } TExErrorDialog = class(TComponent) private FErrorInfo: TErrorInfo; FErrorStatus: TErrorStatus; FErrorType: TErrorType; FErrorSource: string; FErrorMsgUser: string; FErrorMsgSys: string; FErrorClass: string; FErrorMethod: string; FErrorCode: string; FShowDetail: Boolean; FClearAfterShow: Boolean; FOnBeforeShow: TNotifyEvent; FOnAfterShow: TNotifyEvent; procedure SetErrorInfoValues(); procedure SetErrorPropValues(); protected public procedure Clear(); procedure ShowErrorMsg(); overload; procedure ShowErrorMsg( const pErrorInfo: TErrorInfo); overload; published property ErrorStatus: TErrorStatus read FErrorStatus write FErrorStatus; property ErrorType: TErrorType read FErrorType write FErrorType; property ErrorSource: string read FErrorSource write FErrorSource; property ErrorMsgUser: string read FErrorMsgUser write FErrorMsgUser; property ErrorMsgSys: string read FErrorMsgSys write FErrorMsgSys; property ErrorClass: string read FErrorClass write FErrorClass; property ErrorMethod: string read FErrorMethod write FErrorMethod; property ErrorCode: string read FErrorCode write FErrorCode; property ShowDetail: Boolean read FShowDetail write FShowDetail; property ClearAfterShow: Boolean read FClearAfterShow write FClearAfterShow; property OnBeforeShow: TNotifyEvent read FOnBeforeShow write FOnBeforeShow; property OnAfterShow: TNotifyEvent read FOnAfterShow write FOnAfterShow; end; procedure Register; implementation uses untExGuiErrorDialog; procedure Register; begin RegisterComponents('ExControls', [TExErrorDialog]); end; { Mostra a mensagem passando os parametros internos } procedure TExErrorDialog.ShowErrorMsg(); begin { Sincroniza as propriedades com o record } SetErrorInfoValues; { Chama a rotina } ShowErrorMsg(FErrorInfo); end; { Mostra a mensagem passando os parametros de pErrorInfo } procedure TExErrorDialog.ShowErrorMsg( const pErrorInfo: TErrorInfo); begin { Cria o formulario } frmExGuiErrorDialog := TfrmExGuiErrorDialog.Create(Self); FErrorInfo := pErrorInfo; frmExGuiErrorDialog.SetFieldValue(pErrorInfo); { Sincroniza o record com as propriedades } SetErrorPropValues; { Verifica e dispara o evento } if Assigned(FOnBeforeShow) then FOnBeforeShow(Self); { Seta o valor da propriedade ShowDetail } frmExGuiErrorDialog.cmdDetalhes.Visible := FShowDetail; { Mostra o form Modal } frmExGuiErrorDialog.ShowModal; if Assigned(FOnAfterShow) then FOnAfterShow(Self); { Verifica e limpa os parametros } if FClearAfterShow then Clear(); { Libera a memória } frmExGuiErrorDialog.Free; frmExGuiErrorDialog := nil; end; { Seta as propriedades internas do record } procedure TExErrorDialog.SetErrorInfoValues(); begin { Seta as propriedades do record } FErrorInfo.ErrorType := FErrorType; FErrorInfo.ErrorStatus := FErrorStatus; FErrorInfo.ErrorSource := FErrorSource; FErrorInfo.ErrorMsgUser := FErrorMsgUser; FErrorInfo.ErrorMsgSys := FErrorMsgSys; FErrorInfo.ErrorClass := FErrorClass; FErrorInfo.ErrorMethod := FErrorMethod; FErrorInfo.ErrorCode := FErrorCode; end; { Seta as propriedades internas do componente } procedure TExErrorDialog.SetErrorPropValues(); begin { Seta as propriedades do record } FErrorType := FErrorInfo.ErrorType; FErrorStatus := FErrorInfo.ErrorStatus; FErrorSource := FErrorInfo.ErrorSource; FErrorMsgUser := FErrorInfo.ErrorMsgUser; FErrorMsgSys := FErrorInfo.ErrorMsgSys; FErrorClass := FErrorInfo.ErrorClass; FErrorMethod := FErrorInfo.ErrorMethod; FErrorCode := FErrorInfo.ErrorCode; end; { Limpa todas as propriedades internas } procedure TExErrorDialog.Clear(); begin { Limpa as propriedades do record } with FErrorInfo do begin ErrorType := etNone; ErrorStatus := esNone; ErrorSource := EmptyStr; ErrorMsgUser := EmptyStr; ErrorMsgSys := EmptyStr; ErrorClass := EmptyStr; ErrorMethod := EmptyStr; ErrorCode := EmptyStr; end; { Limpa as propriedades publicas } ErrorType := etNone; ErrorStatus := esNone; ErrorSource := EmptyStr; ErrorMsgUser := EmptyStr; ErrorMsgSys := EmptyStr; ErrorClass := EmptyStr; ErrorMethod := EmptyStr; ErrorCode := EmptyStr; end; end.
Listagem 1. Código fonte completo

Propriedades do componente

Nessa seção destaco as propriedades ErrorStatus e ErrorType que são de tipos, delarados no inicio da unit e que nos mostram os tipos de erros que vamos apresentar e qual o status desses erros, como vemos no código a seguir:

{ Tipos de Erro } TErrorType = (etNone = 0, etRunTime = 1, etSQL = 2, etValidation = 3, etLogic = 4); { Status do Erro } TErrorStatus = (esNone = 0, esFatal = 1, esNormal = 2);

Eventos do componente

Declaramos dois eventos para ajudar a controlar erros fatais e dar um tratamento genérico de erro que são OnBeforeShow e OnAfterShow. As propriedades ClearAfterShow e ErrorStatus trabalham baseadas no seguintes eventos:

property OnBeforeShow: TNotifyEvent read FonBeforeShow write FOnBeforeShow; property OnAfterShow: TNotifyEvent read FonAfterShow write FOnAfterShow;

Codificando o componente

Temos duas rotinas que merecem destaque, pois ambas tem o mesmo nome com parâmetros diferentes. ShowErrorMsg (com a diretiva overload, que permite declarar rotinas com nomes iguais e parâmetros de tipos diferentes), em uma das suas declarações possui um parâmetro que é um record, ou seja, uma estrutura de dados (definida no escopo da unit), para facilitar o intercâmbio de um conjunto de informações entre rotinas e funções. Por causa desse record devemos sincronizar as propriedades do componente com o record recebido como parâmetro e vice-versa, para isso temos as rotinas SetErrorInfoValues e SetErrorPropValues veja na Listagem 2.

private procedure SetErrorInfoValues(); procedure SetErrorPropValues(); protected public procedure Clear(); procedure ShowErrorMsg(); overload; procedure ShowErrorMsg( const pErrorInfo: TErrorInfo); overload;
Listagem 2. Rotinas do componente

Formulário do componente

Sei que vocês estão tentados a compilar o componente, mas ainda falta a parte mais importante: o formulário de apresentação, ou seja, onde todas as informações serão exibidas para o usuário final.

Adicione um novo formulário (File|New > Form) no pacote e salve sua unit como “untGuiExErrorDialog.pas” e o nome do formulário como “frmExGuiErrorDialog”. Por que Gui?, Gui é a abreviação de Graphic User Interface nada mais conveniente.

Adicione no formulário os seguintes componentes: um StaticText, dois Buttons, um Memo e quatro Image. Disponha os componentes para que fiquem parecido com a Figura 1.

Figura 1. Formulário de apresentação dos erros

Altere as propriedades dos componentes conforme a Tabela 1.

Componente Nome Valor
StaticText Name lblErrMsg
BevelKind BkTile
Button Name cmdDetalhes
Caption <<&Detalhes
Default True
Button Name cmdOk
Caption &Ok
Cancel True
Memo Name txtErrorDescription
ScrollBars ssVertical
Image Name imgValidation
Image Name imgLogic
Image Name imgInformation
Image Name imgRunTime
Tabela 1. Propriedade dos componentes no formulário

Escolha uma imagem de 32x32 para cada um dos Images (altere para False a propriedade Visible dos Images), cada imagem representa um tipo de erro, e deixe todos exatamente sobrepostos, ao terminar lembre-se de voltar o tamanho do seu formulário para que fique sem aparecer o Memo (altere para bsSingle a propriedade BorderStyle do formulário). Insira na seção public do formulário o seguite código:

procedure SetFieldValue(const pErrorInfo: TErrorInfo);

E implemente-o conforme a Listagem 3.

procedure TfrmExGuiErrorDialog.SetFieldValue( const pErrorInfo: TErrorInfo); begin with pErrorInfo do begin { Verifica qual o tipo de erro } case ErrorType of etNone: begin Caption := 'Tipo de Erro Desconhecido.'; imgRunTime.Visible := True; end; etValidation: begin Caption := 'Erro de Validação.'; imgValidation.Visible := True; end; etSQL: begin Caption := 'Erro de Comando SQL.'; imgRunTime.Visible := True; end; etRunTime: begin Caption := 'Erro de Run-Time.'; imgRunTime.Visible := True; end; etLogic: begin Caption := 'Erro de Lógica de Programação.'; imgLogic.Visible := True; end; end; lblErrMsg.Caption := ErrorMsgUser; txtErrorDescription.Text := 'Code: ' + ErrorCode + #13#10 + 'Source: ' + ErrorSource + #13#10 + 'Class: ' + ErrorClass + #13#10 + 'Method: ' + ErrorMethod + #13#10 + 'Description: ' + ErrorMsgSys; end; end;
Listagem 3. Código do método SetFieldValue do formulário

No botão OK apenas indicamos a propriedade ModalResult como mrOK e fechamos o formulário (Close).

Devemos declarar na cláusula uses do formulário a unit do componente (untExErrorDialog), o que já nos permite tirar vantagem do record, facilitando o intercâmbio de informações entre o componente e o formulário.

Outro detalhe, é o tipo de erro etSQL não possuir uma imagem em particular, ele utiliza a mesma imagem do erro de run-time. Já podemos compilar e instalar o componente.

Alterando a imagem que representa o componente

Escolha uma imagem que representa bem o componente no formato .BMP com o tamanho 24x24, vá no menu Tools e abra o Image Editor, crie um novo Resource files (.dcr), adicione um novo bitmap, mude seu nome para o nome do componente (TEXERRORDIALOG) e cole a imagem desenhada na área em branco, salve o arquivo com o nome “untExErrorDialog.dcr”.

Para que a figura apareça na IDE do Delphi, vá no menu Project|View Source e adicione a seguinte linha de código:

{$R *.res} {$R untExErrorDialog.dcr} <- adicione aqui ...

Veja na Figura 2, como ficará o componente registrado na IDE do Delphi.

Figura 2. Componente ExErrorDialog instalado na IDE do Delphi

Utilizando o ExErrorDialog

Vamos finalmente testar todas as funcionalidades do componente. Crie uma nova aplicação no Delphi e configure o formulário como mostra a Figura 3.

Figura 3. Utilizando o componente

O código completo do componente e do exemplo está para download no endereço do artigo. Para testar, temos na Listagem 4, o código do botão Run-Time, onde utilizamos constantes (declaradas no formulário) para mostrar as mensagens de erros ao usuário (propriedade ErrorMsgUser).

procedure TForm1.Button1Click(Sender: TObject); begin try { Forçando o Erro } Button1.Tag := StrToInt('X'); except { Seta as informaçõe do erro } with ExErrorDialog1 do begin ErrorType := etRunTime; ErrorStatus := esNormal; ErrorClass := 'TButton'; ErrorMethod := 'Tag'; ErrorSource := 'cmdRunTimeClick'; ErrorMsgUser := ErrConvert; <- constante ErrorMsgSys := 'X is not a valid integer value.'; ShowErrorMsg; end; end; end;
Listagem 4. Utilizando o componente de Erro

No exemplo, um erro do tipo fatal pode causar o fechamento do seu programa com uma mensagem elegante (Figura 4), podemos ainda mostrar o último erro ocorrido sem nenhum esforço e ainda tratar mensagens de erro no evento OnBeforeShow. Podemos chamar rotinas passando nosso TErrorInfo como parâmetro e verificar o código retornado na propriedade ErrorCode, o que torna fácil o intercâmbio de informações de erro.

Figura 4. Componente em execução

Aconselho a colocar apenas um componente em seu DataModule e utilizá-lo em todo o seu programa, tornando fácil e prático a exibição de uma mensagem de erro. Podemos chamar funções que já preenchem as propriedades de erro e depois avaliar se mostramos o erro ou não. Os erros de lógica, como por exemplo, pode alertar programadores sobre valores inválidos em suas chamadas de funções e rotinas.

Outro aspecto que devemos destacar é a necessidade que alguns sistemas tem de fazer um log de erro, com o evento AfterShow você poderá chamar sua rotina de log e passar um TErrorInfo para ser gravado em um arquivo.

Se o leitor gostar dessa brincadeira poderá estender o componente e adicionar as funcionalidades de log embutidas no componente e até uma opção de envio de e-mail.

Conclusões

Utilizando corretamente esse componente você será capaz de identificar as seguintes informações sobre o erro: em qual função ocorreu o erro, por que, qual componente foi o responsável pelo erro, qual medida a ser tomada, qual o código, quem disparou a função. Isso reduz seu tempo de manutenção praticamente em 50% pois agora você já sabe tudo sobre o erro e basta somente corrigi-lo e não procurá-lo.

Ebook exclusivo
Dê um upgrade no início da sua jornada. Crie sua conta grátis e baixe o e-book

Artigos relacionados