segunda-feira, 3 de janeiro de 2011

Tratamento de exceção

Aplicações Robustas
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 esta ocorrer e responder adequadamente a essa exceção. Se não houver tratamento 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.
Exceções
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.
Blocos Protegidos
Um bloco protegido é um grupo de comandos com uma seção de tratamento de exceções.
try
A := StrToFloat(EdtA.Text);
B := StrToFloat(EdtB.Text);
ShowMessage(Format('%f / %f = %f', [A, B, A + B]));
except
ShowMessage('Números inválidos.');
end;
Algumas vezes você pode precisar especificar quais exceções quer tratar, como mostrado abaixo.
try
Soma := StrToFloat(EdtSoma.Text);
NumAlunos := StrToInt(EdtNum.Text);
ShowMessage(Format('Média igual a %f.', [Soma / NumAlunos]));
except
on EConvertError do
   ShowMessage('Valor inválido para soma ou número de alunos.');
on EZeroDivide do
   ShowMessage('O número de alunos tem que ser maior que zero.');
else
   ShowMessage('Erro na operação, verifique os valores digitados.');
end;
Principais Exceções
O Delphi define muitas exceções, para cada erro existe uma exceção correspondente.
Classe
Descrição
Exception
Exceção genérica, usada apenas como ancestral de todas as outras exceções
EAbort
Exceção silenciosa, pode ser gerada pelo procedimento Abort e não mostra nenhuma mensagem
EAccessViolation
Acesso inválido à memória, geralmente ocorre com objetos não inicializados
EConvertError
Erro de conversão de tipos
EDivByZero
Divisão de inteiro por zero
EInOutError
Erro de Entrada ou Saída reportado pelo sistema operacional
EIntOverFlow
Resultado de um cálculo inteiro excedeu o limite
EInvalidCast
TypeCast inválido com o operador as
EInvalidOp
Operação inválida com número de ponto flutuante
EOutOfMemory
Memória insuficiente
EOverflow
Resultado de um cálculo com número real excedeu o limite
ERangeError
Valor excede o limite do tipo inteiro ao qual foi atribuída
EUnderflow
Resultado de um cálculo com número real é menor que a faixa válida
EVariantError
Erro em operação com variant
EZeroDivide
Divisão de real por zero
EDatabaseError
Erro genérico de banco de dados, geralmente não é usado diretamente
EDBEngineError
Erro da BDE, descende de EDatabaseError e traz dados que podem identificar o erro
Blocos de Finalização
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.
FrmSobre := TFrmSobre.Create(Application);
try
FrmSobre.Img.LoadFromFile('Delphi.bmp');
FrmSobre.ShowModal;
finally
FrmSobre.Release;
end;
Você pode usar blocos de proteção e finalização aninhados
FrmOptions := TFrmOptions.Create(Application);
try
FrmOptions.ShowModal;
try
   Tbl.Edit;
   TblValor.AsString := EdtValor.Text;
except
   on EDBEngineError do
     ShowMessage('Alteração não permitida.');
   on EConvertError do
     ShowMessage('Valor inválido.');
end;
finally
FrmOptions.Release;
end;
Geração de Exceções
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
EInvalidUser = class (Exception);

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.
try
Tbl.Edit;
TblContador.Value := TblContador.Value + 1;
Tbl.Post;
except
ShowMessage('Erro ao alterar contador.');
raise;
end;
Erros de Bancos de Dados
A exceção EDBEngineError permite a identificação de erros de bancos de dados gerados pela BDE.
try
TblCli.Post;
except
on E: EDBEngineError do
   if E.Errors[0].ErrorCode = DBIERR_KEYVIOL then
     ShowMessage('Cliente já cadastrado.');
end;
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);
begin
if(E is EDBEngineError) then
   with EDBEngineError(E) do
     case Errors[0].ErrorCode of
       DBIERR_KEYVIOL: ShowMessage('Cliente já cadastrado.');
       DBIERR_REQDERR: ShowMessage('Campo obrigatório não preenchido.');
     end
else
   ShowMessage('Erro no banco de dados:' + #13#13 + E.Message);
Action := daAbort;
end;
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.
Constante
Descrição
DBIERR_KEYVIOL
Violação de chave primária
DBIERR_MAXVALERR
Valor máximo excedido
DBIERR_FORIEGNKEYERR
Erro de chave externa, como em integridade referencial
DBIERR_LOCKED
Registro travado
DBIERR_FILELOCKED
Arquivo travado
DBIERR_NETMULTIPLE
Mais de um diretório usado como NetFileDir
DBIERR_MINVALERR
Campo com valor mais baixo que valor mínimo
DBIERR_REQDERR
Campo obrigatório faltando
DBIERR_LOOKUPTABLEERR
Erro em tabela Lookup
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;
begin
SetLength(Result, DBIMAXMSGLEN + 1);
try
   DbiGetErrorContext(Context, PChar(Result));
   SetLength(Result, StrLen(PChar(Result)));
except
   Result := '';
end;
end;
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
ShowMessage('Usuário ''' + GetErrorInfo(ecUSERNAME) + ''' está alterando o registro.');
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.
Constante
Descrição
ecTABLENAME
Nome da Tabela
ecFIELDNAME
Nome do campo
ecUSERNAME
Nome do usuário, muito usado para identificar qual usuário travou o registro
ecFILENAME
Nome do arquivo
ecINDEXNAME
Nome do índice
ecDIRNAME
Pasta
ecKEYNAME
Chave primária
ecALIAS
Alias
ecDRIVENAME
Drive
ecNATIVECODE
Código de erro nativo
ecNATIVEMSG
Mensagem de erro nativa
ecLINENUMBER
Número da linha, usado em instruções SQL
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';
SFCreateError = 'Não é possível criar arquivo %s';
SFOpenError = 'Não é possível abrir arquivo %s';
SInvalidFieldSize = 'Tamanho de campo inválido';
SInvalidFieldRegistration = 'Registro de campo inválido';
SUnknownFieldType = 'Campo ''%s'' tem um tipo desconhecido';

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.


quinta-feira, 30 de dezembro de 2010

Tabela Ascii

Dec
Hex
Char
Dec
Hex
Char
Dec
Hex
Char
Dec
Hex
Char
0
0
NUL
32
20
.
64
40
@
96
60
`
1
1
SOH
33
21
!
65
41
A
97
61
a
2
2
STX
34
22
"
66
42
B
98
62
b
3
3
ETX
35
23
#
67
43
C
99
63
c
4
4
EOT
36
24
$
68
44
D
100
64
d
5
5
ENQ
37
25
%
69
45
E
101
65
e
6
6
ACK
38
26
&
70
46
F
102
66
f
7
7
BEL
39
27
'
71
47
G
103
67
g
8
8
BS
40
28
(
72
48
H
104
68
h
9
9
TAB
41
29
)
73
49
I
105
69
i
10
A
LF
42
2A
*
74
4A
J
106
6A
j
11
B
VT
43
2B
+
75
4B
K
107
6B
k
12
C
FF
44
2C
,
76
4C
L
108
6C
l
13
D
CR
45
2D
-
77
4D
M
109
6D
m
14
E
SO
46
2E
.
78
4E
N
110
6E
n
15
F
SI
47
2F
/
79
4F
O
111
6F
o
16
10
DLE
48
30
0
80
50
P
112
70
p
17
11
DC1
49
31
1
81
51
Q
113
71
q
18
12
DC2
50
32
2
82
52
R
114
72
r
19
13
DC3
51
33
3
83
53
S
115
73
s
20
14
DC4
52
34
4
84
54
T
116
74
t
21
15
NAK
53
35
5
85
55
U
117
75
u
22
16
SYN
54
36
6
86
56
V
118
76
v
23
17
ETB
55
37
7
87
57
W
119
77
w
24
18
CAN
56
38
8
88
58
X
120
78
x
25
19
EM
57
39
9
89
59
Y
121
79
y
26
1A
SUB
58
3A
:
90
5A
Z
122
7A
z
27
1B
ESC
59
3B
;
91
5B
[
123
7B
{
28
1C
FS
60
3C
<
92
5C
\
124
7C
|
29
1D
GS
61
3D
=
93
5D
]
125
7D
{
30
1E
RS
62
3E
>
94
5E
^
126
7E
~
31
1F
US
63
3F
?
95
5F
_
127
7F
DEL

quarta-feira, 29 de dezembro de 2010

Cuidados ao usar o OnExit

É comum fazermos uso do evento OnExit quando queremos validar o conteúdo de um Edit. E essa pode ser uma boa prática quando necessitamos verificar o que foi digitado apenas quando o usuário terminar de fazer a entrada de dados, como, por exemplo, um Edit que vai receber o CPF ou CNPJ.
Ao colocarmos um código qualquer no evento OnExit ele sempre será executado quando o usuário sair do Edit, o que acontece quando ele pressiona a tecla TAB, clica com o mouse em um outro Edit ou pressiona um botão OK, por exemplo.
No entanto, existem algumas situações especiais em que o evento OnExit não é gerado. Quer um exemplo? Você está no Edit e, ao invés de clicar no botão OK, você pressiona as teclas ALT + O (considerando que o botão OK tem a tecla O como atalho). É como se você tivesse pressionado o botão OK, porém, sem perder o foco que está no Edit. Só mais um exemplo: Os botões do tipo SpeedButton não recebem foco, então, mesmo que clique com o mouse sobre um SpeedButton, o foco continuará no Edit e, conseqüentemente, o evento OnExit não será gerado.

E a solução?

A solução para esse pequeno inconveniente é simples. Basta você colocar o seguinte código no evento OnClick do botão.

procedure TForm1.Button1Click(Sender: TObject);

begin

ActiveControl := nil;

...

end;

Suponhamos que você possua 2 Edits em um formulário. Supondo também que você queira dar alguma informação ao usuário da aplicação logo depois que ele sair do Edit1 você faz:

procedure TForm1.Edit1Exit(Sender: TObject);

begin

    MessageDlg('Mensagem...', mtInformation, [mbOk], 0);

end;


A princípio está tudo ok, ou melhor, parece estar tudo ok.

Se você altera o foco para o outro Edit através do pressionamento da tecla TAB, tudo bem. Mas experimente alterar o foco clicando com o mouse sobre o Edit2. Neste segundo caso a mensagem será exibida normalmente. Mas ao fechar o dialogo onde aparece a mensagem, o foco simplesmente se perde. Para setar o foco no Edit2 é necessário clicar novamente sobre ele.

Isso não teria problema nenhum até que seu usuário experimente esta situação. Nada que ele digitar será acatado.

Mas existe uma maneira fácil de resolver o problema. Basta você cancelar o foco e forçar uma reentrada no componente Edit2. Como fazer isso? Veja o código:

procedure TForm1.Edit1Exit(Sender: TObject);

begin

   MessageDlg('Mensagem...', mtInformation, [mbOk], 0);

   // cancela o foco e força novamente a entrada

   ActiveControl := nil;

   PostMessage(Edit2.Handle, WM_SETFOCUS, 0, 0);

   Edit2.SetFocus;

end;

Porém, você nunca terá certeza se o usuário clicou no Edit2. Então temos que criar uma rotina genérica que leva o foco para qualquer outro controle:

procedure TForm1.Edit1Exit(Sender: TObject);

var

   Ctrl: TWinControl;

begin

   MessageDlg('Mensagem...', mtInformation, [mbOk], 0);

   // cancela o foco e força novamente a entrada

   Ctrl := ActiveControl;

   ActiveControl := nil;

   PostMessage(TWinControl(Ctrl).Handle, WM_SETFOCUS, 0, 0);

   TWinControl(Ctrl).SetFocus;

end;

Observe que antes de cancelar o foco com ActiveControl := nil, salvamos qual é o controle que detém o foco fazendo Ctrl := ActiveControl.

Depois enviamos uma mensagem ao controle que detinha o foco, forçando-o a receber o foco novamente.

Rave - Imprimindo Gráficos (Chart)

Uma coisa muito comum nos sistemas, são os relatórios com gráficos estatísticos, os famosos Charts.

Antigamente eu usava o QuickReport e o ReportBuilder, porém relatórios com gráficos só precisei fazer no QuickReport e não tive muitas dificuldade, pois ele já tem um componente que facilita a vida.

Mas, como agora larguei o QuickReport e estou usando somente o Rave, houve então a necessidade de saber como fazer relatórios nele com gráficos.

Comecei então a fuçar no Rave e percebi que não existia nenhum componente que facilitasse a vida. Então fiz algumas pesquisas na internet para saber se não existiam componentes de terceiros, mas infelizmente não achei nenhum, porém nessas pesquisas, achei duas soluções para o caso :

1 – Salvar o conteúdo de um TCustomChart em um BMP e imprimir esse BMP no Rave
2 – Usar o método WriteChartData disponível na unit RPTChart

Esta última solução é a indicada pela Nevrona.

Fiz o teste com as duas soluções e as duas foram satisfatórias, porém gostei mais da última solução, pois como disse, é indicada pela própria Nevrona e não aparenta ser uma "gambiarra" :-) como a primeira solução.

Depois desta pesquisa, resolvi escrever este artigo para demonstrar como não é um bicho de sete cabeças fazer isso funcionar.

Então vamos lá...

Antes de iniciarmos, gostaria de explicar como a "coisa funciona", para depois partirmos para prática.

O método WriteChartData, "escreve" o conteúdo de um TCustomChart dentro de um campo do tipo Graphic. Então, a princípio, nosso gráfico será montado no TChart ou TDBChart do Delphi, e depois disso, iremos utilizar o WriteChartData para fazer com que o conteúdo do gráfico seja impresso, desenhado dentro do nosso relatório.

Agora vamos colocar em prática esta teoria...

Irei utilizar um RVCustomConnection, pois não preciso estar conectado a um DataSet para gerar o gráfico no Rave, só preciso de alguém que me disponibilize um campo para poder usá-lo na impressão, então o RVCustomConnection é o ideal para isso. Para quem não sabe para que serve o RVCustomConnection, vale a pena começar a mexer nele, pois é muito interessante.

Agora é serio, chega de teoria e vamos para prática...:-)

1 - Insira os seguintes componentes no Form:
TRVProject
TRVCustomConnection
TRVSystem
TChart

2 – Na clausula uses, insira a unit RPTChart. É esta unit que nos disponibilizará o método WriteChartData

3 - Ajuste a propriedade Engine do RaveProject, apontando para o RVSystem.

4 - Ajuste o Chart, de forma que represente algum gráfico, só para podermos visualizar o resultado final.

5 – Agora iremos ajustar dois eventos do componente RVCustomConnection:

OnGetCols:
Este evento é chamado quando o Rave necessita extrair os meta-data dos campos. É aqui que criaremos nosso campo do tipo graphic. Para isso, coloque o seguinte código neste evento:

with Connection do
begin
WriteField('CampoChart', dtGraphic, 0, '', '');
end;

 

OnGetRow:
Este evento é chamado quando o Rave necessita extrair os valores dos campos do registro atual. É aqui que iremos alimentar o valor do campo que criamos acima. Para isso, coloque o seguinte código neste evento:

WriteChartData(Connection, Chart1);

 

Chart1 é o nome do TChart que inserimos no Form.

A parte de codificação já está pronta, agora vamos para parte visual.

6 – Entre no RaveDesigner

7 – Crie uma Region e dentro desta Region crie uma banda simples.

8 – Dentro da Band1(que acabou de ser criada), insira o componente MetaFile, que está na palheta Standard do Rave.

9 – Crie uma DataView, da mesma forma que se fosse criar uma outra qualquer, a diferença é que os dados agora virão de um RVCustomConnection.

10 - Após ter criado, perceba que na TreeView o DataView1 está disponível, porém se clicar no sinal de mais do DataView1 para exibir os campos, perceberá que o campo que criamos via código(CampoChart) não está disponível, só tem um campo, que é o que vem de brinde, mas não use-o. Para que o nosso campo possa ser criado, o evento OnGetCols terá que ser chamado, e como fazer isso em tempo de projeto ?

O segredo é : O RaveDesigner tem que estar aberto juntamente com a aplicação e depois chamar o Refresh do DataView.

Então vamos lá...

11 - Execute a aplicação e volte para o RaveDesigner, mas não feche a aplicação ainda.

12 – Selecione o DataView1 lá na TreeView e clique com o botão direito do mouse sobre o DataView1. Aparecerá um item chamado Refresh, basta clicar nele e...bingo :-). Pronto, o campo que criamos via código irá aparecer na lista. Quando clicar pela primeira vez no Refresh, vai aparecer uma mensagem, é uma alerta de que um campo será excluído, isso ocorre pois quando criamos a DataView, já veio aquele campo de brinde, mas como não criamos ele no OnGetCols, então a mensagem alertará de que o mesmo será excluído.

13 – Agora selecione o componente MetaFile que foi inserindo em Band1 e altere duas propriedades dele:

DataField : CampoChart (nome do campo que criamos)
DataView : DataView1 (nome da dataview criada)

Pronto...Pode executar a aplicação e fazer o teste.

Caso queira fazer o teste no próprio preview do RaveDesigner, basta deixar a aplicação aberta, caso contrário, se a aplicação não estiver aberta e você chamar o preview do RaveDesigner, receberá de presente um errinho básico, o famoso "Acess violation..." :-)

Lembre-se, o exemplo que fizemos foi com um TChart, o mesmo poderá ser feito com um TDBChart.

Outro detalhe, utilizamos um RVCustomConnection, mas poderíamos utilizar um RVDataSetConnection sem problemas, porém neste caso, só fique atento com o seguinte:

No evento OnGetCols, antes de criar o campo, chame o método DoGetCols do Connection que vem como parâmetro neste evento. Isso deve ser feito para que primeiro sejam criadas as colunas do DataSet que está associado e depois sim poder criar as suas colunas.

E no evento OnGetRow, antes de chamar o WriteChartData, chame o método DoGetRow do Connection que vem como parâmetro neste evento. Isso deve ser feito para que primeiro seja alimentando os campos do DataSet que está associado e depois sim poder alimentar seus campos criados manualmente.

Espero que um dia possa surgir um componente para utilizarmos no Rave, pois assim ficará muito mais fácil, mas enquanto isso não acontece, temos esta solução

terça-feira, 28 de dezembro de 2010

Usuário e senha do DBA do MySql

Para Windows = ODBC (Maíusculo e sem senha) – usuário root e sem senha

Para Linux = root (Minúsculo e sem senha)

Detalhe importante: O DBA pode ser acessado somente via localhost (máquina local). Depois do primeiro acesso SQL, mude os Rights se quiser acessar de outro micro na rede (mas aconselho a não fazer isso, olha a segurança!!!!!).

 

Alguns comandos básicos para Firebird

Corrigindo problemas de instalação do Borland Data Provider For FireBird:

Em alguns casos, o Borland Data Provider For Firebird têm apresentado problemas de instalação, abaixo segue algumas dicas para tentar auxiliar na resolução dos mesmos:

Verificar se o D8 está atualizado com Update Pack#2;

Se o BDP For Firebird foi instalado 'antes' do D8 ter sido atualizado,desinstalar, atualizar o D8 e após isso instalar o BDP For Firebird novamente;

Efetuar testes diretamente via 'Data Explorer' a fim de verificar se a conexão ocorre sem problemas;

Copiar o arquivo 'FirebirdSql.Data.Bdp' para a pasta

C:\Arquivos de programas\Arquivos comuns\Borland Shared\BDS\Shared\Assemblies\2.0

5. No projeto, acesse 'Project Manager' | 'References' e adicione a referência ao 'Firebirdsql.Data.Bdp'


Listar as tabelas e views do banco de dados (Firebird):

SELECT RDB$RELATION_NAME FROM RDB$RELATIONS;

Listar as tabelas do banco de dados (Firebird):

SELECT RDB$RELATION_NAME FROM RDB$RELATIONS
WHERE RDB$VIEW_BLR IS NULL;


Listar as views do banco de dados (Firebird):

SELECT RDB$RELATION_NAME FROM RDB$RELATIONS
WHERE NOT RDB$VIEW_BLR IS NULL;


Obter o valor de um generator:

Para obter o valor de um generator use a função GEN_ID do InterBase/FireBird. 

A sintaxe é: 
GEN_ID(NomeDoGenerator, Incremento);

Exemplos

GEN_ID(Gen_Cliente_Codigo, 1);
GEN_ID(Gen_Cliente_Codigo, 0);

Reiniciar a contagem de um generator:

Para re-iniciar a contagem de um generator execute o comando abaixo.
SET GENERATOR TO X;

Excluir generator (Firebird):

DROP GENERATOR NOME_DO_GENERATOR;

Trocar o tipo de um campo (FireBird):

ALTER TABLE <Table1> ALTER <Campo> TYPE <Tipo do Campo>

Excluir código-fonte de stored procedure (Interbase/FireBird):

UPDATE RDB$PROCEDURES SET RDB$PROCEDURE_SOURCE = 'deletado';

Usando um For Select dentro da StoredProcedure no FireBird/Interbase:

create procedure SP_RETORNO(PCOD INTEGER)
returns (COD INTEGER, NOME VARCHAR(30),
VALOR NUMERIC(15,4))

as
begin
for select CODIGO, NOME from CLIENTES
where CODIGO = :PCOD
into :COD, :NOME do
begin

   select VALOR from VALORES
   where CODIGO = :COD
   into :VALOR;
   if ((VALOR IS NULL) or (VALOR = 0)) then
   VALOR = 1;
   suspend;

end;
end

Como traduzir as mensagens do Delphi

Boa tarde,
Abaixo uma dica valiosa de como traduzir as mensagens que o delphi retorna, após identificar uma operação ilegal.
O arquivo das mensagens que você deverá traduzir está em \Arquivos de Programas\ Borland\ Delphixx\ Source\ Vcl\ Consts.pas.
Traduza as mensagens normalmente. Depois de alterar o arquivo, eu salvo uma cópia na pasta \bin e na pasta \lib. O programa já funciona com os alertas traduzidos.
Você deverá traduzir também o arquivo DBConsts.pas que contém as mensagens relativas a componentes database.