Todos os posts e artigos sobre Delphi

Delphi 6 no Windows Vista

Publicado por Marcos Dell Antonio em 07/11/2007 | Delphi

O meu notebook novo, um HP dv6433, veio com o Windows Vista instalado. Muita gente desiste logo no começo deste sistema operacional e faz um downgrade. Não é o meu caso. Mesmo com os possíveis problemas que vou encontrar pela frente, prefiro encarar o desafio e aproveitar para conhecer esta nova versão do sistema operacional.

O primeiro deles não demorou para aparecer: as versões mais antigas do Delphi são incompatíveis com o Vista. No entanto, em poucos minutos encontrei a solução: antes de executar a IDE, acesse as propriedades do executável e na aba Compatibility há duas opções que devem ser definidas:

  • Compatibility mode: marque a opção Run this program in compatibility for e escolha no combo abaixo o item Windows XP (Service Pack 2);
  • Privilege level: marque a opção Run this program as an administrator.

Configurações para rodar o Delphi 6 no Windows Vista

É isto! Boa sorte. :)

3 comentários - clique aqui para fazer o seu

Refatoração no Visual Studio .NET 2003

Publicado por Marcos Dell Antonio em 09/07/2007 | .NET, Delphi

Quem já utilizou uma boa ferramenta de refactoring (como a do Eclipse) não consegue mais desenvolver código legível quando não tem à disposição este recurso. Digo isto por experiência própria, pois apesar de trabalhar com Delphi na maior parte do tempo, sempre utilizo o Visual Studio 2005 em projetos pessoais.

Para o Visual Studio 2003 o jeito é utilizar uma ferramenta de terceiros. No site Refactoring existe uma extensa lista com diversas sugestões de ferramentas para diversas linguagens e plataformas. Para .NET existem cinco. Eu acabei de instalar e testar a ReSharper, que parece ser a mais madura de todas. Aparentemente cumpre com o que promete, mas não possui o recurso de encapsulação de múltiplos campos (você deve gerar o get e o set para cada campo, um de cada vez). Se fosse no Eclipse bastaria utilizar a opção Generate getters and setters. Até mesmo o Delphi faz melhor neste sentido: Ctrl+Shift+C e pronto, todas as declarações private são publicadas através de métodos get e set.

Ah, se você procura alguma ferramenta desde tipo para Delphi, encontrei ao acaso a ModelMaker Code Explorer. Faça o teste! :)

T+

Seja o primeiro a comentar este post

Delphi: rodando um executável como um Serviço do Windows

Publicado por Marcos Dell Antonio em 28/06/2007 | Delphi

[Update 04/07/2007] Se você pretende criar uma aplicação para rodar como serviço e sabe que ela precisará de uma interface gráfica para entrada de dados, a melhor solução é criar o serviço em um projeto e a GUI em outro (dois executáveis).

Desta forma, você deverá estabelecer algum meio de comunicação entre a GUI e o serviço. Para isto, poderá usar sockets, objetos distribuídos (acho que não compensa), named pipes ou qualquer outro recurso de compartilhamento de memória entre processos.

Estas foram as conclusões que eu e outro colega de trabalho chegamos após diversos testes com um serviço que precisava de uma interface para administrá-lo. [Fim do update]

Os serviços do windows são uma boa solução quando você precisa executar alguma tarefa antes do login de qualquer usuário no sistema.

Na empresa onde trabalho atualmente, utilizamos esta tecnologia em um servidor que gerencia o acesso dos usuários ao sistema.

Neste post apresento como criar um serviço a partir de uma aplicação pronta. Além disso, também vou descrever alguns detalhes que só a experiência em trabalhar com esta tecnologia pode trazer à tona.

Os exemplos foram criados com base no Delphi 6 e utilizando o Windows XP.

- Criando o serviço

Antes de tudo, é claro, você deve ter uma aplicação normal para transformá-la em um serviço. Com ela aberta no Delphi, vá até o menu File / New / Other e escolha o item Service. Veja:

Delphi - Criando um novo serviço

Com o serviço criado (eu chamei de srvServidor) você deve fazer as seguintes alterações:

Projeto (dpr): o uses Forms deve ser removido e o SvcMgr adicionado. Além disso, o único objeto que deve ser criado na inicialização é o serviço. Veja:

Criação do serviço no dpr

Implementar o método Execute: o serviço possui um método chamado Execute. É nele que você deverá criar a tela principal da sua aplicação. Veja:

Método Execute do Serviço

Ignore por enquanto a inicialização do COM (método CoInitialize) . Ela será explicada mais adiante.

Instalar o serviço: a instalação de um serviço no windows é feita executando a aplicação com o parâmetro /INSTALL. A desinstalação é feita com o /UNINSTALL. Veja um exemplo:

Instalação de um serviço

- Adaptando o executável para rodar como um serviço

Existem diversas alterações que você deve fazer para rodar sua aplicação como um serviço. Abaixo segue uma lista das mais comuns.

Objeto TService

A primeira alteração deve ser feita no objeto TService criado anteriormente. Você precisa definir as seguintes propriedades:

  • DisplayName: nome que aparecerá na lista de serviços;
  • Interactive: define se o serviço poderá se comunicar com o Desktop ou não. Para mostrar um form ou até mesmo uma simples mensagem (ShowMessage, por exemplo), esta propriedade deve estar definida como true.

Inicialização do COM

Se a sua aplicação utiliza algum objeto COM, você deverá inicializar o suporte ao COM com o método CoInitialize. Isto pode ser visto no método Execute apresentado acima.

Sem esta alteração, provavelmente você receberá uma mensagem do tipo “CoInitialize has not been called” ao rodar o serviço.

Método GetCurrentDir

O método GetCurrentDir quando utilizado em um serviço retorna sempre c:\windows\System32. Eu não achei em lugar algum documentação que explicasse este comportamento.

Para contornar este problema, utilizei o ParamStr(0) juntamente com ExtractFilePath.

OnMouseLeave

Se você utiliza este evento em Labels (talvez em outros componentes também), terá que esquecê-lo. Em todos os testes que fiz o OnMouseLeave não foi executado. Logo, deixei de usá-lo.

FormStyle

O form principal de uma aplicação normalmente é do tipo fsMDIForm. Em um serviço você deverá alterar esta propriedade para fsNormal.

Nos testes que fiz, ao utilizar o tipo fsMDIForm não foi possível modificar a propriedade Visible de alguns frames.

É isso ae.

Até +.

13 comentários - clique aqui para fazer o seu

Implementando um cache de dados simples em Delphi

Publicado por Marcos Dell Antonio em 15/06/2007 | Delphi

Evitar ao máximo consultas com o banco de dados pode ser uma excelente forma de aumentar a performance da aplicação.

Grande parte dos softwares desenvolvidos hoje em dia adotam a arquitetura cliente/servidor. Isto significa que uma requisição do cliente implica em uma viagem de ida e volta até o servidor (round trip). Na pior das hipóteses, isto se traduz em um novo Open na conexão com o banco de dados, uma nova autenticação e um número infinito de controles e verificações relacionadas ao protocolo em questão, requisitos de segurança, etc, etc, etc.

Resumidamente, quando for possível evitar o acesso remoto para obter alguma informação, não deixe para depois.

O exemplo que segue abaixo implementa um cache de dados simples utilizando um objeto da classe TStringList do Delphi. Eu uso muito esta técnica em relatórios onde, por exemplo, preciso retornar uma série de informações sobre uma lista de registros mas não posso fazer isto diretamente na query enviada ao banco (seja por questões técnicas, pois utilizamos o MySQL 3.x, ou de legibilidade).

Tipo TObj utilizado no Cache
Tipo TObj utilizado na implementação do cache

Função que implementa um cache de valores em Delphi
Função que implementa o uso do cache

Esta função funciona da seguinte maneira:

  1. Recebe o código de um produto qualquer para retornar o seu valor cadastrado na base;
  2. Verifica se este valor já foi retornado anteriormente. Se foi, não há motivos para buscar do banco, então retorna da lista que está armazenada localmente;
  3. Se ainda não foi retornado, então busca do banco e armazena no cache.

Claro que esta abordagem é inviável para determinadas situações. Você não pode generalizar e usar este cache para todas os casos, pois imagine que você tenha localmente todos os valores dos produtos e alguém, de outra máquina conectada ao banco, faz uma alteração em um destes valores. Neste caso, o que você tem localmente já não reflete mais o que está armazenado no banco e, portanto, estará trabalhando com valores desatualizados. Se você for fazer algum lançamento de estoque onde envolva valores, por exemplo, estará realizando uma operação incorreta que prejudicará mais adiante em uma consulta de movimentação de produtos.

Em relatórios, sempre que o usuário pede para exibir as informações eu costumo limpar o cache (utilizando FCacheValores.Clear) para garantir que, caso entre a geração de um relatório e outro o valor do banco tenha mudado, eu esteja com os dados atualizados localmente. Veja um exemplo de uso desta função:

Exemplo de utilização do Cache
Função que implementa o uso do cache

É isso ae. Adapte esta idéia à sua situação.

Até +

Seja o primeiro a comentar este post

Função Explode do PHP em Delphi

Publicado por Marcos Dell Antonio em 14/06/2007 | Delphi

Mais uma dica pra quem vem de outras linguagens e não encontra no Delphi o que estava acostumado a usar.

A função Explode do PHP divide uma string em várias outras conforme um separador qualquer. Por exemplo, a string “teste1;teste2″ explodida utilizando o separador “;” retorna um array de strings onde na primeira posição está a palavra “teste1″ e na segunda “teste2″.

Em Delphi eu criei algo semelhante:

Função Explode em Delphi

Download do código fonte

Existem algumas diferenças entre a minha implementação e a oficial do PHP. A idéia não era fazer um clone, mas sim uma adaptação, de tal forma que o resultado final (uma lista de strings) pudesse ser obtido a partir de um conteúdo qualquer (strings separadas por algum delimitador).

Para utilizá-la basta fazer como no exemplo abaixo:

Exemplo de uso da função Explode em Delphi

Até +.

4 comentários - clique aqui para fazer o seu

Operador IF ternário em Delphi e C#

Publicado por Marcos Dell Antonio em 14/06/2007 | .NET, Delphi

Quem usa o Delphi 4, 5 ou 6 (talvez as versões mais novas também) deve sentir muita falta do if ternário. Este operador, além de dar mais produtividade durante o desenvolvimento da aplicação, torna o código final muito mais legível.

No livro 58+ Soluções em .NET o Thiago Fernandez fala sobre este operador no C#. Exemplificando o que ele definiu como Dado Travestido de Código, segue um breve trecho de uma solução implementada com o if normal e o equivalente com o if ternário:

Exemplo utilizando IF normal em C#
Exemplo em C# utilizando o if normal

Exemplo em C# utilizando o IF ternário
Exemplo em C# utilizando o if ternário

If ternário no Delphi

Infelizmente nas versões 4, 5 e 6 do Delphi (e talvez nas mais novas, como disse anteriormente) este operador não está disponível. Porém, existe uma maneira muito simples de simular ele.

Veja a função que utilizamos na empresa onde trabalho:

IF ternário em Delphi

Agora imagine a seguinte situação: você tem vários checkboxes em um formulário. Quando o usuário clica no botão salvar, o valor gravado no banco de dados deverá ser Sim para os checkboxes marcados e Nao para os não marcados. Com o if ternário isto pode ser facilmente resolvido assim:

Exemplo utilizando if ternário em Delphi

Sem a função iif você precisaria testar a propriedade Checked utilizando um if/then/else. Ou seja, teria algumas linhas a mais de código. Isto a princípio não prejudica em nada o código, mas imagine se você tiver 30 checkboxes. Vai fazer um if para cada um?

É isso ae.

Até +.

9 comentários - clique aqui para fazer o seu

Utilizando XPath no Delphi

Publicado por Marcos Dell Antonio em 29/05/2007 | Delphi

Se você ainda utiliza o bom e velho Delphi 6 deve saber o quanto é complicado encontrar e utilizar certas APIs para trabalhar com tecnologias relativamente novas.

Não que XPath ou XML sejam algo novo, mas que não é comum um desenvolvedor Delphi 4, 5 ou 6 utilizar estas tecnologias, isto é fato!

Nos últimos dias estou trabalhando em um novo projeto que armazenará todas as configurações utilizando um arquivo XML. Se fosse em .NET poderia escolher até mesmo o db4o pra isso, mas em Delphi as únicas possibilidades são o bom e velho arquivo INI ou, com a maior paciência do mundo, arquivos XML.

Como bom metido que sou, resolvi utilizar os XMLs. Já tinha uma noção básica de como utilizá-los, mas desta vez eu precisei de um recurso extra: o XPath.

Mas que diabos é esse XPath?

É uma linguagem de acesso às informações contidas em um arquivo XML. Tome como base o seguinte arquivo:

Exemplo XML - XPath

Para acessar o valor do nodo login que está dentro do bancodados, basta utilizar a seguinte expressão XPath:

/configs/bancodados/login

Entendeu? É quase igual ao acesso de um arquivo dentro de pastas e subpastas.

XPath no Delphi

A unit que dá suporte às expressões XPath no Delphi é a XMLDom. Nela você encontrará a classe IDOMNodeSelect, que provê o método selectNode.

Para ler o valor de um nodo utilizando este método use o código apresentado abaixo:

Método para leitura de valores usando XPath

FXMLDoc é um objeto do tipo IXMLDocument que está declarado como private e foi instanciado previamente. FNodoRaiz é o nodo root do arquivo XML. No exemplo acima, este nodo é o configs.

Veja que o resultado do selectNode não possui diretamente o valor do nodo. Ao invés disso, usa-se a propriedade childNodes.item[0].nodeValue. Levei algumas horas até descobrir isso.

Para a alteração do valor de um nodo o método é semelhante ao apresentado acima. Veja:

Exemplo de alteração de valor utilizando XPath

A única diferença é que se encontrou o nodo então altera o valor ao invés de retorná-lo.

Existe um detalhe muito importante que os métodos acima não tratam. Caso um dos nodos referenciados na expressão XPath não exista, então o selectNode retornará nil. No caso do método RetornaValor isto não é um problema tão sério, pois simplesmente retornará vazio. No entanto no AlteraValor o comportamento mais óbio possível seria a auto-criação dos nodos não existentes. Para tal, você pode utilizar a rotina abaixo:

Método que adiciona os nodos de uma expressão XPath

Este método cria todos os nodos não encontrados numa expressão XPath. Pode ser chamado dentro da AlteraValor da seguinte forma:

Método AlteraValor utilizando o AdicionaNodos

Todas estas rotinas estão disponíveis dentro de uma classe base de acesso a arquivos XML. O download dela pode ser feito aqui.

É isso ae. Até +.

2 comentários - clique aqui para fazer o seu

Delphi: como interceptar o evento OnExit de um TMaskEdit?

Publicado por Marcos Dell Antonio em 05/05/2007 | Delphi

Com certeza o componente TMaskEdit foi modelado às pressas pela equipe responsável, pois forçar uma validação com uma mensagem fixa sempre que o usuário sai do campo é um crime. E tem mais: eu diria que a pena é de prisão perpétua, pois além disso, quando você limpa o conteúdo do campo e sai dele, a validação também é executada. Neste caso, adivinha o que acontece? Uma mensagem de erro aparece mesmo sem ter valor algum no campo.

Há quem diga que é impossível desabilitar esta validação. Realmente é, se pensarmos de forma racional. No entanto, não custa nada partir pra baixaria uma vez ou outra.

Todos os componentes trabalham com mensagens enviadas pelo Windows para ativar os eventos. Portanto, o que acontece com o TMaskEdit?

Ele recebe uma mensagem informando que o usuário saiu do campo, executa o OnExit e, neste momento, faz a validação do conteúdo.

Já que o problema descrito acima acontece mesmo quando o usuário limpa o valor do campo, a solução é interceptar o evento OnExit e só executar o código padrão caso algum valor tenha sido informado.

Como fazer? Simples. Veja:

Declare uma procedure type of object (em outra palavras, um método) e um field do tipo TWndMethod:

FOldWindowProc: TWndMethod; procedure WMWindowProc(var Msg: TMessage);

A implementação do método é a seguinte:

procedure TfrmForm.WMWindowProc(var Msg: TMessage); begin if (Msg.Msg = CM_EXIT) then begin if (maskEdit.Text <> '') then FOldWindowProc(Msg); end else FOldWindowProc(Msg); end;

No evento FormShow do formulário faça as seguintes atribuições:

FOldWindowProc := maskEdit.WindowProc; maskEdit.WindowProc := WMWindowProc;

Pronto! Agora sempre que o evento OnExit do TMaskEdit for executado ele só fará a validação se o Text for diferente de vazio.

T+

Ps1: falei especificamente do TMaskEdit, porém este conceito de captura de eventos vale para todos os componentes.

Ps2: há alguns meses eu escrevi sobre algo parecido com o componente TScrollBox. Veja aqui.

3 comentários - clique aqui para fazer o seu

Delphi: duplicar um registro de um dataset

Publicado por Marcos Dell Antonio em 26/04/2007 | Delphi

Aqui na empresa temos algumas situações onde precisamos disponibilizar para os usuários opções para duplicar determinados registros.

Como usamos DataSets para armazenar os dados enquanto o usuário está manipulando eles, temos duas opções para realizar esta operação:

1 - Copiar manualmente

Esta é a forma mais simples e lógica: você copia os registros do DataSet para umas variáveis, faz um Append ou Insert nele e define os novos valores conforme os copiados. Claro que se o seu DataSet tiver uma quantidade considerável de campos isso é inaplicável, portanto use a segunda opção.

2 - Utilizar um método genérico

Um pouco mais complexo que a forma acima, neste caso a idéia é usar um método genérico que copia as informações do registro atual para um novo. Este método é implementado da seguinte forma:

class procedure TFuncoesClass.DuplicaRegistroDataSet( dataSet: TCustomClientDataSet; recNo: integer; camposIgnorar: array of string); function IgnorarCampo(campo: string): boolean; var i: integer; begin Result := false; for i := 0 to Length(camposIgnorar) - 1 do begin if (camposIgnorar[i] = campo) then begin Result := true; Break; end; end; end; var valores: Variant; i: integer; readOnly: boolean; begin // Duplica o registro "recNo" de um dataset dataSet.RecNo := recNo; valores := VarArrayCreate([0, dataSet.FieldCount - 1], VarVariant); for i := 0 to (dataSet.FieldCount - 1) do valores[i] := dataSet.Fields[i].Value; dataSet.Append; for i := 0 to (dataSet.FieldCount - 1) do begin if (not(IgnorarCampo(dataSet.Fields[i].FieldName))) then begin readOnly := dataSet.Fields[i].ReadOnly; dataSet.Fields[i].ReadOnly := false; dataSet.Fields[i].Value := valores[i]; dataSet.Fields[i].ReadOnly := readOnly; end; end; end;

Logicamente que você pode definir este método como um procedimento. Basta remover a declaração class procedure TFuncoesClass e adicionar a palavra procedure.

Este método recebe três parâmetros, conforme descrito abaixo:

- dataSet: é o DataSet que você deseja duplicar o registro;
- recNo: identifica qual é o registro a ser duplicado. Você pode remover este parâmetro da implementação retirando ele da declaração do método e apagando a linha dataSet.RecNo := recNo. Caso fizer isso, o registro duplicado sempre será o atual. Já se você deseja manter o parâmetro e quer duplicar o registro atual, basta passar para ele o valor dataSet.RecNo;
- camposIgnorar: se você quiser ignorar alguns campos ao duplicar o registro, basta passá-los da seguinte forma: ['Campo1', 'Campo2', 'CampoN']. Se não quiser usar este parâmetro, use o valor [].

Repare também que o método faz um tratamento caso o campo seja ReadOnly. Quando isso acontecer, ele armazenará o estado atual, definirá como false, fará a cópia e voltará ao estado antigo. Isto foi feito para evitar uma Exception ao copiar campos ReadOnly.

Exemplo de uso deste método:

procedure TfrmCliente.acDuplicarExecute(Sender: TObject); begin inherited; // Duplica as informações do registro atual TFuncoesClass.DuplicaRegistroDataSet(cdsClientes, cdsClientes.RecNo, ['CODIGO']); end;

É isso ae. Até+.

Ps: este código é uma adaptação do original disponível na Active Delphi.

2 comentários - clique aqui para fazer o seu

Fazer acontecer

Publicado por Marcos Dell Antonio em 29/03/2007 | .NET, Delphi, Mobilidade

Faz tempo que estou dedicando várias horas do dia ao meu Trabalho de Conclusão de Curso. Degrau após degrau, problemas sobre problemas e aqui estou: firme e forte.

Como se torna gratificante quando você se aventura em um mundo desconhecido e vai vencendo todas as batalhas, desafios e obstáculos, um após o outro até chegar ao destino.

Muito melhor do que isso é quando você acha que tudo está perdido mas não desiste. Vai em frente. Tira código dali, adiciona aqui. Reescreve algumas rotinas. Remove algumas limitações. O resultado é mais do que o esperado.

Hoje, antes tarde do que nunca, apareceu um cubo 3D no meu emulador. :-)

Além de 3D ele é colorido e iluminado. Em outras palavras, matei todos os coelhos com uma paulada só, afinal o que é uma noite de sono a menos na vida do cara?

Fica aí outra imagem do que está por vir:

Ah, acabei de fazer um check in de todas as últimas alterações. Dentre elas:

  • A rotina que carrega o arquivo OBJ foi reescrita e agora suporta normais, texturas e vários formatos de faces (1/1/1, 1, 1//1, etc);
  • Corrigido bug na rotina que carrega o arquivo MTL. Agora está carregando corretamente os valores para Ka, Kd e Ks;
  • Adicionado um projeto chamado ThreeDV que nada mais é do que uma implementação usando a Gita.

É isso ae! Até +. :-)

1 comentário - clique aqui para fazer o seu

Anúncios

Anúncio provido pelo BuscaPé