segunda-feira, 18 de outubro de 2010

Conceito de Banco de Dados

Bancos de Dados
Conceitos Importantes
O gerenciamento de bancos de dados é essencial para o desenvolvimento comercial, e para criar um banco de dados eficiente é necessário o conhecimento prévio de modelagem de bancos de dados relacionais. Conceitos como banco de dados, tabelas, campos, registros, índices, chaves, relacionamentos, normalização, dentre outros são pré-requisitos básicos para o desenvolvimento desse conteúdo.
Modelo de Dados
É essencial planejar o banco de dados antes de implementar. Um dos métodos que você pode utilizar é o DER, como no exemplo não normalizado mostrado logo abaixo.

imagem1
Borland Database Engine
A BDE fornece a capacidade de acesso padronizado a banco de dados para Delphi, C++ Builder e outros ambientes de programação da Borland, oferecendo um grande conjunto de funções para auxiliar no desenvolvimento de aplicações Desktop e Cliente/Servidor.
Os controladores da BDE podem ser usados para acessar bases de dados dBase, Paradox, Access, FoxPro, Interbase, Oracle, Sybase e MS-SQL Server, DB2, Informix, além de um controlador de acesso a arquivos texto. Você também pode utilizar fontes de dados ODBC, podendo acessar qualquer base de dados compatível.
As funções que compõe uma API da BDE são usadas internamente pelos componentes de acesso a dados do Delphi e muito raramente você teria que usá-las diretamente, mas isso é totalmente possível. A referência completa das funções da BDE, com exemplos em Delphi, está no BDE API Help na pasta do Delphi no Menu Iniciar.
Arquitetura de Acesso
O acesso e manipulação de um banco de dados por um programa Delphi é realizado como mostrado abaixo, note que a aplicação não acessa os dados diretamente, mas usa sempre a BDE.

imagem2
Assim, para uma aplicação de bancos de dados funcionar, é preciso que a BDE esteja instalada na máquina, não bastando apenas o arquivo executável.
Criação do Banco de Dados
Para criar um banco de dados novo, normalmente, é necessário dispor de alguma ferramenta do próprio banco de dados, como o Access, mas se a base de dados for Paradox, ou dBase, você pode usar o Database Desktop, um utilitário que vem com o Delphi e permite a criação desses tipos de bancos de dados.
Database Desktop
Fornece uma interface simples e completa para configuração, definição e manipulação de tabelas de bancos de dados Paradox e dBase. Além disso na Opção Tools/Alias Manager você pode configurar seu banco de dados, como será lembrado logo adiante.
Tabelas Paradox
Para criar tabelas Paradox, siga os passos abaixo. Você deve salvar as tabelas de um mesmo banco de dados na mesma pasta, pois o Paradox trata a pasta onde estão as tabelas como sendo o banco de dados.
Clique em File/New/Table
Escolha o tipo da nova tabela, Paradox 7
Aparece uma janela para que você defina a estrutura de campos, índices e demais opções necessárias na criação da tabela
Em Field Name, você escolhe o nome do campo, com até 25 caracteres
Em Type, o Tipo do campo, com a barra de espaço ou o botão direito do mouse você pode escolher o tipo a partir de uma lista
Size é o tamanho do campo, usado somente em alguns tipos de campos
Key especifica os campos que farão parte da chave primária, que não pode se repetir e deve ser composta pelos primeiros campos da tabela
Table Properties
Em Table Properties você define os vários aspectos de configuração da tabela. Muitas dessas opções podem ser implementadas no Delphi e vários programadores preferem não usá-las no Database Desktop.
Opção Descrição
Validity Checks Validações para os campos, como obrigatoriedade, valor mínimo e máximo
Table Lookup Indica que o valor atribuído a um determinado campo tem que estar gravado em outra tabela
Secondary Indexes Cria índices secundários
Referential Integrity Cria integridade referencial, geralmente utilizada em relacionamentos de 1 para N.
Password Security Permite a criação de senhas, protegendo a tabela de acesso não autorizado
Table Language Especificar o driver de língua utilizado pela tabela, geralmente é o Pdox ANSI Intl850
Dependent Tables Mostra todas as tabela dependentes através da integridade referencial
Tipos de Campos
Os principais tipos de campos são mostrados abaixo, mas existem outros além desses. Os tamanhos marcados com asterisco indicam que o campo pode guardar tamanhos maiores que os informados, o que ultrapassar o tamanho será guardado em um arquivo externo com a extensão MB.
Tipo Descrição Faixa Tamanho
A Alfanumérico 1-255
N Numérico ± 10 308
$ Monetário
S Short Integer ± 32767
I Long Integer ± 2147483648
D Data
T Hora
@ Data e Hora de modificação
M Memo 1-240*
G Gráfico 1-240*
L Lógico True/False
+ Autoincremental 1-2147483648
Configuração
Para configurar o acesso a um banco de dados, você tem várias opções, criar um Alias, usar o componente TDatabase ou os dois juntos.
Aliases
Um Alias é um nome lógico, um atalho para um banco de dados. Todo o trabalho do Delphi com um banco de dados pode ser feito baseado no Alias, de forma que para mudar de banco de dados, só é necessário mudar o Alias. Para criar um Alias você pode usar Database Explorer, o BDE Administrator ou o próprio Database Desktop.
Database Explorer
Pode aparecer com os nomes Database Explorer ou SQL Explorer. Nele você pode manipular os Aliases, navegar pelas estruturas dos bancos de dados, alterar os dados das tabelas e executar comandos SQL.
Para criar um Alias selecione o item Databases, clique em Object/New, escolha o tipo do banco de dados, ou Standard para dBase, Paradox e arquivos texto, depois digite um nome do Alias, esse nome será usado pelo Delphi quando você quiser acessar o banco de dados, finalmente defina as propriedades do banco de dados na seção Definition, cada banco de dados terá suas próprias definições.
BDE Administrator
Com o BDE Administrator você pode alterar a configuração da BDE, por exemplo em Configuration/System/Init você tem a propriedade Local Share que deve ser setada para True, quando você quiser que a base de dados seja compartilhada em uma rede. Além disso, você pode criar Aliases, como no Database Explorer.
TDatabase
Esse componente permite a manipulação de um banco de dados, através de um Alias da BDE ou a criação de um Alias local, somente visível dentro da aplicação, esse componente também permite o gerenciamento de transações, garantindo uma integridade maior no projeto. Por essas e outras razões o uso do componente Database é altamente recomendado como opção para criação de Aliases.
Propriedades Descrição
AliasName Nome do Alias do banco de dados, usado quando você criar um Alias da BDE
Connected Define se a conexão com o banco de dados está ativa
DatabaseName Nome do Alias local a ser usado pelos outros componentes do Delphi
DataSetCount Número de DataSets (Tabelas) abertos no banco de dados
DataSets Lista com os DataSets abertos
DriverName Driver usado para criar um Alias local, automaticamente cancela a propriedade AliasName
InTransaction Define se o Database está em transação
KeepConnection Define se a conexão com o banco de dados será mantida, mesmo sem DataSets abertos
LoginPrompt Define se será mostrado o quadro de login padrão da BDE
Params Parâmetros do banco de dados, com itens semelhantes à seção Definition do Database Explorer
TransIsolation         Nível de isolamento da transação, define como uma transação irá enxergar outra
Métodos Descrição
Close Encerra a conexão com o banco de dados, todos os DataSets serão fechados
CloseDataSets Fecha todos os DataSets abertos, mas a conexão não é encerrada
Commit Grava alterações feitas durante a transação
Open Abre a conexão com o banco de dados
Rollback Anula todas as alterações feitas durante a transação
StartTransaction Inicia uma transação
Eventos Descrição
OnLogin Evento usado quando você quiser escrever seu próprio método de conexão com o banco de dados
Para acessar uma base de dados Access, você poderia usar os valores mostrados na descrição textual a seguir.
AliasName = 'Northwind'
DatabaseName = 'Dados'
LoginPrompt = False
KeepConnection = True
Params.Strings = (
'DATABASE NAME=C:\Meus Documentos\NorthWind.mdb'
'USER NAME=paulo'
'OPEN MODE=READ/WRITE'
'LANGDRIVER=intl850'
'PASSWORD=elvis')
Para ajudar a preencher os parâmetros de um Database, clique duas vezes sobre o componente e clique em Defaults, todos os parâmetros defaults serão apresentados.
Para acessar uma base Paradox, use as propriedades abaixo, note que para o Paradox, a única informação realmente significante é o Path, a pasta onde estão as tabelas.
AliasName = 'DBDEMOS'
DatabaseName = 'Dados'
LoginPrompt = False
KeepConnection = True
Params.Strings = (
'PATH=d:\Borland\Delphi 3\Demos\Data'
'ENABLE BCD=FALSE'
'DEFAULT DRIVER=PARADOX')
Após a criação do Alias da BDE ou do Alias local, usando o componente TDatabase, o banco de dados está configurado e pronto para ser usado.
Database Form Wizard
Após a configuração do banco de dados, a maneira mais rápida, de se fazer uma janela de manutenção de dados é através do Form Wizard no menu Database. Ao chegar no Wizard são feitas uma série de perguntas que podem resultar em uma janela simples ou Mestre/Detalhe. O acesso ao banco de dados pode ser feito através de componentes TTable ou através de SQL, com o componente TQuery, usaremos o componente TTable. Todos os campos selecionados aparecem na janela permitindo entrada de dados através de componentes do tipo TDBEdit. Cada DBEdit recebe um Label baseado no nome do campo na tabela selecionada. Na Janela é incluído também um componente para permitir a navegação e a manutenção dos dados, um DBNavigator. O componente utilizado para fazer a ligação entre os componentes visuais e o TTable é um TDataSource. Geralmente os componentes TTable e TDataSource são inseridos em DataModules, que são a base para a criação de classes de dados. Sempre Após usar o Wizard, lembre-se de mudar os nomes dos componentes, para que fiquem mais claros.
Form Passo a Passo
O diagrama abaixo mostra como o Wizard fez a ligação entre os componentes, onde os quadrados são componentes e as elipses, propriedades.

imagem3
Para concluir, acompanhe abaixo os passos realizados pelo Wizard e tente você mesmo criar seu próprio Form.
Inclua um novo DataModule
Adicione ao DataModule um Table e um DataSource
No Table Coloque em DatabaseName o nome do Alias criado pela propriedade DatabaseName do Database e em TableName, o nome da tabela
No evento OnCreate do DataModule, chame o método Open do componente Table
No DataSource coloque em DataSet o nome do componente TTable
No Form, para definir a interface com o usuário, use os componentes de controle de dados que estão na página DataControls, basicamente DBEdit e DBNavigator
Para poder acessar os dados, coloque a Unit onde está o DataModule no uses da Unit do Form
Em todos os componentes DataControls, escolha na propriedade DataSource, o componente DataSource criado no DataModule
Em alguns controles, como no DBEdit, deve ser especificado também o campo da tabela, na propriedade DataField
Seguindo esses passos, o Form estará pronto para usar. Mais adiante, veremos uma forma mais rápida de se criar um Form de manutenção, mas o mais importante é compreender os passos mostrados acima, com todos os componentes e propriedades envolvidas. Vamos detalhar agora cada um dos componentes envolvidos nesse processo, para compreendermos melhor o que está acontecendo.
TDataModule
Um DataModule é como se fosse um Form invisível, onde iremos inserir os componentes de acesso a dados, como o Table e o Datasource. Por serem também classes, os DataModules permitem a fácil implementação de modelos de objetos, permitindo herança, criação de métodos, dentre outros aspectos. Para inserir um DataModule em um projeto, escolha New DataModule do menu File. Os DataModules não gastam recursos do sistema, servem apenas para conter os componentes de acesso a dados e criar, assim, uma classe persistente.
TTable
Componente usado para acessar uma tabela em um banco de dados. Esse componente é o mais importante quando acessamos bases de dados Desktop. Muitas dos itens mostrados abaixo estão definidos na classe TDataSet, ancestral do TTable.
Propriedades Descrição
Active Define se a tabela esta aberta ou fechada
BOF Informa se está no início da tabela
CanModify Define se a aplicação pode inserir, deletar ou alterar registros
DatabaseName Nome do banco de dados onde está a tabela, deve ser escolhido um Alias, que pode ser local
EOF Informa se está no fim da tabela
Exclusive Define se a tabela pode ser compartilhada por outro usuário
FieldCount Número de campos da tabela
FieldDefs Lista com a Definição dos campos da tabela
Fields Lista de objetos do tipo TField, que representam os campos da tabela
Filter String com uma condição de filtragem
Filtered Define se a tabela é filtrada
IndexFieldNames Nome dos campo de índice, usados para ordenar os registros da tabela
IndexName Nome do índice atual, vazia quando o índice for a chave primária
IndexDefs Lista com a definição dos índices
MasterFields Campos usados no relacionamento com a tabela mestre
MasterSource DataSource da tabela mestre em uma relação Mestre/Detalhe
Modified Define se o registro atual foi modificado
ReadOnly Define se a tabela é somente para leitura
RecNo Número do registro atual
RecordCount Número de registros
State Estado da tabela
TableName Nome da tabela
TableType Tipo da tabela
Método Descrição
AddIndex Cria um novo índice, a tabela deve ser exclusiva
Append Entra em modo de inserção e, ao gravar, o registro será colocado no fim do arquivo
AppendRecord Insere um registro no final do arquivo através de código
Cancel Cancela as alterações feitas no registro atual
Close Fecha a tabela
CreateTable Cria uma tabela, depende de FieldDefs e IndexDefs
Delete Exclui o registro corrente
DeleteIndex Exclui um índice
DeleteTable Exclui a tabela
DisableControls Desabilita a atualização dos controles visuais
Edit Permite a alteração dos campos do registro atual
EmptyTable Apaga todos os registro da tabela, para isso a tabela não pode esta sendo compartilhada
EnableControls Habilita os controles visuais
FieldByName Acessa um campo, do tipo TField, pelo nome
FindKey Procura o registro com os valores exatos aos dos parâmetros nos campos do índice atual
FindNearest Procura o registro com os valores mais aproximados aos dos parâmetros nos índices
First Move para o primeiro registro
Insert Entra em modo de inserção de um novo registro na posição atual
InsertRecord Adiciona um novo registro, já com os dados, na posição atual
IsEmpty Define se a tabela está vazia
Last Move para o último registro
Locate Procura um registro, usando ou não índices, de acordo com a disponibilidade
LockTable Trava a tabela
Lookup Procura um registro e retorna valores dos campos deste
MoveBy Move um número específico de registros
Next Move  para o próximo registro
Open Abre a tabela
Post Grava as alterações no registro atual
Prior Move para o primeiro registro
Refresh Atualiza a tabela com os dados já gravados
RenameTable Renomeia a tabela
UnlockTable Destrava a tabela
Evento Descrição
AfterCancel Após do método Cancel
AfterClose Após o fechamento da tabela
AfterDelete Após do método Delete
AfterEdit Após do método Edit
AfterInsert Após do método Insert
AfterOpen Após do método Open
AfterPost Após do método Post
AfterScroll Após mudar de registro
BeforeCancel Antes do método Cancel
BeforeClose Antes do fechamento da tabela
BeforeDelete Antes do método Delete
BeforeEdit Antes do método Edit
BeforeInsert Antes do método Insert
BeforeOpen Antes do método Open
BeforePost Antes do método Post
BeforeScroll Antes de mudar o registro
OnCalcFields Evento usado para calcular os valores dos campos calculados
OnDeleteError Quando ocorre um erro ao chamar o método Delete
OnEditError Quando ocorre um erro ao chamar o método Edit
OnFilterRecord Evento usado com filtragem variável
OnNewRecord Quando a tabela entra em modo de inserção, não deixa Modified igual a True
OnPostError Quando ocorre um erro ao chamar o método Post
Filtros
Usando o Filter, você pode filtrar os registro de uma tabela usando uma expressão lógica, como nos exemplos abaixo. Para tornar um filtro ativo, basta colocar Filtered igual a True.
Data = '20/04/1998'
(Data = '20/04/1998') AND (Vendedor = 'Gilherme Augusto da Fonseca')
(Nome > 'A') AND (Nome < 'B')
Contudo, se a condição de filtragem for muito variável, é preferível usar um código como o mostrado abaixo no evento OnFilterRecord da Table, para fazer uma filtragem dinâmica, com a propriedade Filter vazia e Filtered igual a True.
Accept := TblData.Value = Date;
Ao filtrar uma tabela, a propriedade RecordCount da Table, só mostra o número de registros que satisfazem ao filtro, como se os outros registros nao existissem.
Alterando Registros
Para alterar registros em código, colocamos a tabela em modo de edição, alteramos o valor dos campos e gravamos as alterações, se for necessário.
with DtmPedidos do;
begin
Tbl.Edit;
TblData.Value := Date;
TblHora.Value := Time;
Tbl.Post;
end;
Inserindo Registros
Para inserir registros em código você pode usar os métodos AppendRecord e InsertRecord, caso você não precise de algum campo, mesmo assim ele deve ser informado com o valor Null.
DtmProd.Tbl.AppendRecord([Null, EdtDescricao.Text, EdtPreco.Text]);
Localizando Registros
Para localizar registros você pode usar vários métodos, mas o melhor deles é o Locate, no exemplo abaixo é feita uma pesquisa exata.
if not DtmCli.Tbl.Locate('CodCli', Edt.Text, []) then
ShowMessage('Cliente não encontrado.');
Você também pode fazer uma pesquisa parcial e/ou sem sensitividade de caso usando o terceiro parâmetro, que é um conjunto de opções.
DtmCli.Tbl.Locate('Nome', Edt.Text, [loPartialKey, loCaseInsensitive]);
Se você precisar fazer uma pesquisa por mais de um campo, separe os nomes dos campos por ponto e vírgula e use a função VarArrayOf para criar um array com os valores que você quer procurar.
if not DtmPed.Tbl.Locate('Vendedor;Data', VarArrayOf([EdtVendedor.Text, EdtData.Text]),
[loCaseInsensitive]) then
ShowMessage('O vendedor não realizou nenhuma venda nessa data');
Caso os campos pesquisados sejam indexados, a pesquisa será muito mais eficiente, senão será criado um filtro temporário da BDE para localizar os registros
Indexação
A indexação é usada para ordenar os registros da tabela, para isso você deve escolher os campos pelos quais você quer ordenar na propriedade IndexFieldNames, inclusive em código, como mostrado abaixo, todos campos devem ser indexados e separados por ponto e vírgula.
DtmCli.Tbl.IndexFieldNames := 'Nomcli';
DtmPed.Tbl.IndexFieldNames := 'Data, Vendedor';
Estados da Tabela
A propriedade State determina o estado das tabelas, os principais estados são demonstrados abaixo, veja como os métodos mudam o estado.

imagem4
Verificando Alterações
Onde for necessário a verificação de alterações feitas em uma Tabela, por exemplo no evento OnClose de um Form de manutenção, você pode usar a propriedade Modified, como mostrado no exemplo abaixo.
if DtmCli.Tbl.Modified then
if Application.MessageBox('Gravar alterações?', 'Dados Alterados', MB_ICONQUESTION
   + MB_YESNO) = IDYES then
   DtmCli.Tbl.Post
else
   DtmCli.Tbl.Cancel;
Valores Default
Caso você queira especificar valores Default para os campos de uma tabela, use o evento OnNewRecord, pois nesse evento o registro não é marcado como modificado.
TblData.Value := Date;
Percorrendo uma Tabela
Utilize um código semelhante ao mostrado abaixo para percorrer uma tabela do início ao fim.
Tbl.DisableControls;
Total := 0;
Tbl.First;
while not Tbl.EOF do
begin
Total := Total + TblValor.Value;
Tbl.Next;
end;
Tbl.EnableControls;
Forms Modais de Inclusão/Alteração
Para mostrar Forms Modais de inclusão ou alteração de registros utilize comandos como os mostrados abaixo.
TblCli.Insert;
if FormInsCli.ShowModal = mrOk then
TblCli.Post
else
TblCli.Cancel;
Mestre/Detalhe
Nos relacionamentos de 1 para N, uma tabela pode estar ligada a outra em uma relação Mestre/Detalhe, nesse tipo de relação os registros da tabela de ordem N são filtrados pelo campo de relacionamento com a tabela de ordem 1. Por exemplo, se o relacionamento de Clientes com Pedidos  for mestre/detalhe, só serão acessados em pedidos, os registros cujo campo CodCli seja igual ao CodCli da tabela de Clientes.
Para fazer esse tipo de relacionamento, siga os passos abaixo.
No uses da Unit detalhe, Pedidos, inclua a Unit da tabela mestre, Clientes
Na Table detalhe, Pedidos, Coloque em MasterSource o DataSource da tabela mestre, Clientes
Em MasterFields, chame o Fields Links Designer e escolha os campos de ligação das tabelas, no caso, CodCli para as duas tabelas
Fields Editor
Para criar objetos para os campos de uma tabela clique duas vezes no componente TTable ou escolha Fields Editor no seu menu de contexto, na janela do Fields Editor, clique com o botão direito do mouse e escolha Add, na janela Add Fields, escolha os campos que você vai querer usar e clique em Ok.
No Fields Editor podemos também remover os campos criados, alterar sua ordem de apresentação e usar suas propriedades e eventos no Object Inspector. Para cada campo é criado um objeto de um tipo descendente de TField, como TStringField, TIntegerField, TFloatField. As principais propriedades dos objetos TField estão listadas na tabela abaixo.
Se você não criar nenhum objeto TField, todos os campos da tabela estarão disponíveis, mas caso você crie algum, somente os campos que você criar estarão disponíveis.
Se você selecionar os campos no Fields Editor e arrastar para o Form, serão criados os controles visuais para esses campos, Label, DBEdit e outros, mas antes coloque a descrição dos campos na propriedade DisplayLabel.
TField
A classe TField é usada como ancestral para todos as classes dos campos. Geralmente iremos usar objetos de classes descendentes de TField, mas em todos eles podemos encontrar os itens mostrados abaixo.
Propriedades Descrição
Alignment Alinhamento do texto do campo nos controles visuais
AsBoolean Valor do campo convertido para Boolean
AsCurrency Valor do campo convertido para Currency
AsDateTime Valor do campo convertido para DataTime
AsFloat Valor do campo convertido para Double
AsInteger Valor do campo convertido para Integer
AsString Valor do campo convertido para string
AsVariant Valor do campo convertido para Variant
Calculated Indica se o campo é calculado em tempo de execução
CanModify Indica se um campo pode ser modificado
ConstraintErrorMessage Mensagem de erro se a condição de CustomConstraint não for satisfeita
CustomConstraint Condição de validação do campo
DataSet DataSet onde está o campo
DataSize Tamanho do campo, em Bytes
DataType Propriedade do tipo TFieldType, que indica o tipo do campo
DefaultExpression Expressão com valor Default do campo para novos registros
DisplayLabel Título a ser exibido para o campo
DisplayText Texto exibido nos controles visuais associados ao campo
DisplayWidth Número de caracteres que deve ser usado para mostrar o campo no controles visuais
EditMask Máscara de edição do campo
FieldKind Propriedade do tipo TFieldKind que indica o tipo do campo, como Calculado ou Lookup
FieldName Nome do campo na tabela
FieldNo Posição física do campo na tabela
Index Posição do campo nos controles visuais
IsIndexField Indica se um campo é válido para ser usado como índice
IsNull Indica se o campo está vazio
KeyFields Campo chave da tabela no relacionamento com LookupDataSet, usado em campos Lookup
Lookup Indica se um campo é Lookup
LookupCache Define se será usado cache para campos Lookup
LookupDataSet DataSet onde está definido o valor do campo Lookup
LookupKeyFields Campo chave do relacionamento em LookupDataSet
LookupResultField Valor do campo, que será mostrado nos controles visuais
ReadOnly Define se um campo é somente para leitura
Required Define se o campo é obrigatório
Size Tamanho físico do campo
Text Texto de edição do campo
Value Acesso direto ao valor do campo
Visible Define se um campo é visível
Eventos Descrição
OnChange Chamado quando o valor do campo é mudado
OnSetText Chamado pelos controles visuais para atribuir o texto digitado pelo usuário ao campo
OnGetText Chamado para formatar o texto de exibição do campo
OnValidate Validação do valor atribuído ao campo, caso o valor não seja válido, gere uma exceção
Método Descrição
Assign Atribui um valor de um campo a outro, inclusive nulo
FocusControl Seta o foco para o controle visual ligado ao campo nos Forms
Clear Limpa o conteúdo do campo
Estão listadas abaixo algumas classes que realmente iremos manipular no tratamento dos campos de uma tabela, são classes descendentes de TField.
TStringField TBlobField TTimeField
TSmallintField TIntegerField TBytesField
TFloatField TWordField TVarBytesField
TCurrencyField TAutoIncField TGraphicField
TBooleanField TBCDField TMemoField
TDateField TDateTimeField
Em alguns desses campos você pode encontrar as propriedades mostradas abaixo, que não estão presentes em TField.
Propriedades Descrição
MaxValue Valor máximo para o campo
MinValue Valor mínimo para campo
DisplayFormat Formato de apresentação do campo, como ,0.00” %” ou ,0.##” Km”
EditFormat Formato de edição do campo
Currency Define se um campo é monetário
DisplayValues Usado com campos Boolean, define o texto para True e False, como Sim;Não
Métodos Descrição
LoadFromFile Carrega o conteúdo do campo de um arquivo
SaveToFile Salva o conteúdo do campo para um arquivo
Para acessar os campo de uma tabela, existem várias abordagens, como mostrado abaixo..
Usando o objeto TField ligado ao campo.
TblDescricao.Value := TblVendedor.Value + ' em ' + TblData.AsString;
Usando a notação de colchetes. Se você não especificar nenhuma propriedade, é assumida a propriedade Value por padrão.
Tbl['Descricao'] := Tbl['Vendedor'] + ' em ' + Tbl['Data'].AsString;
Através do método FieldByName
Tbl.FieldByName('Descricao').Value := Tbl.FieldByName('Vendedor').Value + ' em '
+ Tbl.FieldByName('Data').AsString;
Usando a lista Fields do TTable
Tbl.Fields[5].Value := Tbl.Fields[3].Value + ' em ' + Tbl.Fields[4].AsString;
Conversão de Tipos
A conversão de tipo de um campo pode ser feita através as propriedades tipo As..., como AsString.
DtmPed.TblData.AsString := EdtData.Text;
Validação
Para validar os valores de um campo, você pode usar a propriedade CustomConstraint, por exemplo para garantir que a quantidade de um item seja maior que zero, use em CustomConstraint Quantidade > 0, e em CustomConstraint coloque a mensagem para o usuário caso a condição seja falsa. Outra forma, mais flexível, é usando o evento OnValidate, com um código como abaixo, onde é gerada uma exceção para cancelar a atribuição do valor ao campo.
if TblQuantidade.Value <= 0 then
raise Exception.Create('Quantidade deve ser maior que zero.');
Formatação Personalizada
Caso queira fazer uma formatação personalizada do campo, pode usar os eventos OnGetText e OnSetText. Por exemplo, se tiver um campo Estado, e quiser que quando o valor do campo for C fosse mostrado Casado S, Solteiro, no evento OnGetText use um código como o abaixo.
if TblEstado.Value = 'C' then
Text := 'Casado'
else if TblEstado.Value = 'S' then
Text := 'Solteiro';
Como controle visual para o usuário escolher o valor do campo, você poderia usar o DBComboBox, com Solteiro e Casado na propriedade Items, e no evento OnGetText do campo o código mostrado abaixo.
if Text = 'Casado' then
TblEstado.Value := 'C'
else if Text := 'Solteiro' then
TblEstado.Value = 'S';
Campos Calculados
Para criar campos calculados, clique com o direito no Fields Editor e escolha New Field, no quadro NewField, digite o nome do campo, o nome do objeto será automaticamente informado, o tipo do campo, seu tamanho e escolha Calculated em Field type.
Para colocar um valor nesse campo usaremos o evento OnCalcFields do componente TTable, em nenhuma outra parte os valores desses campos podem ser alterados.
O código do evento OnCalcFields deve ser enxuto, pois este é chamado várias vezes durante a edição de um registro e um procedimento pesado pode comprometer a performance do sistema.
procedure TDtmAluno.TblCalcFields(DataSet: TDataSet);
begin
if TblFaltas.Value > DtmTurma.TblMaxFaltas.Value then
   TblSituacao.Value := 'Evadido'
else if TblNota.Value >= 7 then
   TblSituacao.Value := 'Aprovado'
else
   TblSituacao.Value := 'Retido'
end;
Campos Lookup
Para fazer um relacionamento, às vezes precisamos criar um campo de descrição, por exemplo em uma biblioteca, na tabela de empréstimos, temos o código do Livro, mas gostaríamos de mostrar o Título, esses campos são chamados de campos Lookup.
Para criar um campo Lookup, siga os passos abaixo, tomando como exemplo o caso do livro no empréstimo.
Abra o Fields Editor do Table desejado, Empréstimos
Clique com o direito e escolha New Field
No quadro New Field, escolha as propriedades do campo como descrito em campos calculados, mas em Field type, escolha Lookup
Em Key Fields escolha o campo da tabela que faz parte do relacionamento, CodLivro
DataSet é a tabela onde está a descrição, Livros
Em Lookup Keys, escolha o campo de DataSet que faz parte do relacionamento, CodLivro
Finalmente, escolha em Result field o campo de DataSet que vai ser mostrado para o usuário, Título
Essas opções correspondem a algumas propriedades do objeto TField gerado, que podem ser alteradas no Object Inspector, KeyFields, LookupDataSet, LookupKeyFields, LookupDataSet e LookupResultField.
Quando esses campo são exibidos em um DBGrid, por padrão é criado um botão de lookup que mostrará os valores da outra tabela uma lista. Para colocar esses campos em um Form, devemos usar o DBLookupComboBox, apenas com as propriedades padrão, DataSource e DataField, onde deve ser escolhido o campo Lookup, quando você arrastar o campo para o Form isso será feito automaticamente.
TDataSource
Componente usado para fazer a ligação entre um DataSet e os componentes visuais.
Propriedade Descrição
AutoEdit Define se a tabela entrará em modo de edição assim que o usuário digitar novos valores nos controles
DataSet DataSet ao qual o TDataSource faz referência
Evento Descrição
OnDataChange Ocorre quando o DataSet é alterado, ao mudar de registro ou mudar os valores dos campos
OnStateChange Ocorre quando o estado do DataSet é alterado
OnUpdateData Ocorre antes de uma atualização
Botões de Navegação Personalizados
O DBNavigator tem os principais botões necessários para a navegação por uma tabela, contudo se você quiser criar seus próprios botões de navegação, o que não é recomendado, no evento OnClick desses botões deve ser chamados os métodos de navegação, como indicado abaixo.
DtmCli.Tbl.Next;
Para controlar a habilitação dos botões de navegação use o evento OnDataChange do DataSource correspondente como indicado abaixo.
BtnProx.Enabled := not DtmCli.Tbl.EOF;
Para criar botões de controle, como inclusão e exclusão, use o evento OnStateChange do DataSource como indicado abaixo para controlar a habilitação.
BtnAlterar.Enabled := DtmCli.Tbl.State = dsBrowse;
Data Controls
Controles usados na interface com o usuário. Todos esses componentes tem uma propriedade DataSource, que deve ter o DataSource do Table ao qual estão ligados.
TDBNavigator
O DBNavigator permite que o usuário realize operações padrão de controle de dados. Cada um dos botões do DBNavigator chama um método do Componente Table ao qual está ligado.
Podemos personalizar o DBNavigator usando as suas propriedades e eventos, mas se quisermos mudar a figura dos botões teremos que editar diretamente o arquivo LIB\DBCTRLS.RES, na pasta do Delphi.
Propriedades Descrição
VisibleButtons Define os botões que serão visíveis
Hints Hints exibidos pelos botões
ConfirmDelete Define se será solicitado uma confirmação antes da exclusão
Eventos Descrição
BeforeAction Quando um botão do Navigator é pressionado, antes da ação ser executada
OnClick Quando um botão do Navigator é pressionado, depois da ação ser executada
TDBGrid
Mostra os registros de uma tabela em forma de grade, cada coluna é um campo e cada registro, uma linha.
Propriedades Descrição
Columns Lista do tipo TDBGridColumns, com as colunas da Grid, cada item da lista é do tipo TColumn
Fields Lista de  objetos TField mostrados nas colunas
Options Set com as opções da Grid, como ConfirmDelete, MultiSelect, ColumnResize
SelectedField Campo da coluna selecionada
SelectedIndex Índice da coluna selecionada
SelectedRows Lista do tipo TBookmarkList, com os registros selecionados em uma Grid com MultiSelect
TitleFont Fonte do título das colunas
FixedColor Cor Fixa, usada nas colunas e indicadores
Eventos Descrição
OnCellClick Ao clicar em uma célula da Grid
OnColEnter Quando uma célula de alguma coluna da Grid recebe o foco
OnColExit Quando uma célula de alguma coluna da Grid perde o foco
OnColumnMoved Quando o usuário mover uma coluna
OnDrawDataCell Evento usado para personalizar a forma de desenhar os dados que são apresentados na Grid
OnEditButtonClick Ao clicar no botão de edição de uma célula, mostrado pela propriedade ButtonStyle da coluna
OnTitleClick Ao clicar no título das colunas
TColumn
Item de uma lista TDBGridColumns, usada na propriedade Columns da Grid, objetos desse tipo representam uma coluna da Grid. Às vezes as propriedades definidas para o campo sobrepõem as propriedades
Propriedades Descrição
ButtonStyle Botão mostrado ao editar as células da coluna
Field Objeto TField ligado à coluna
FieldName Nome do campo ligado à coluna
PickList TStrings com os itens da lista DropDown usada nas células da coluna
Title Propriedade do tipo TColumnTitle com as opções do título da coluna
TDBText, TDBEdit, TDBMemo, TDBListBox, TDBComboBox, TDBImage, TDBRichEdit
Controles genéricos ligados a um campo de uma tabela.
Propriedades Descrição
DataField Campo ao qual o controle está ligado
TDBCheckBox
Usado em campos que podem receber apenas dois valores, como campos lógicos.
Propriedades Descrição
ValueChecked Valor a ser armazenado quando está selecionado
ValueUnchecked Valor a ser armazenado quando não está selecionado
TDBRadioGroup
Mostra algumas opções para o preenchimento de um campo.
Propriedades Descrição
Values Valor a ser armazenado para cada botão de rádio
TDBLookupListBox, TDBLookupComboBox
Preenche um campo com dados contidos em outra tabela. Se o campo mostrado nesses componentes for um campo Lookup, você não precisa especificar nenhuma das propriedades abaixo, apenas DataSource e DataField.
Propriedades Descrição
ListSource DataSource que contém os valores a serem exibidos na lista
ListField Campo de ListSource que será exibido
KeyField Campo de ListSource usado no relacionamento
Exercícios
1. Crie uma aplicação que cadastre os Clientes de uma empresa e as Compras feitas por estes Clientes, permita inclusão, alteração, exclusão e consulta aos dados cadastrados. Na janela principal fica o cadastro de Clientes, com a grade de visualização de suas Compras, crie também uma Janela para localizar Clientes por Nome.
A tabela de clientes deve ter Nome, Endereço, Bairro, Cidade, Estado, CEP e Telefone, defina também índices para melhorar a localização de clientes por Nome. Na tabela de Compras, deseja-se saber a Data, Produtos e Valor, assuma que cada compra tem um Produto apenas. Como foi mencionado, as compras serão cadastradas pelo cliente atual, crie a relação Mestre/Detalhe entre Clientes e Compras.
O Form de localização de Clientes deve permitir pesquisa Nome, da mesma forma da questão anterior.
2. Uma academia de ginástica deseja manter um controle maior sobre seus Alunos, para isso ela organizou os clientes em turmas. Os dados de uma Turma são Número de alunos, Horário da aula, Duração da aula, Data inicial, Data final e Instrutor. Deve ser feita também uma tabela de instrutores para evitar a digitação repetitiva do Nome. Os dados dos Alunos são Matrícula, Data de Matrícula, Nome, Endereço, Bairro, Cidade, Estado, Telefone, Data de nascimento, Altura e Peso. Crie um banco de dados, normalizado, para guardar essas informações.
No cadastro de Turmas, o Horário de aulas deve ser entre 7:00 e 18:00, a Duração não pode ser maior que 2 horas e a Data Final tem que ser, no mínimo 5 dias após a Inicial. Esse cadastro deve ser ordenado primeiro pela Data Final, em ordem decrescente e depois pelo Horário, em ordem crescente. As turmas já encerradas não devem ser mostradas no cadastro, mas crie um arquivo morto com as turmas já encerradas, onde os dados não possam ser alterados. Deve ser possível também procurar o Instrutor pelo Nome, usando um ComboBox, com os registros da tabela de Instrutores.
No cadastro de Alunos, a matrícula é Auto-incremental, a Data de Matrícula deve ser, obrigatoriamente, a Data do sistema e deve ser criado um campo calculado com o peso ideal do cliente, altura menos 1,15.

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'

Como usar a cláusula UNION em um Query

O uso do componente TQuery gera muitas vantagens e economiza muitas linhas de programação. Mas muitas vezes nos deparamos com situações que parecem não ser resolvidas com sentenças SQL. Vejamos um exemplo:

Você possui 2 tabelas (VendasExternas e VendasInternas) e deseja fazer um resumo de todas as vendas de um vendedor chamado Carlos. Se você usar a sentença

SELECT Nome, Valor FROM VendasExternas, VendasInternas
WHERE Nome = 'Carlos'

você vai obter como resultado uma query com 4 campos (Nome, Valor, Nome_1 e Valor_1) e um resultado bem confuso para ser manipulado.

Para resolver o problema, você poderá usar a sentença

SELECT Nome, Valor FROM VendasExternas
WHERE Nome = 'Carlos'
UNION ALL
SELECT Nome, Valor FROM VendasInternas
WHERE Nome = 'Carlos'

A sentença acima pede para que sejam identificados as vendas de Carlos na tabela VendasExternas, as vendas de Carlos na tabela VendasInternas e que o resultado da primeira seja unido com o resultado da segunda produzindo uma query com apenas 2 colunas.

Mostrando progresso de uma SQL

A tecnica apresentada nao apenas serve para o proposito procurado, mas tambem serve para mostrar o progresso de diversas outras atividades que o BDE executa, como:

* Criacao de tabelas
* Criacao de indices para tabelas
* Reestruturacao de tabelas
* Execucao de queries (ja comentado)
* alguma outra coisa que no momento nao me ocorre... :))


Importante:

1) No meu exemplo, estou usando o Delphi 3.02. Caso seu Delphi seja de uma versao menor, vc devera ter um trabalho extra para repor a classe TBDECallback. Acredito que seja possivel fazer uma rotina que funcione em Delphi 1, mas que com certeza dara um certo trabalhinho, ah, isso dara... :-/

2) Ate agora so usei esse codigo com tabelas Paradox, mas realmente acredito que ele venha a funcionar com base de dados Interbase, Oracle, etc...

3) Nao sei se com o uso do Opus, Apollo ou qualquer outro substituto do BDE a tecnica ira funcionar, uma vez que nao se estaria trabalhando com o BDE original. Talvez alguem da lista possa dar essa informacao.

Teoria
=====

Segundo o help do Delphi, "o TBDECallback eh um wrapper para uma funcao de callback do BDE. Com ele eh possivel instruir o BDE para que o mesmo execute algumas tarefas em resposta a eventos que ocorram durante uma chamada de uma funcao do BDE. " - Fim do plagio do arquivo de help.

O tipo de callback depende de um parametro CBType que eh fornecido no momento da criacao do TBDECallback. E, entre os diversos valores que o CBType pode apresentar, existe um que muito nos interessa; o cbGENPROGRESS. :))

Assim, vc deveria criar uma funcao de callback do tipo cbGENPROGRESS chamada AtualizaGauge e indicar que a mesma eh que devera ser executada "entre cada respiracao" do BDE. Na rotina AtualizaGauge, o BDE iria te informar o percentual de progresso da tarefa . O que voce faria nessa rotina ? Simples... atualizar o Gauge / ProgressBar. 

Tudo muito bonito, tudo muito comovente, mas agora vamos para o lado pratico...
 
Pratica
======
Para que o BDE possa informar o progresso da tarefa, ele precisa obter essa informacao da base de dados que esta sendo utilizada. Acontece que, por razoes diferentes, nem sempre ele eh capaz de saber o PERCENTUAL da tarefa. Numa copia de registros de uma tabela para outra, ele pode saber que ja foram copiados 270 registros, mas nao saber que esse esforco representa 36 % de todos os registros que serao copiados.
Assim sendo, na funcao de callback que sera criada, receberemos um parametro do tipo pCBPROGRESSDesc, que por sua vez eh um ponteiro para uma estrutura que contem duas informacoes:

iPercentDone => percentual do servico realizado
szMsg => texto descrevendo o progresso do servico.

Como usar esses parametros ? Simples: sempre que o iPercentDone for negativo, voce devera considerar o texto descrito no campo szMsg. Se for igual ou maior que zero, entao vc devera considerar o valor do proprio iPercentDone.

Uma boa noticia para quem se preocupa com as mensagens que aparecem em ingles, quando se quer na verdade mostra-las em portugues: a mensagem fornecida por szMsg devera sempre aparecer no formato <:> .....
Exemplo:

Records copied: 170

Assim, voce pode procurar pelos dois pontos ":" e pegar o valor que vem a seguir para montar sua propria informacao em portugues.

Pessoalmente, ate agora nunca obtive um iPercentDone positivo. Li no newsgroup da Borland que poucas bases de dados eram capazes de informar o real percentual para o BDE. Se nao me engano, o Sybase era um deles... NAO ESTOU CERTO DISSO.

Vamos para um exemplo pratico ? Crie um projeto novo, e coloque um: TQuery, TButton, TProgressBar e TLabel.
Sua query deve ser montada para abrir uma tabela razoavelmente grande, de modo que a operação de abertura demore um pouco.

Agora vamos aos codigos:

1) Acrescente a unit BDE no seu USES da unit.

2) Acrescente algumas declarações na declaração do seu Form:
==============================
type
TForm1 = class(TForm)
... (bla bla bla)
private
{ Private declarations }
FCBPROGRESSDesc: pCBPROGRESSDesc;
FProgressCallback: TBDECallback;
function GetDataCallback(CBInfo: Pointer): CBRType;
public
{ Public declarations }
end;
==============================

No evento OnCreate do seu Form:
==============================
procedure TForm1.FormCreate(Sender: TObject);
begin
  FCBPROGRESSDesc := AllocMem(SizeOf(CBPROGRESSDesc));
  FProgressCallback := TBDECallback.Create(Self, Query1.Handle,
  cbGENPROGRESS, FCBPROGRESSDesc, SizeOf(CBPROGRESSDesc),
  GetDataCallback, True);
end;
==============================

Percebam que no segundo parametro do Create do callback, eu coloquei Query1.Handle.
Caso voce queira usar isso numa TTable, coloque Table1.Handle. E se quiser que essa funcao de callback seja chamada para todos os "progressos" de qualquer componente DataSet, voce deixa esse parametro como NIL.

No evento OnDestroy do Form:
==============================
procedure TForm1.FormDestroy(Sender: TObject);
begin
FProgressCallback.Free;
FreeMem(FCBPROGRESSDesc, SizeOf(CBPROGRESSDesc));
end;
==============================
E agora, a tao falada funcao de callback:
==============================
function TForm1.GetDataCallback(CBInfo: Pointer): CBRType;
begin
 Result := cbrCONTINUE;
 with pCBPROGRESSDesc(CBInfo)^ do
 begin
 if iPercentDone < 0 then
 begin
 Label1.Caption := szMsg;
 Label1.Refresh;
 ProgressBar1.StepIt; {Apenas para ficar rodando o gauge}
 end
 else
 ProgressBar1.Position := iPercentDone;
 end;
 end;
==============================
 Agora é só executar a query no clicar do botao e curtir o visual... :))
 
IMPORTANTE !!!!!!

Caso voce receba uma mensagem de erro informando que nao foi possivel inicializar o BDE (o que provavelmente acontecera, pois voce esta criando uma funcao de callback do BDE, quando ate entao nenhuma tabela havia sido aberta), va no DPR do seu projeto (Menu View -> Project Source) e faca o
seguinte:

1) Acrescente a unit BDE no uses do projeto.
2) Acrescente a instrucao

DbiInit(nil);

apos a instrucao Application.Initialize;

Isso deve resolver o problema.

Verificando atributo do arquivo

Para verificar um atributo de um arquivo, crie uma variável do tipo word, por exemplo, Attributes. Depois, atribua a esta variável o valor retornado por FileGetAttr. Ex.:


var
Attributes: Word;
begin
Attributes := FileGetAttr( 'nomedoarquivo' );
// Supondo 4 CheckBoxe's, 1 para cada atributo, Ok?
CheckBox1.Checked := (Attributes and faReadOnly) = faReadOnly;
CheckBox2.Checked := (Attributes and faArchive) = faArchive;
CheckBox3.Checked := (Attributes and faSysFile) = faSysFile;
CheckBox4.Checked := (Attributes and faHidden) = faHidden;
end;

quarta-feira, 1 de setembro de 2010

Obtendo a data do último dia do mês, ou último dia útil, de uma data informada

Function LastDayOfMonth (Data : TDateTime; lSabDom : Boolean) : TDateTime;
var
    Ano, Mes, Dia : word;
    AuxData : TDateTime;
    DiaDaSemana : Integer;
begin
    AuxData := FirstDayOfMonth (NextMonth (Data), False) - 1;
    if lSabDom Then begin
        DecodeDate (Auxdata, Ano, Mes, Dia);
        DiaDaSemana := DayOfWeek (AuxData);
        if DiaDaSemana = 1 Then
            Dia := Dia - 2
        else
        if DiaDaSemana = 7 Then
            Dec (Dia);
        AuxData := EnCodeDate (Ano, Mes, Dia);
    end;
    LastDayOfMonth := AuxData;
end;

Fazendo cálculo de horas

var
    hora1: TDateTime;
    hora2: TDateTime;
    total: TDateTime;
begin
    hora1 := StrToTime(Edit1.Text);
    hora2 := StrToTime(Edit2.Text);
    total := Hora2 - Hora1;
    Label1.Caption := FormatDateTime('hh:nn:ss',total);
end;

Descobrir se uma data é fim do mês

Esta função retorna true se a data passada como parâmetro é fim de mês. Retorna false caso contrário.

Inclua na seção uses: SysUtils

function tbFimDoMes(const Data: TDateTime): boolean;
var
  Ano, Mes, Dia: Word;
begin
  DecodeDate(Data +1, Ano, Mes, Dia);
  Result := Dia = 1;
end;

terça-feira, 31 de agosto de 2010

Definir data/hora de um arquivo

Inclua na seção uses: SysUtils

{ Esta função altera a data e hora de um arquivo. Se obter sucesso retorna true, caso contrário retorna false. }
function DefineDataHoraArq(NomeArq: string; DataHora: TDateTime): boolean;
var
  F: integer;
begin
  Result := false;
  F := FileOpen(NomeArq, fmOpenWrite or fmShareDenyNone);
  try
  if F > 0 then
  Result := FileSetDate(F, DateTimeToFileDate(DataHora)) = 0;
  finally
  FileClose(F);
  end;
end;
{ Exemplo de uso 1: Usa a data atual do sistema (Now) }

if DefineDataHoraArq('c:\teste\logo.bmp', Now) then
  ShowMessage('Data/Hora do arquivo definida com sucesso.')
else
  ShowMessage('Não foi possível definir data/hora do arquivo.');

{ Exemplo de uso 2: Usa uma data fixa }
var
  DataHora: TDateTime;
begin
  { Define a data para 5-Fev-1999 e a hora para 10:30 }
  DataHora := EncodeDate(1999, 2, 5) + EncodeTime(10, 30, 0, 0);
  if DefineDataHoraArq('c:\teste\logo.bmp', DataHora) then
  ShowMessage('Data/Hora do arquivo definida com sucesso.')
  else
  ShowMessage('Não foi possível definir data/hora do arquivo.');
end;

Converte hora (formato HH:MM) para minutos

Function HoraToMin(Hora: String): Integer;
begin
    Result := (StrToInt(Copy(Hora,1,2))*60) + StrToInt(Copy(Hora,4,2));
end;

Como saber se o ano é bisexto

function TForm1.AnoBiSexto(Ayear: Integer): Boolean;
begin
    Result := (AYear mod 4 = 0) and ((AYear mod 100 <> 0) or
    (AYear mod 400 = 0));
end;
 
procedure TForm1.Button1Click(Sender: TObject);
begin
    if AnoBiSexto(1999) Then
        ShowMessage('Ano de 1999 é Bisexto')
    Else ShowMessage('Ano de 1999 não é Bisexto');
end;

Como saber quantos dias tem no mes

function TForm1.AnoBiSexto(Ayear: Integer): Boolean;
begin
    // Verifica se o ano é Bi-Sexto
    Result := (AYear mod 4 = 0) and ((AYear mod 100 <> 0) or
    (AYear mod 400 = 0));
end;
 
function TForm1.DiasPorMes(Ayear, AMonth: Integer): Integer;
const DaysInMonth: array[1..12] of Integer = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
begin
    Result := DaysInMonth[AMonth];
    if (AMonth = 2) and AnoBiSexto(AYear) then
    Inc(Result);
end;
 
procedure TForm1.Button1Click(Sender: TObject);
begin
    Label1.Caption := IntToStr(DiasPorMes(1999, 10));
end;

Como formatar data para exibição por extenso

O Delphi permite formatar datas para apresentação por extenso de forma bastante simples. Vejamos os seguintes exemplos:

Para formatar a data 18/03/1999, podemos utilizar:

No create do form colocar

Shortdateformat:=

dddd, dd/mm/yyyy = Quinta, 18/03/1999
dd/mmm/yyyy = 18/Mar/1999
dddd, dd" de "mmmm" de "yyyy = Quinta, 18 de Março de 1999
dd" de "mmmm" de "yyyy, dddd = 18 de Março de 1999, Quinta

segunda-feira, 30 de agosto de 2010

Enviando combinação de teclas para o buffer do teclado

Exemplo : PostKeyEx32(Ord('A'), [ssCtrl], false); Envia Ctrl+A para o controle que tiver o foco. Key : virtual keycode da tecla a enviar. Para caracteres imprimíveis informe o código ANSI (Ord(CHARACTER)). Shift : estado das teclas modificadoras. Shift, Control, Alt, Mouse Buttons.  SpecialKey: normalmente deve ser False. Informe True se a tecla desejada for, por exemplo, do teclado numérico.  

procedure PostKeyEx32(Key: Word; const Shift: TShiftState; SpecialKey: boolean);
type
    TShiftKeyInfo = Record
    shift: Byte;
    vkey : Byte;
End;
    byteset = Set of 0..7;
    const
    ShiftKeys: array [1..3] of TShiftKeyInfo =
    ((shift: Ord(ssCtrl); vkey: VK_CONTROL ),
    (shift: Ord(ssShift); vkey: VK_SHIFT ),
    (shift: Ord(ssAlt); vkey: VK_MENU ));
    var
    Flag: DWORD;
    bShift: ByteSet absolute shift;
    i: Integer;
begin
    for i := 1 to 3 do begin
        if shiftkeys[i].shift in bShift then
            Keybd_Event(ShiftKeys[i].vkey,
        MapVirtualKey(ShiftKeys[i].vkey, 0), 0, 0);
    end; // for
    if SpecialKey Then
        Flag := KEYEVENTF_EXTENDEDKEY
    else
        Flag := 0;
    Keybd_Event(Key, MapvirtualKey(Key, 0), Flag, 0);
    Flag := Flag or KEYEVENTF_KEYUP;
    Keybd_Event(Key, MapvirtualKey(Key, 0), Flag, 0);
    for i := 3 DownTo 1 do begin
        if ShiftKeys[i].shift in bShift then
            Keybd_Event(shiftkeys[i].vkey,
        MapVirtualKey(ShiftKeys[i].vkey, 0),
        KEYEVENTF_KEYUP, 0);
    end; // for
end; // PostKeyEx32 

Virtual keys

vk_LButton = $01;
vk_RButton = $02;
vk_Cancel = $03;
vk_MButton = $04; { NOT contiguous with L & RBUTTON }
vk_Back = $08;
vk_Tab = $09;
vk_Clear = $0C;
vk_Return = $0D;
vk_Shift = $10;
vk_Control = $11;
vk_Menu = $12;
vk_Pause = $13;
vk_Capital = $14;
vk_Escape = $1B;
vk_Space = $20;
vk_Prior = $21;
vk_Next = $22;
vk_End = $23;
vk_Home = $24;
vk_Left = $25;
vk_Up = $26;
vk_Right = $27;
vk_Down = $28;
vk_Select = $29;
vk_Print = $2A;
vk_Execute = $2B;
vk_SnapShot = $2C;
vk_Copy = $2C {not used by keyboards }
vk_Insert = $2D;
vk_Delete = $2E;
vk_Help = $2F;
{vk_A thru vk_Z are the same as their ASCII equivalents: 'A' thru 'Z' }
{ vk_0 thru vk_9 are the same as their ASCII equivalents: '0' thru '9' }
vk_NumPad0 = $60;
vk_NumPad1 = $61;
vk_NumPad2 = $62;
vk_NumPad3 = $63;
vk_NumPad4 = $64;
vk_NumPad5 = $65;
vk_NumPad6 = $66;
vk_NumPad7 = $67;
vk_NumPad8 = $68;
vk_NumPad9 = $69;
vk_Multiply = $6A;
vk_Add = $6B;
vk_Separator = $6C;
vk_Subtract = $6D;
vk_Decimal = $6E;
vk_Divide = $6F;
vk_F1 = $70;
vk_F2 = $71;
vk_F3 = $72;
vk_F4 = $73;
vk_F5 = $74;
vk_F6 = $75;
vk_F7 = $76;
vk_F8 = $77;
vk_F9 = $78;
vk_F10 = $79;
vk_F11 = $7A;
vk_F12 = $7B;
vk_F13 = $7C;
vk_F14 = $7D;
vk_F15 = $7E;
vk_F16 = $7F;
vk_F17 = $80;
vk_F18 = $81;
vk_F19 = $82;
vk_F20 = $83;
vk_F21 = $84;
vk_F22 = $85;
vk_F23 = $86;
vk_F24 = $87;
vk_NumLock = $90;
vk_Scroll = $91;

Colocando funções em uma DLL

Edite diretamente no DPR, e depois salve como Funcoes.dpr:

Library Funcoes;

Uses SysUtils,WinTypes,WinProcs;
{ Uma função que tira os espaços no início e no final de uma string }

Function Trim(J:String):String; Export;
Begin
While J[Length(J)]=#32 do Dec(J[0]);
If Length(J)>1 then
While (J[1]=' ') do
Begin
Delete(J,1,1);
If Length(J)<=1 then J:='';
end;
Result:=J;
end;
Exports { Torna visivel para os programas }
Trim;
Begin
End.
Para usar num programa:

Unit Unit1;
Interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
Forms, Dialogs, StdCtrls, Buttons;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
Var
Form1: TForm1;
Implementation
{ Declara a funcao }
Function Trim(J:String):String; External 'funcoes.dll';
{$R *.DFM}
Procedure TForm1.FormClick(Sender: TObject);
begin
Caption:=Trim(' Visite sempre o Delphi Club '); { Note os espacos }
end;
 
As vantagens de colocar as funções em DLL são:

1. O programa exigirá menos memória

2. Você poderá reaproveitar as funções

3. Em alguns casos pode-se atualizar apenas as dll para um upgrade

Como atribuir um valor inicial para uma variável global

No Delphi, pode-se atribuir um valor inicial para uma variável global enquanto a declara. É possível escrever, por exemplo:

var
  Value: Integer = 10;
  Correct: Boolean = True;

Esta técnica de inicialização funciona apenas para variáveis globais, não para variáveis declaradas no escopo de um procedimento ou método.

quarta-feira, 18 de agosto de 2010

Traduzindo a mensagem "Delete Record ?"

Quando clicamos sobre o botão de deleção no DBNavigator (o do sinal de menos) surge uma box com a mensagem "Delete Record?" com botões Ok e Cancel.

Para fazer aparecer a mensagem em português deverá selecionar o componente Table e mudar a propriedade ConfirmDelete para False e no evento da tabela BeforeDelete colocar o seguinte:

procedure TForm1.Table1BeforeDelete(DataSet:TDataSet);
begin
if MessageDlg('Eliminar o Registro?',mtConfirmation,[mbYes,mbNo],0)<>mrYes then Abort;
end;

terça-feira, 17 de agosto de 2010

Enviar um email

 smtp.postmessage.toAddress := 'StringList (por ex uma listbox';
smtp.postmessage.FromAdreess := 'ex: meu_email@123.pt';
smtp.userid := 'ex: user@123.pt'
smtp.host := 'ex: smtp@123.pt'
smtp.postmessage.subject := 'Assunto'
smtp.postmessage.body := 'Texto da mensagem (stringlist)'

smtp.connect;
smtp.sendmail;
smtp.disconnect;
Contribuição:
O Anonymous.nick enviou um complemento explicando melhor o procedimento para enviar um e-mail usando o Delphi.

Fazer um aplicativo completo para manipulação de e-mails é um tanto trabalhoso e não é o assunto desta dica. Muitas vezes, porém, queremos apenas dar ao nosso software a capacidade de enviar simples e-mails. Isto é fácil, especialmente porque o Delphi5 nos oferece o componente TNMSMTP (paleta FastNet) que faz praticamente todo o trabalho para nós. Precisamos apenas alterar algumas propriedades e chamar alguns métodos para que a mensagem seja enviada. Vamos para a prática:

1. Coloque um componente TNMSMTP no form.

2. Coloque um botão e no evento OnClick deste botão escreva:

procedure TForm1.Button1Click(Sender: TObject);
begin

  { Seu servidor SMTP }
  NMSMTP1.Host := 'smtp.servidor.com.br';

  { Porta SMTP, **NÃO MUDE ISTO** }
  NMSMTP1.Port := 25;

  { Nome de login do usuário }
  NMSMTP1.UserID := 'MeuLogin';

  { Conecta ao servidor }
  NMSMTP1.Connect;

  { Se ocorrer algum erro durante a conexão com o servidor, avise! }
  if not NMSMTP1.Connected then
  raise Exception.Create('Erro de conexão');

  with NMSMTP1.PostMessage do begin
  { Seu e-mail }
  FromAddress := 'meuemail@meuserver.com.br';

  { Seu nome }
  FromName := 'Meu Nome';

  { E-mail do destinatário }
  ToAddress.Clear;
  ToAddress.Add('destinatario@servidor.com.br');

  { Assunto da mensagem }
  Subject := 'Assunto da mensagem';

  { Corpo da mensagem }
  Body.Clear;
  Body.Add('Primeira linha da mensagem');
  Body.Add('Segunda linha da mensagem');
  Body.Add(''); { Linha em branco }
  Body.Add('Última linha da mensagem');

  { Anexar arquivos(Se não quiser anexar arquivos, apague as 3 linhas seguintes) }

  Attachments.Clear;

  { Endereço do anexo }
  Attachments.Add('c:\diretorio\arquivo.ext');

  end;

 { Manda o e-mail }
  NMSMTP1.SendMail;
 { Disconecta do servidor }
  NMSMTP1.Disconnect;
end;


Pronto! É só fazer as adaptações necessárias e você terá envio de e-mails em sua aplicação.

Observações:
Para enviar o mesmo e-mail para vários destinatário de uma só vez basta adicionar os endereços de e-mails de todos os destinatários em NMSMTP1.PostMessage.ToAddress

Como saber se estou conectado à internet

interface
uses
Windows, SysUtils, Registry, WinSock, WinInet;

type
TConnectionType = (ctNone, ctProxy, ctDialup);

function ConnectedToInternet : TConnectionType;
function RasConnectionCount : Integer;


implementation

const
cERROR_BUFFER_TOO_SMALL = 603;
cRAS_MaxEntryName = 256;
cRAS_MaxDeviceName = 128;
cRAS_MaxDeviceType = 16;
type
ERasError = class(Exception);

HRASConn = DWord;
PRASConn = ^TRASConn;
TRASConn = record
dwSize: DWORD;
rasConn: HRASConn;
szEntryName: Array[0..cRAS_MaxEntryName] Of Char;
szDeviceType : Array[0..cRAS_MaxDeviceType] Of Char;
szDeviceName : Array [0..cRAS_MaxDeviceName] of char;
end;

TRasEnumConnections =
function (RASConn: PrasConn; { buffer para receber dados da conexao}
var BufSize: DWord; { tamanho em bytes do buffer }
var Connections: DWord { numero de conexoes escritas no buffer }
): LongInt; stdcall;


function ConnectedToInternet: TConnectionType;
var
    Reg : TRegistry;
    bUseProxy : Boolean;
    UseProxy : LongWord;
begin
    Result := ctNone;
    Reg := TRegistry.Create;
    with REG do
    try
    try
        RootKey := HKEY_CURRENT_USER;
        if OpenKey('\Software\Microsoft\Windows\CurrentVersion\Internet settings',False) then begin
            //I just try to read it, and trap an exception
            if GetDataType('ProxyEnable') = rdBinary then
                ReadBinaryData('ProxyEnable', UseProxy, SizeOf(LongWord) )
        else begin
            bUseProxy := ReadBool('ProxyEnable');
            if bUseProxy then
                UseProxy := 1
            else
                UseProxy := 0;
        end;
        if (UseProxy <> 0) and ( ReadString('ProxyServer') <> '' ) then Result := ctProxy;
    end;
    except
        //Nao conectado com proxy
    end;
    finally
    Free;
end;

    if Result = ctNone then begin
    if RasConnectionCount > 0 then Result := ctDialup;
    end;
    end;

function RasConnectionCount : Integer;
var
    RasDLL : HInst;
    Conns : Array[1..4] of TRasConn;
    RasEnums : TRasEnumConnections;
    BufSize : DWord;
    NumConns : DWord;
    RasResult : Longint;
begin
    Result := 0;

    //Load the RAS DLL
    RasDLL := LoadLibrary('rasapi32.dll');
    if RasDLL = 0 then exit;

    try
        RasEnums := GetProcAddress(RasDLL,'RasEnumConnectionsA');
        if @RasEnums = nil then
        raise ERasError.Create('RasEnumConnectionsA not found in rasapi32.dll');

        Conns[1].dwSize := Sizeof (Conns[1]);
        BufSize := SizeOf(Conns);

        RasResult := RasEnums(@Conns, BufSize, NumConns);

        If (RasResult = 0) or (Result = cERROR_BUFFER_TOO_SMALL) then Result := NumConns;
    finally
    FreeLibrary(RasDLL);
end;
end;

segunda-feira, 16 de agosto de 2010

Como pegar a URL ativa no Browser

Uses ddeman;

function GetURL(Service: string): String;
var
    ClDDE: TDDEClientConv;
    temp:PChar;
begin
    Result := '';
    //create a new DDE Client object
    ClDDE:= TDDEClientConv.Create( nil );
    with ClDDE do
    begin
        SetLink(Service,'WWW_GetWindowInfo');
        temp := RequestData('0xFFFFFFFF');
        Result := StrPas(temp);
        StrDispose(temp);
        CloseLink;
    end;
    ClDDE.Free;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
    ShowMessage(GetURL('IExplore'));
end;

Como descobrir se você esta conectado com a Internet?

1º) Você deve acrescentar um componente NMFTP (da paleta FastNet).
2º) Insira o seguinte código no evento OnShow do formulário.
If (NMFtp1.GetLocalAddress <> '0,0,0,0') Then ShowMessage('Você não está conectado!') Else ShowMessage('Você está conectado!');
3º) Execute o programa e veja o resultado.

By Carlos Naves - http://www.carlosnaves.hpg.com.br/

Como conectar uma unidade de rede

procedure TForm1.Button1Click(Sender: TObject);
var
  NRW: TNetResource;
begin
  with NRW do
  begin
  dwType := RESOURCETYPE_ANY;
  lpLocalName := 'G:';
  lpRemoteName := '\\servidor\c';
  lpProvider := '';
  end;
  WNetAddConnection2(NRW, 'MyPassword', 'MyUserName', CONNECT_UPDATE_PROFILE);
end;

Como compartilhar uma pasta de um outro micro e mapear com uma letra

var
err : DWord;
PServer, PSenha, PLetra : PChar;
Begin
PServer := '\\Caminho\Caminho' + #0;
PLetra := 'L:';
PSenha := '';

ERR := WNetAddConnection ( PServer , PSenha , PLetra );

CASE ERR of
ERROR_ACCESS_DENIED : ShowMessage ( 'Acesso negado.' );
ERROR_ALREADY_ASSIGNED : ShowMessage ( 'A letra do drive especificada já está conectada.' );
ERROR_BAD_DEV_TYPE : ShowMessage ( 'O tipo de dispositivo e o tipo de recurso não são compatíveis.' );
ERROR_BAD_DEVICE : ShowMessage ( 'Letra inválida.' );
ERROR_BAD_NET_NAME : ShowMessage ( 'Nome do servidor não é válido ou não pode ser localizado.' );
ERROR_BAD_PROFILE : ShowMessage ( 'Formato incorreto de parâmetros.' );
ERROR_CANNOT_OPEN_PROFILE : ShowMessage ( 'Conexão permanente não disponível.' );
ERROR_DEVICE_ALREADY_REMEMBERED : ShowMessage ( 'Uma entrada para o dispositivo especificado já está no perfil do usuário.' );
ERROR_EXTENDED_ERROR : ShowMessage ( 'Erro de rede.' );
ERROR_INVALID_PASSWORD : ShowMessage ( 'Senha especificada inválida.' );
ERROR_NO_NET_OR_BAD_PATH : ShowMessage ( 'A operação não foi concluída porque a rede não foi inicializada ou caminho é inválido.' );
ERROR_NO_NETWORK : ShowMessage ( 'A rede não está presente.' );
else if Err > 0 then
ShowMessage (IntToStr(Err));
end;
end;

Obs.:Se "PLetra" for deixada em branco, o acesso será liberado sem ser criada uma unidade lógica.

Chamar um site utilizando o seu browse padrão

Uses UrlMon;

Procedure TForm1.Button1Click(Sender: TObject);
Begin
    HlinkNavigateString(nil,'http://www.lloydsoft.hpg.ig.com.br');
End;

Chamar um e-mail pelo Delphi

procedure TForm1.Button1Click(Sender: TObject);
var
    Mail : String;
begin
    Mail := 'mailto:rudineirosa@gmail.com';
    ShellExecute(GetDesktopWindow,'open',pchar(Mail),nil,nil,sw_ShowNormal);
end;

Bloqueando um arquivo em ambiente de rede

Quando você programar visando uma rede e quiser bloquear um arquivo, é só chamar o metodo "Edit" da Tabela que estiver usando.

Exemplo:

Table1.edit;
Se o registro já estiver bloqueado, ocorrerá um erro, então você deve fazer o seguinte :

try { para verificar o erro }
Table1.edit;
exception on TDBEngineError do { o erro..}
MensageDlg('Registro ja esta sendo usado...!',mtInformation,[ mbOk ],0 );
end;
Nao use o DBNavigation

sexta-feira, 13 de agosto de 2010

Ligar/desligar a tecla Caps Lock

Inclua na seção uses: Windows

{ Esta função liga/desliga Caps Lock, conforme o parãmetro State }

procedure tbSetCapsLock(State: boolean);
begin
  if (State and ((GetKeyState(VK_CAPITAL) and 1) = 0)) or
  ((not State) and ((GetKeyState(VK_CAPITAL) and 1) = 1)) then
  begin
  keybd_event(VK_CAPITAL, $45, KEYEVENTF_EXTENDEDKEY or 0, 0);
  keybd_event(VK_CAPITAL, $45, KEYEVENTF_EXTENDEDKEY or KEYEVENTF_KEYUP, 0);
  end;
end;

{ Exemplos de uso: }
tbSetCapsLock(true); { Liga Caps Lock }
tbSetCapsLock(false); { Desliga Caps Lock }
Observações

Aparentemente, podemos usar esta mesma técnica para ligar/desligar Num Lock. Neste caso trocaríamos VK_CAPITAL por VK_NUMLOCK. Por incrível que pareça não funcionou (pelo menos no teste que fiz). E tem mais: isto está na documentação do (R)Windows.

Descobrir o código ASCII de uma tecla

- Coloque um Label no form (Label1);

- Mude a propriedade KeyPreview do form para true;

- Altere o evento OnKeyDown do form como abaixo:

procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
      Label1.Caption :=
      Format('O código da tecla pressionada é: %d', [Key]);
end;

Observações

Para testar execute e observe o Label enquanto pressiona as teclas desejadas.

Como usar as teclas de função F1, F2, etc

- Para você colocar chamadas usando as teclas de função basta colocar o seguinte código no evento 'OnKeyDown' do formulário:

procedure Tform1.FormKeyDown(Sender:TObject; var Key: Word; Shift: TShifState);
begin
    if key = vk_F1 then begin
        { instrucoes a serem executadas }
    end;
end;

- OBSERVAÇÃO:
Não se esqueça de colocar a propriedade 'KeyPreview' do formulário em 'True'.

Você também pode usar as variáveis VK_F1 até VK_F12 referentes as outras teclas de função.

quinta-feira, 12 de agosto de 2010

Traduzindo a mensagem "Delete Record ?"

Quando clicamos sobre o botão de deleção no DBNavigator (o do sinal de menos) surge uma box com a mensagem "Delete Record?" com botões Ok e Cancel.

Para fazer aparecer a mensagem em português deverá selecionar o componente Table e mudar a propriedade ConfirmDelete para False e no evento da tabela BeforeDelete colocar o seguinte:

procedure TForm1.Table1BeforeDelete(DataSet:TDataSet);
begin
if MessageDlg('Eliminar o Registro?',mtConfirmation,[mbYes,mbNo],0)<>mrYes then Abort;
end;

Tradução de Mensagens

Depois de algum tempo pesquisando uma forma de fazer aparecer as mensagens em português, consegui uma solução muito fácil de implementar no ambientede programação do Delphi.

CHEGA DE YES/NO !!!

messagedlg('Confirma ? mtConfirmation, [mbYes, mbNo], 0);

Aí vai:

1 - No diretório DELPHI\LIB, copie o arquivo consts.dcu para consts.old;

2 - Inicie o Delphi e crie um nova Unit;

3 - Insira nesta, o arquivo consts.int do diretório DELPHI\DOC E faça as devidas alterações nas mensagens que desejares alterar e nas partes duplicadas da Unit como "implement" e etc, também deixe o cabeçalho como Unit Consts.

4 - Salve esta nova Unit no diretório DELPHI\LIB e pronto todas as mensagens alteradas por você estarão aplicadas nos seus próximos programas sem uma linha de programa e da forma que você quiser.

No Delphi você deve nenomear o arquivo consts.dcu para consts.old e modificar o arquivo constst.pas. (Há outros arquivos *consts*.pas que podem ser modificados).

Como saber se o CD está no drive

Function MidiaPresente(MediaPlayer: TMediaPlayer): Boolean;
var
Params: MCI_STATUS_PARMS;
S: array [0.255] of char;
r: Integer;
begin
//verifica se existe um cd inserido
Params.dwItem:= MCI_STATUS_MEDIA_PRESENT;
r:= MCISendCommand(MediaPlayer.DeviceID, MCI_STATUS, MCI_STATUS_ITEM, Integer(Addr(Params)));
if r <> 0 then
begin
MCIGetErrorString(r, S, SizeOf(S));
ShowMessage('Erro: ' + StrPas(S));
end
else
Result:= Params.dwReturn = 1;
end;

Relatórios em HTML

Relatórios em HTMLEm vez de Quickreport1.Print faca :

QuickRep1.ExportToFilter(TQRHtmlExportFilter.Create('teste.html'));

FindNearest numa Query

Query1.Locate('campo onde ira porcurar','Texto a buscar',[loPartialKey,loCaseInsensitive]);

Preenche com quantidade determinada de zeros o lado esquerdo de uma string

unit Zero;
interface
function RetZero(ZEROS:string;QUANT:integer):String;
implementation
function RetZero(ZEROS:string;QUANT:integer):String;
var
I,Tamanho:integer;
aux: string;
begin
  aux:=zeros;
  Tamanho:=length(ZEROS);
  ZEROS:='';
  for I:=1 to quant-tamanho do
  ZEROS:=ZEROS+'0';
  aux:=zeros+aux;
  RetZero:=aux;
end;
end.

Gera número por extenso

 unit Ext;
interface
function extenso (valor: real): string;
implementation
uses
  SysUtils, Dialogs;
function extenso (valor: real): string;
var
Centavos, Centena, Milhar, Milhao, Texto, msg: string;
const
Unidades: array[1..9] of string = ('Um', 'Dois', 'Tres', 'Quatro', 'Cinco', 'Seis', 'Sete', 'Oito', 'Nove');
Dez: array[1..9] of string = ('Onze', 'Doze', 'Treze', 'Quatorze', 'Quinze', 'Dezesseis', 'Dezessete', 'Dezoito', 'Dezenove');
Dezenas: array[1..9] of string = ('Dez', 'Vinte', 'Trinta', 'Quarenta', 'Cinquenta', 'Sessenta', 'Setenta', 'Oitenta', 'Noventa');
Centenas: array[1..9] of string = ('Cento', 'Duzentos', 'Trezentos', 'Quatrocentos', 'Quinhentos', 'Seiscentos', 'Setecentos', 'Oitocentos', 'Novecentos');
function ifs(Expressao: Boolean; CasoVerdadeiro, CasoFalso: String): String;
begin
if Expressao
then Result:=CasoVerdadeiro
else Result:=CasoFalso;
end;

function MiniExtenso (trio: string): string;
var
Unidade, Dezena, Centena: string;
begin
Unidade:='';
Dezena:='';
Centena:='';
if (trio[2]='1') and (trio[3]<>'0') then
  begin
  Unidade:=Dez[strtoint(trio[3])];
  Dezena:='';
end
else
 begin
  if trio[2]<>'0' then Dezena:=Dezenas[strtoint(trio[2])];
  if trio[3]<>'0' then Unidade:=Unidades[strtoint(trio[3])];
 end;
if (trio[1]='1') and (Unidade='') and (Dezena='')
 then Centena:='cem'
else
 if trio[1]<>'0'
  then Centena:=Centenas[strtoint(trio[1])]
  else Centena:='';
 Result:= Centena + ifs((Centena<>'') and ((Dezena<>'') or (Unidade<>'')), ' e ', '')
  + Dezena + ifs((Dezena<>'') and (Unidade<>''),' e ', '') + Unidade;
end;
begin
if (valor>999999.99) or (valor<0) then
 begin
  msg:='O valor está fora do intervalo permitido.';
  msg:=msg+'O número deve ser maior ou igual a zero e menor que 999.999,99.';
  msg:=msg+' Se não for corrigido o número não será escrito por extenso.';
  showmessage(msg);
  Result:='';
  exit;
 end;
if valor=0 then
 begin
  Result:='';
  Exit;
 end;
Texto:=formatfloat('000000.00',valor);
Milhar:=MiniExtenso(Copy(Texto,1,3));
Centena:=MiniExtenso(Copy(Texto,4,3));
Centavos:=MiniExtenso('0'+Copy(Texto,8,2));
Result:=Milhar;
if Milhar<>'' then
  if copy(texto,4,3)='000' then
  Result:=Result+' Mil Reais'
  else
  Result:=Result+' Mil, ';
if (((copy(texto,4,2)='00') and (Milhar<>'')
  and (copy(texto,6,1)<>'0')) or (centavos=''))
  and (Centena<>'') then Result:=Result+' e ';
if (Milhar+Centena <>'') then Result:=Result+Centena;
if (Milhar='') and (copy(texto,4,3)='001') then
  Result:=Result+' Real'
 else
  if (copy(texto,4,3)<>'000') then Result:=Result+' Reais';
if Centavos='' then
 begin
  Result:=Result+'.';
  Exit;
 end
else
 begin
  if Milhar+Centena='' then
  Result:=Centavos
  else
  Result:=Result+', e '+Centavos;
if (copy(texto,8,2)='01') and (Centavos<>'') then
  Result:=Result+' Centavo.'
 else
  Result:=Result+' Centavos.';
end;
end;
end.

Verifica Validade de CGC e CPF

unit CPFeCGC;

interface
function cpf(num: string): boolean;
function cgc(num: string): boolean;

implementation
uses SysUtils;


function cpf(num: string): boolean;
var
    n1,n2,n3,n4,n5,n6,n7,n8,n9: integer;
    d1,d2: integer;
    digitado, calculado: string;
begin
    n1:=StrToInt(num[1]);
    n2:=StrToInt(num[2]);
    n3:=StrToInt(num[3]);
    n4:=StrToInt(num[4]);
    n5:=StrToInt(num[5]);
    n6:=StrToInt(num[6]);
    n7:=StrToInt(num[7]);
    n8:=StrToInt(num[8]);
    n9:=StrToInt(num[9]);
    d1:=n9*2+n8*3+n7*4+n6*5+n5*6+n4*7+n3*8+n2*9+n1*10;
    d1:=11-(d1 mod 11);
    if d1>=10 then d1:=0;
    d2:=d1*2+n9*3+n8*4+n7*5+n6*6+n5*7+n4*8+n3*9+n2*10+n1*11;
    d2:=11-(d2 mod 11);
    if d2>=10 then d2:=0;
    calculado:=inttostr(d1)+inttostr(d2);
    digitado:=num[10]+num[11];
    if calculado=digitado then
          cpf:=true
      else
          cpf:=false;
end;


function cgc(num: string): boolean;
var
    n1,n2,n3,n4,n5,n6,n7,n8,n9,n10,n11,n12: integer;
    d1,d2: integer;
    digitado, calculado: string;
begin
    n1:=StrToInt(num[1]);
    n2:=StrToInt(num[2]);
    n3:=StrToInt(num[3]);
    n4:=StrToInt(num[4]);
    n5:=StrToInt(num[5]);
    n6:=StrToInt(num[6]);
    n7:=StrToInt(num[7]);
    n8:=StrToInt(num[8]);
    n9:=StrToInt(num[9]);
    n10:=StrToInt(num[10]);
    n11:=StrToInt(num[11]);
    n12:=StrToInt(num[12]);
    d1:=n12*2+n11*3+n10*4+n9*5+n8*6+n7*7+n6*8+n5*9+n4*2+n3*3+n2*4+n1*5;
    d1:=11-(d1 mod 11);
    if d1>=10 then d1:=0;
    d2:=d1*2+n12*3+n11*4+n10*5+n9*6+n8*7+n7*8+n6*9+n5*2+n4*3+n3*4+n2*5+n1*6;
    d2:=11-(d2 mod 11);
    if d2>=10 then d2:=0;
    calculado:=inttostr(d1)+inttostr(d2);
    digitado:=num[13]+num[14];
    if calculado=digitado then
          cgc:=true
    else
          cgc:=false;
end;
end.

Protegendo o seu programa e o seu bolso

  Caso você seja um desenvolvedor contratado por alguma empresa criando sistemas específicos para cada cliente talvez isto nunca seja uma preocupação séria pois há poucas possibilidades do sistema interessar outra pessoa física ou jurídica com características operacionais diferentes (não estou me referindo a código-fonte aqui mas ao produto acabado).

Mas caso você esteja no mercado de softwares para o público em geral, bem-vindo ao grande clube dos programas pirateados. Este artigo visa introduzir alguns conceitos de proteção de software para programadores em geral mas antes de começarmos há uma coisa muito importante sobre a qual gostaria de dizer: Muitas grandes empresas liberam softwares com baixa proteção contra pirataria exatamente visando uma divulgação indireta do produto (ocorre muito com componentes para Delphi, a maioria possui uma chave publicamente divulgada). É uma tática que dá certo em países aonde há uma certa lei e consciência por parte dos usuários com software mas isto acredito não se aplica ao Brasil.Um conselho: não faça isso ! Ninguém vai comprar aqui o seu produto e a quantidade que comprar não cobrirá os custo do desenvolvimento.

Você poderá distribuir o seu software como produto de prateleira ou como um shareware pela Internet. A grande vantagem do shareware é a divulgação boca à boca (ou seria clique à clique ?) que ele oferece mas o ideal é as duas abordagens ao mesmo tempo. Isto possibilita que o usuário que ficou hesitante em adquirir o produto na loja possa baixar e testá-lo. Obviamente nunca esqueça de divulgar o seu site na embalagem.

Para vender software, um bem intangível materialmente, é preciso um bom sistema de proteção pois o seu programa poderá virar um freeware em poucos segundo. É só encontrar um hacker que goste dele. Interessante é que dificilmente um hacker irá quebrar a proteção de um produto do qual não goste ou não use, portanto quando encontrar cracks para o seu programa em vários sites da web sinta-se elogiado ! Depois pode ficar triste por não ter recebido um tostão com o seu trabalho.

Algumas técnicas de proteção (os prós e contras)
Para avaliação do software:
Nag-Screen: O programa sempre exibe uma tela avisando que é uma cópia de execução restrita (ou algo semelhante) e solicita o registro do mesmo por um determinando período.

Prós: O Usuário sempre é lembrado com uma tela de aviso que está com uma cópia restrita ou não totalmente funcional. Alguns nag-screens apenas roubam um pouco do tempo de uso do software mas não chegam a atrapalhar o uso efetivo, o que é ótimo para o usuário: a lógica da comida grátis que vicia.

Contras: Se a limitação for apenas o nag-screen todo mundo se acostuma com ela e depois de um certo tempo até se esquece que ela existe. Não é eficiente.

Período: Um programa funciona dentro de um certo período de avaliação.

Prós: O usuário usará todo o software com todos os recursos e poderá testar tudo.

Contras: É comum o usuário esquecer que está com uma cópia de avaliação. Alguns softwares, com uma péssima proteção, podem ser contornados simplesmente voltando o calendário do sistema.

Recursos Chaves Desabilitados: O programa possui alguns recursos que não funcionam como opções para salvar, exportar etc.

Prós: O Usuário tem mais liberdade que todos os métodos acima e sempre que tentar fazer o salvamento de dados lembrará que está com um software não totalmente funcional forçando uma compra.

Contras: É uma das formas mais fáceis de um programa receber um crack para liberar o recurso protegido.

Para licenciamento do software:
Número Serial: O Programa possui um número único serial que o habilita completamente.

Prós: O usuário somente digita o serial e pronto, o programa está liberado. Ideal para softwares de prateleira aonde o usuário já adquiriu o produto. O sistema preferido para a divulgação indireta.

Contras: Centenas de outros usuários somente digitam o mesmo serial e pronto: centenas de programas registrados. Use esta forma caso não deseje receber pelo seu programa ou queira uma divulgação indireta do mesmo e esperar pela consciência do usuário, isso não funciona no Brasil. Centenas ou milhares de usuário podem usar o mesmo serial. Esta caindo em desuso rapidamente pois há centenas de sites com index sofisticados de seriais para qualquer programa o que torna o sistema quase completamente inútil hoje em dia.

HardLocks: Um pequeno dispositivo é colocado na porta serial, paralela ou USB com uma identificação única para liberar o funcionamento do sistema.

Prós: Um dos sistemas mais difíceis de serem quebrados e definitivamente o mais seguro. Garante a taxa de uma licença por máquina o que é o ideal. Para sistemas caros (algo acima de $1000,00) considere seriamente o uso de HardLocks.

Contras: Custo do HardLock por cópia licenciada e alguns problemas (raros) com periféricos usando aquela mesma porta, mas nada tão sério assim para prejudicar o usuário. As portas seriais e paralelas estão caindo em desuso com o USB e já existem HardLocks para elas também. Os Hardlocks pode apresentar defeitos dependendo do tipo.

Você deve adquirir um kit de gravação e um Hardlock para cada cópia de distribuição. Não é muito caro mas o seu software deve compensar isto obviamente.

Disquete de Habilitação: Um disquete é fornecido junto com o programa para a sua habilitação ou desabilitação.

Prós: É o hardlock dos pobres sem o mesmo nível de proteção.

Contras: Não faça proteção via disquetes de habilitação/desabilitação como o Dr. Case e outros. É perda de tempo ! Há vários utilitários que fazem a cópia perfeita do disquete sendo possível habilitá-lo em qualquer máquina. E ainda pode-se ter o problema de superfície no disco e um belo dia quando o usuário precisar instalar o software em outra máquina ele terá a surpresa do disco já mofado ou perdido. Tenha certeza que isto sempre acontece no final de semana quando ele liga para o seu escritório na segunda descarregando o seu vocabulário.

Identidade Única: Uma das melhores e mais eficientes tipos de proteções é de identidade do equipamentos recentemente implementado pela própria Microsoft. Isto consiste em recolher dados únicos sobre o seu computador como serial do HD, informação da BIOS (não aconselhável), Versão do OS, Nome do computador etc. Com base nestas informações você poderá gerar uma fechadura de identificação. Essa fechadura precisará de uma chave fornecida por você para habilitar o seu software unicamente para aquela máquina.

Prós: Difícil de ser quebrado quando bem implementado e permite alta adaptabilidade contra cracks criado contra o sistema como patchs invisíveis que permitem a modificação do sistema. Permite a taxa de apenas uma licença por computador.

Contras: Caso o usuário formate a maquina, modifique periféricos chaves o sistema pode desabilitar a cópia automaticamente. Isto cria problemas com a solicitação de uma nova licença para instalação. O usuário pode simplesmente ligar alegando este fato e de boa fé você terá que fornecer uma nova liberação.

Habilitação Pela Internet: Ao adquirir um produto o usuário recebe uma senha que permite a geração de um número para habilitação e instalação em uma máquina.

Prós: Após habilitar, o número é automaticamente invalidado para outros usuário o que evita o problema de divulgação do mesmo pela rede.

Contras: O seu site deve estar em um provedor 100% confiável ou esteja preparado para aquele cliente que comprou o produto no sábado a tarde e o seu site ficou fora do ar no domingo sem ele poder autorizar o software para o uso.

Conclusão
Não há método melhor ou pior na minha opinião para proteger um programa mas, mais trabalhoso ou menos trabalhoso para um hacker quebrar. O Ideal é que que você use duas ou mais técnicas para proteger o programa.

Há um princípio básico que ao se usar dois sistemas simples de proteção independentes cria-se um sistema forte de proteção. Por exemplo, caso use o sistema de data é fácil monitorar aonde se esta gravando esta informação (quando o programa foi instalado) e adiar o tempo limite de uso. Nag-Screens podem ser rastreadas e um hacker com bons conhecimentos de assembler pode editar diretamente o seu código binário fazendo jumps nas avaliações de restrições. Agora combinando duas ou três técnicas as coisas complicam para ele e talvez o trabalho não valha a pena (coisa que ele realmente não é acostumado).

Mas antes de começar a desenvolver o sistema de proteção para o seu programa, siga algumas dicas que aprendemos depois de muito prejuízo com pirataria.

1- Nunca coloque literalmente as mensagens referentes a registro, nag-screens, avisos de limitação e etcs, em um formato legível para um ser humano. Faça uma encriptação destas mensagens. Uma técnica bem simples dos hackers consiste em procurar no código binário por uma determinada ocorrência tipo " Cópia Trial" e perto tem um "IF" aonde se pode bloquear e lá se foi a sua proteção. Portanto, faça uma função de codificação e decodificação e grave todas as mensagens codificadas no seu programa (no código-fonte) sendo que as mesmas somente serão exibidas quando você chamar a função de decodificação.

2- Faça uma estampa no software. Ou seja, todos os programas possuem uma informação referente ao seu tamanho físico, data e hora de criação e CRC. Caso o seu programa tenha sido alterado após aquela data, através de uma função, o programa poderá verificar em sua estampa interna se ele foi modificado. Aqui pode ocorrer alguns problemas com programas anti-virus que usam a mesma técnica para monitorar alteração de executáveis aplicando uma "vacina" neles mas já é da responsabilidade do usuário.

3- Grave dados no registry sempre encriptados e, se eles forem sobre as informações de registro nunca coloque na mesma árvore das outras definições do seu software. Isto dificulta e muito as coisa. Mas se o hacker possui um programa de monitoramento do registry ele poderá facilmente contornar isto mas não antes sem um bom trabalho de adivinhação. E, nunca use chaves com nomes óbvios tipo "Serial", "SenhaPrograma", "DataLimite" e etc. Dê sempre preferência a gravar dados de configuração e autorização no registry do que em arquivos INI (lembra-se deles ?). O registry é mais difícil de ser manipulado por um leigo e é possível colocar chaves observando a mudança de outras chaves o que torna as coisas mais complexas.

4- Se possível, implemente um sistema periódico de verificação da licença via site na web.

Coloque este alerta em seu termo de uso do seu software e o faça de tal forma que seja invisível, indolor (não fique interrompendo o usuário) e que desabilite o software imediatamente ao verificar uma cópia com o número de série irregular ou divulgado. É importante aqui implementar nos CGI do site um alerta via e-mail quando ocorrer várias tentativas de liberação de um mesmo serial. Assim você descobre rapidamente quem vazou um serial para o público.

5- Tenha uma proteção não divulgada. Por exemplo, após o software executar 300 vezes uma mensagem surge do nada indicando o download de uma cópia mais atualizada. Nesta nova cópia você já deve ter contornado todos os problemas com cracks que apareceram no período.

6- Faça Check Point em pontos não óbvios do seu software e em vários lugares. Use uma periodicidade completamente aleatória para fazer isto. Por exemplo, quando o usuário abrir uma determinada tela não muito utilizada, ocorre uma verificação em background sobre a validade da licença esta verificação pode e deve ser aleatória. Após validar e algo estiver errado, nunca exiba no mesmo momento que a licença é irregular pois se torna uma referência para o Hacker aonde ele deve procurar no código binário para quebrar o seu programa. Lembre-se que tudo serve como marcação no código binário: um caption de um form, o conteúdo string de um controle etc.

7- Use constantes. Abuse delas em vez de escrever diretamente nos dialogs para comunicação com usuário. Faça uma Unit separada aonde somente é armazenado estas constantes. Isso confunde quem olha o código binário do executável pois fisicamente no executável elas não estarão próximas.

Todas estas informações foram adquiridas com a nossa experiência e, principalmente, com dicas de dois conhecidos especialistas em quebras de programas e criação de cracks. Na primeira divulgação de um dos nossos softwares um deles me retornou uma cópia crackeada em questão de horas (segundo ele, estava sem tempo por isso demorou tanto), e ficamos envergonhados para dizer a verdade. Depois ao implementarmos a codificação de string e outras técnicas, ele levou uma semana mas não conseguiu liberar todos os recursos. Ao ativarmos os sistemas randômicos de verificação em background e validação pelo site ainda não tivemos uma quebra. Claro que ela irá ocorrer, mas enquanto isto estamos recebendo pelo nosso trabalho.

Acho que seguindo estas dicas você terá uma boa chance de receber pelo seu trabalho na quantidade justa. Lembre-se que há uma legião de pessoas ai fora tentando quebrar a proteção pelo simples prazer de dizer que foram eles.

Portanto a questão não é fazer um sistema 100% seguro (o que é quase impossível) mas um que seja 110% trabalhoso para eles. Tenha em mente também que eles são em maior número e problema não é se vão quebrar a segurança mas quando e o que você vai fazer em seguida.

Não poderíamos terminar este artigo sem falar sobre alguns componentes para proteção.

Uma coisa que percebi é que certos componentes de proteção são tão complicados que você as vezes tem que passar uma semana implementando ele no seu sistema. Mas aqui estão alguns que são ótimos pela simplicidade e fácil implementação.:

OnGuard (TurboPower) - Não é propaganda pessoal ! Os componentes da TurboPower tem uma grande qualidade e o OnGuard faz tudo o que se precisa de proteção. Validação e criação de serial, limitação por data, estampa no executável, dias de execução e muito mais. A grande vantagem é que há exemplos para tudo que podem ser fácil e rapidamente adaptados.

Lock&Key - Gosto do Lock&Key pela sua simplicidade de uso. Ele gera uma "fechadura" com base nos dados do HD do usuário e com este número-fechadura você cria um número único para liberar o software. Trabalha com níveis o que é ideal para proteção progressiva. Não estamos usando mais este, apesar de sua boa qualidade.

SharewareIt - Crie sistemas de shareware de forma bem simples. A vantagem é que é gratuito.

Criando uma base de dados MS Access pelo Delphi

Resumo:

Aprenda como criar uma base de dados MS Access sem o MS Access. Cria a base, as tabelas, índices, enfim, tudo utilizando puro código delphi.

INTRODUÇÃO:

Quando se cria um sistema para ambientes desktop sempre surge a dúvida de qual base de dados usar. Geralmente são usados bancos DBase, Paradox ou MS Access. Destes, a base mais robusta e confiável é, sem dúvida, MS Access. Mas existe um grande problema para se criar a base de dados MS Access, pois faz-se necessário o uso do ambiente MS Access.

Algumas pessoas não têm este aplicativo instalado em sua máquina e então torna-se inviável o uso desta base de dados, impedindo, desta forma, um crescimento tecnológico do programador que fica preso a ferramentas obsoletas.

Neste tutorial você irá aprender como criar uma base de dados MS Access a partir do nada, usando puro código Delphi e a Tecnologia ADO Extensions que é distribuída pela Microsoft.

ADOX, faz parte dos componentes ADO, quer dizer, é uma extensão do ADO. O ADOX fornece ferramentas de acesso a estrutura, segurança, definições de tabelas e muitos outros.

Como dito anteriormente, ADOX é uma library distribuída pela Microsoft, o arquivo chama-se "Msadox.dll", sua definição é "Microsoft ADO Ext. 2.x for DDL and Security" e é este arquivo que iremos importar para nossa IDE no Delphi.

INSTALANDO:

Para usar este objetos no Delphi basta seguir os seguintes passos:

1- Selecione PROJECT > IMPORT TYPE LIBRARY

2- Procure pela descrição: "Microsoft ADO Ext. 2.x for DDL and Security (Version 2.x)"

2- Em CLASS NAMES, altere o nome dos objetos acrescentando ADOX após a letra T, exemplo: TTable mude para TADOXTable, TColumm mude para TADOXColumn. Repita este procedimento para todos objetos nesta lista.

3- Em PALETTE PAGE selecione ou digite um novo nome para a paleta onde os componentes ficarão, exemplo: ADOX.

4- Pressione INSTALL, logo depois pressione Ok confirmando o início da instalação.

5- Pressione YES confirmando que você quer instalar os componentes.

6- Pressione Ok na tela que indica os objetos instalados.

7- Selecione FILE > CLOSE ALL e pressione YES para salvar este package criado.

O motivo da troca do nome dos objetos é muito óbvio, estes nomes de classe como Ttable já existem, então iria gerar conflitos na compilação, por isso bastou trocar o nome da classe.

Pronto, os objetos estão instalados, agora sempre que você utilizar estes objetos será inserido na clausula USES a Unit ADOX_TLB pois este é o nome da unit criada a partir da importação da DLL.

INICIANDO:

DEFININDO A BASE DE DADOS E OBJETOS A SEREM USADOS
Vamos criar uma base onde serão armazenados informaçõe sobre animais de estimação (para sair um pouco da rotina de CLIENTES/PRODUTOS/PEDIDOS).

Para esta base serão criadas as seguintes tabelas:

> PROPRIETARIO

> PRO_ID

> PRO_NOME

>ANIMAL

> ANI_ID

> ANI_PROPRIETARIO

> ANI_NOME

> ANI_NASCIMENTO

Onde um proprietario pode ter mais de um animal formando assim um relacionamento UM PARA MUITOS.

No Delphi, crie uma nova aplicação. Será criado um novo Form, a este insira os seguintes componentes:

> 3 TButtons

Para lançar os procedimentos de criação da base de dados e das tabelas.

Altere as seguintes propriedades para cada TButtons respectivamente:

Caption: Criar base

Name: btnBase

Caption: Criar tabelas

Name: btnTabelas

Caption: Navegar

Name: btnNavegar

> 1 TEdit

Para armazenar o path da base de dados a ser criada.

Altere as seguintes propriedades:

Name: edtPath

Text: (deixe em branco)

> 1 TSaveDialog

Para navegar no disco e informar o path da base de dados.

Altere as seguintes propriedades:

Filter: Base MS Access|*.mdb

Title: Salvar como...

DefaultExt: .mdb

> 1 TADOConnection

Para fazer a conexão com a base criada.

Altere as seguintes propriedades:

Login prompt: False

> 1 TADOCommand

Para fazer a ligação e criação das tabelas.

Altere as seguintes propriedades:

Connection: Selecione o ADOConnection1

> 1 TADOXCatalog

Para criar a base de dados.

CRIANDO A BASE DE DADOS:
Agora vamos ao código. Clique duas vezes no objeto btnNavegar e digite:

procedure TForm1.btnNavegarClick(Sender: TObject);
begin
  if SaveDialog1.Execute then
  edtPath.Text := SaveDialog1.FileName;
end;
Com isso informamos o nome que a base terá.

Clique duas vezes no objeto btnBase e digite o seguinte procedimento:

procedure TForm1.btnBaseClick(Sender: TObject);
var
  Base: String;
begin
  if edtPath.Text = '' then
  begin
  ShowMessage('Nome da base de dados não informada.');
  exit;
  end;
  Base := 'Provider=Microsoft.Jet.OLEDB.4.0'+
  ';Data Source=' + edtPath.Text +
  ';Jet OLEDB:Engine Type=4';
  ADOXCatalog1.Create1(Base);
end;
Primeiro verificamos se há algum texto no objeto TEdit, em seguida atribuímos a string de conexão à variável BASE informando vários parâmetros, mas atente para a seguinte linha: "...Engine Type=4...", isto quer dizer que iremos criar uma base Access 97, para Access 2000 informe 5.

Em seguida é efetivamente criado a base de dados através do método Create1 do objeto ADOXCatalog, passando para este a string da BASE. Observe que o método é Create1 e não simplesmente Create, pois o método Create já existe e é da classe.

Pronto, criamos uma base de dados vazia, não existe nada nela, mas já é um arquivo comum ao MS Access e pode ser aberto normalmente.

CRIANDO TABELAS:

Vamos começar a criar as tabelas, seus índices e integridade referencial. Para isso clique duas vezes no objeto btnTabelas e digite:

procedure TForm1.btnTabelasClick(Sender: TObject);
var
  base, comando: string;
begin
  { definindo a base de dados }
  base := 'Provider=Microsoft.Jet.OLEDB.4.0' +
  ';Data Source=' + edtPath.Text +
  ';Persist Security Info=False';
  ADOConnection1.ConnectionString := base;
  { Criando as tabelas... }
  {>>> PROPRIETARIO <<<}
  comando := 'CREATE TABLE PROPRIETARIO (' +
  'PRO_ID INT,' +
  'PRO_NOME TEXT(50))';
  ADOCommand1.CommandText := comando;
  ADOCommand1.Execute;
  { ADICIONANDO INDICES }
  comando := 'CREATE INDEX IDX_PRO_ID ' +
  'ON PROPRIETARIO (PRO_ID) WITH PRIMARY';
  ADOCommand1.CommandText := comando;
  ADOCommand1.Execute;
  {>>> ANIMAL <<<}
  comando := 'CREATE TABLE ANIMAL (' +
  'ANI_ID INT,' +
  'ANI_PROPRIETARIO INT ' +
  'CONSTRAINT IDX_PRO_ID ' +
  'REFERENCES PROPRIETARIO (PRO_ID),' +
  'ANI_NOME TEXT (50),' +
  'ANI_NASCIMENTO DATETIME)';
  ADOCommand1.CommandText := comando;
  ADOCommand1.Execute;
end;
 
CONCLUÍNDO:

Pronto, tudo muito fácil e simples. Agora rode o programa e faça os testes. Clique em navegar, selecione um diretório e digite o nome que sua base terá, então clique em CRIAR BASE e veja que o programa criará a base, logo após isto clique em CRIAR TABELAS então as tabelas serão criadas.

Agora ficou fácil criar sistemas desktops usando uma base mais robusta sem a necessidade de se ter o MS Access instalado em sua máquina. É possível criar e acessar todos os recursos de tabelas da base de dados MS Access usando os objetos ADOX, aqui foi mostrado como criar utilizando linguagem DDL, ou seja, escrevemos diretamente para que o comando fosse executado, mas é possível ter acesso à estes recursos diretamente com os componentes distribuídos por esta library, mas este assunto ficará para outra ocasião.

Se você tiver o MS Access instalado em sua máquina pode abri-lo e verificar nossa base de dados, caso contrário (como é o meu caso) crie uma simples aplicação com dois DBGrids para exibir os campos das tabelas, assim como inserir dados.

Cuidado quando gravar arquivos binarios

Quando gravar em arquivos binários deve evitar o uso em seus records de longStrings pois, por default, uma longstring é uma string "sem fim" . Se vc tentar gravar em um arquivo binário ele irá truncá-la(ou seja, vai cortar uma parte da string) e vc poderá perder dados. Ao invés disso, use shorstrings para arquivos binários...

Criar sub-diretório no diretório do EXE

Inclua na seção uses: FileCtrl, SysUtils

Problema:

Gostaria de criar um sub-diretório dentro do diretório onde se encontra o EXE de minha aplicação. Como fazer?

Solução:

Primeiramente vamos conhecer algumas funções do Delphi que precisaremos usá-las:

ParamStr(Indice) - Retorna valores passados na linha de comando quando executamos o programa. Se o valor de Indice for 0 (zero) será retornado o caminho+nome do EXE.

ExtractFilePath(NomeArq) - Retorna o caminho (path) do nome de arquivo informado.

Exemplo:

S := 'C:\NomeDir\Programa.exe';
ExtractFilePath(S); { retorna: 'C:\NomeDir\' }
DirectoryExists(CaminhoDir) - Retorna true se o diretório informado existe. False em caso contrário.

CreateDir(CaminhoDir) - Tenta criar o diretório informado.

Se conseguir, retorna true. Caso contrário retorna false.

Agora que sabemos como trabalham estas funções, vamos escrever uma função que precisamos para criar um sub-diretório conforme proposto.

function CriaSubDir(const NomeSubDir: string): boolean;
var
  Caminho: string;
begin
  Caminho := ExtractFilePath(ParamStr(0)) + NomeSubDir;
  if DirectoryExists(Caminho) then
  Result := true
  else
  Result := CreateDir(Caminho);
end;
Exemplo de uso:

- Chame a função no evento OnCreate do form:

procedure TForm1.FormCreate(Sender: TObject);
begin
  if not CriaSubDir('MeuSubDir') then
  ShowMessage('Não foi possível criar o sub-diretório MeuSubDir.');
end;

Criando um arquivo de log

procedure AddLog;
var
   log: textfile;
begin
   try
       AssignFile(log, 'c:\log.log');
       if not FileExists('c:\log.log') then Rewrite(log,'c:\log.log');
      Append(log);
      WriteLn(log, 'informações a serem inclusas');
  finally
      CloseFile(log);
      end;
end;

Criando diretório

Para criar um diretório você precisa usar a função ForceDirectories, o exemplo a baixo testa se não existe um diretório e cria o diretório apartir de uma variável string testando se o diretório já existe

Unit
FileCtrl

procedure TForm1.Button1Click(Sender: TObject);
var
  Dir: string;
begin
  Dir := 'C:\APPS\SALES\LOCAL';

if not DirectoryExists(Dir) then
  ForceDirectories(Dir);
  Label1.Caption := Dir + ' foi criado';
end;

quarta-feira, 11 de agosto de 2010

Alterando a fonte de determinado registro em um DBGrid

Para trocar a fonte de um DBGrid, utilize a rotina abaixo no evento OnDrawDataCell:

if Tabela.FieldByName ('Salario').Value >= 10000 then begin
    DbGrid1.Canvas.Font.Color := clRed;
    DbGrid1.Canvas.Font.Style := [fsBold];
end;

DbGrid1.DefaultDrawDataCell(Rect, Field, State);

No caso, somente os registros com salário maior que R$ 10.000,00 ficarão com cor vermelha e em negrito.

Nota: Não é necessário mover o ponteiro da tabela para colorir os registros.

Adicionar o evento OnClick do DBGrid

Problema:

Meu programa precisa processar algo quando o usuário clicar no DBGrid em um determinado form. O problema é que o DBGrid não possui o evento OnClick. É possível adicionar este evento no DBGrid?

Solução:

É possível sim. Afinal é muito simples. Siga os passos abaixo para resolver seu problema:

- Monte seu form normalmente, colocando o DBGrid e demais componentes;

- Vá na seção "private" da unit e declare a procedure abaixo:

private
  procedure DBGridClick(Sender: TObject);
- Logo após a palavra "implementation", escreva a procedure:

implementation
{$R *.DFM}
 
procedure TForm1.DBGridClick(Sender: TObject);
begin
  ShowMessage('Clicou no DBGrid.');
end;
- Coloque as instruções abaixo no evento OnCreate do Form:

procedure TForm1.FormCreate(Sender: TObject);
begin
  DBGrid1.ControlStyle :=
  DBGrid1.ControlStyle + [csClickEvents];
  TForm(DBGrid1).OnClick := DBGridClick;
end;
- E pronto. Execute e teste.

Observações:
O segredo principal desta dica está OnCreate do Form. A primeira instrução ativa o evento OnClick. A segunda instrução acessa o manipulador do evento OnClick. Para isto precisamos tratar o DBGrid como se fosse Form, pois o evento OnClick está declarado como protegido (protected) na classe TDBGrid.

Copiando Arquivos Via Programação

Function CopiaArquivo(scrname,destname:string):byte;
var
     source,destination:file;
      buffer:array[1..1024] of byte;
      readcnt,writecnt:word;
      pname,dname,fname,ename:String;
      { USO: R:=COPIAARQUIVO('C:\diretorio\FILE.EXT','C:\diretorio\FILE.EXT'); Devolve 0=Ok, 1=Erro no Origem, 2=Erro no Destino, 3=Disco Cheio }
begin
      AssignFile(source,scrname);
      Try
          Reset(source,1);
      Except
          CopiaArquivo:=1;
          Exit;end;If destname[length(destname)]='\' then
      begin
          pname:=scrname;
          destname:=destname+separa(scrname,'\',Ocorre(scrname,'\')+1);
      end;
      AssignFile(destination,destname);
      Try
          Rewrite(destination,1);
      Except
          CopiaArquivo:=2;
          Exit;
    end;
    Repeat
          BlockRead(source,buffer,sizeof(buffer),readcnt);
          Try
              BlockWrite(destination,buffer,readcnt,writecnt);
          Except
              CopiaArquivo:=3; {Disco Cheio?}
              Exit;
        end;
    until (readcnt=0) or (writecnt<>readcnt);
    CloseFile(destination);
    CloseFile(source);
    CopiaArquivo:=0;
end;