24 de nov. de 2008

Debugando projetos Intraweb, a forma fácil

Geralmente, projetos Intraweb vão ser usados em forma de DLL, e a maioria das pessoas começa seu projeto IW escolhendo ISAPI Extension no Application Wizard do IW. O problema com ISAPI é a dificuldade em debugar tais DLLs. Existem alguns truques que envolvem ISS, COM e outras parafernálias que funcionam, mas existe uma forma bem mais simples.

A "técnica" consiste em termos 2 projetos. O primeiro deles é a própria DLL, que vai ser a aplicação a ser distribuida. O segundo projeto é uma versão Stand Alone Server da DLL. O Stand Alone Server é um executável que já tras seu próprio webserver embutido, facilitado o processo de debug, já que não é necessário configurar nada.


Com os dois projetos, à medida que você cria novas units para a DLL, adicione-as ao projeto de teste (o Stand Alone Server). O contrário também é válido, e é o que eu costumo fazer. Crio as novas units no projeto de teste e vou adicionando ao projeto principal.

Para debugar, basta executar o projeto do Stand Alone Server a partir da IDE do Delphi/CBuilder e colocar seus breakpoints normalmente. O resto da história você já sabe.

Uma coisa importante é configurar os Outputs (Output Directory e Unit Output Directory) do projeto de teste para o mesmo diretório da DLL ISAPI, caso você tenha os 2 projetos em diretórios distintos (como é o meu caso). Dessa forma, alguma unit que você esqueça de adicionar ao projeto sempre será linkada com a última compilação, além de não termos DCUs duplicadas. Outra coisa é que você pode compartilhar arquivos entre os dois projeto (imagens, Templates, etc).

Crie também um Project Group e adicione seus dois projetos. Isso facilita na hora de compilar.

É isso aí, apenas uma pequena dica que pode ser útil pra muite gente.

Bom proveito.

19 de nov. de 2008

Diálogo OK - Cancel no Intraweb

Uma forma muito simples de adicionar uma caixa de diálogo OK - Cancel no Intraweb é usando a propriedade Confirmation do TIWButton.

Se você quer que o usuário confirme a execução de algum processo que vai ser disparado por um botão, basta escrever na propriedade Confirmation o texto que você quer que apareça na caixa de diálogo.

Quando o usuário clicar no botão, uma caixa de diálogo, como a do exemplo acima, é exibida. O evento OnClick do botão só vai ser executado se o usuário clicar no botão OK. O legal é que isso tudo é feito no browser, sem chamadas extras ao servidor.
Importante lembrar que isso só vai funcionar para o evento OnClick. Isso ainda não está implementado para o evento OnAsyncClick. Talvez num futuro release isso esteja disponível também para eventos assíncronos (é aqui aonde o Intraweb usa AJAX).
Bom proveito.

14 de nov. de 2008

Gerando scripts de inserção via SQL

Durante o desenvolvimento de uma aplicação, geralmente me deparo com a situação aonde preciso gerar uma série de inserts a partir de uma tabela ou tabelas do banco de dados.

Isso acontece por exemplo quando tenho que dar um drop numa tabela (por qq motivo) e eu não quero perder os dados que estão lá. Ou então quando tenho vários bancos de dados de testes e quero testar uma massa de dados que está em um banco em outro dos meus bancos de testes.

Eu não uso nenhuma ferramenta mágica para isso exceto o próprio SQL.

Vamos supor que temos uma tabela de nome Clientes com os campos Nome, Endereco, fone_01, fone_02 e fax. Essa tabela tem 500 registros e quero copiá-los para outro banco de dados. Podemos montar o SQL de inserção usando um select simples:

select 'insert into cliente (nome, endereco, fone_01, fone_02, fax) values (''' + nome + ''', ' + endereco + ''', ' + fone_01 + ''', ' + fone_02 + ''', ' + fax + '''); '
from cliente
where 
  fone_01 is not null

A partir daí, basta salvar o resultado (uma série de strings contendo seus inserts) em um arquivo texto e executar o script no outro banco.

Bom proveito

11 de nov. de 2008

Reduzindo o consumo da CPU em processos longos

Hoje me deparei com um problema um tanto comum para a maioria dos desenvolvedores. Uma de minhas aplicações rodando em um servidor de internet estava consumindo 100% da CPU do mesmo, quando em execução. Isso estava prejudicando o acesso das demais aplicações e degradando o sistema.

O objetivo da aplicação é ler uma base de dados em XML e atualizar o banco, portanto, envolve uma série de loops. 

Uma solução super simples resolveu meu problema. Bastou colocar um comando Sleep(10) no fim de cada loop (pode ser no início também) e acabou-se o problema de consumo de processador.

A função Sleep faz com que a thread da aplicação adormeça por 10 milisegundos (no meu caso) e devolve o controle da CPU ao Sistema Operacional, tempo mais que suficiente para que os outros processos sejam executados tranquilamente. 

Com o uso do Sleep, o consumo de CPU caiu de 100% para uma média de 7 a 8%, e em raros momentos chega a 40%.

28 de out. de 2008

Embutindo inteligência nos TFrames

Uma das coisas que sinto falta quando uso frames é a possibilidade de inicialização dos mesmos sem depender de códigos externos. Não sei porque cargas d'água Frames não tem eventos Oncreate e OnDestroy publicados.

Mas nada está perdido. Com alguns "truques" da POO é possível estender um frame de tal forma que o mesmo possa ser um pouco mais esperto.

Imaginemos uma situação hipotética aonde queremos um frame que seja capaz de exibir um dataset num DBGrid, abrir o dataset no momento da construção do form, e de fechá-lo no momento em que nosso form for destruído, sem nossa intervenção.



Já que um TFrame é herdado de TObject, com certeza o mesmo tem construtor e destrutor, e uma solução para resolver nosso problema e ter nosso frame mais independente é dar um override nesses métodos.

Criamos então nossos overrides para métodos já existentes e que serão chamados automaticamente e aproveitamos a oportunidade para fazer com que nosso frame se auto-inicialize e se auto-finalize, sem precisar intervir no frame quando formos usá-los em nossos forms.




Em nosso exemplo hipotético, nosso frame abre e fecha o dataset automaticamente, bastando apenas colá-lo em nosso form.


Com certeza essa técnica abre diversas possibilidades. 

Bom proveito...

24 de out. de 2008

Cansado do if-then-else?

Se você estiver cansado de digitar if-then-else toda hora, o Delphi tem uma função bacana de nome IfThen que permite fazer o que o nosso (aqui difamado) if-then-else faz em apenas 1 linha.

Veja na figura abaixo que, com o tradicional
if-then-else temos 4 linhas abertas no código. Já com a função IfThen, temos apenas uma linha aberta, tornando nosso código menor e de leitura mais rápida.



O primeiro parâmetro da função é uma
expressão booleana. Se ela for verdadeira, o segundo parâmetro será o retorno de IfThen. Caso ela seja falsa, o terceiro parâmetro passado será o retorno de nossa função.

O segundo e o terceiro parâmetros podem ser dos tipos:

  • Integer
  • Int64
  • Double
Isso é possível porque há 3 overloads para a função IfThen na unit Math. O tipo de retorno da IfThen será de acordo com o tipo dos parâmetros passados (Integer, Int64 ou Double).

Vale ressaltar que os parâmetros também podem ser funções, desde que as mesmas retornem os tipos esperados pelos parâmetros da IfThen.

Para usar a função
IfThen, inclua a unit Math na uses de sua unit.

Além da IfThen da unit Math, há uma implementação da IfThen na unit StrUtils. Nesse caso, o tipo de parâmentro esperado pela IfThen são strings e a função também retorna uma string.

Sei que pode parecer extranho o uso inicial da IfThen, mas com o tempo você vai gostar, assim como eu.

Bom proveito...

O que fazer com os arquivos que sua aplicação Intraweb gera?

Durante o desenvolvimento de uma aplicação Intraweb, tive a necessidade de gerar algumas planilhas Excel para que o usuário pudesse ter localmente uma série de informações apresentadas em um Grid.

Para gerar as planilhas em Excel, usei o componente TmxDataSetExport, um freeware bem simples e que atendeu às minhas necessidades de apenas salvar o conteúdo do Grid em um arquivo XLS.

Bastava gerar um nome de arquivo, fazer algumas configurações no nosso amigo mxDataSetExport, incluindo passar o nome do arquivo a ser gerado e meter bronca.




AddSlash é apenas um alias para
IncludeTrailingPathDelimiter, e digamos de passagem, é um porre ter que ficar
digitando IncludeTrailingPathDelimiter toda hora, né?

Tudo beleza!!! Arquivo Excel gerado no servidor e enviado pro usuário!!!

Beleza? Beleza nada, tua batata tá assando meu filho...

Você acha que o administrador do seu site/aplicação vai ter a bondade de todo dia ir lá na pasta Files e verificar se alguém esqueceu algum arquivo lá e apagar pra você? Então tá...

Duas semanas depois lá estão 2 mil arquivos Excel boiando sem dono no seu diretório Files...

A solução então é ter uma lista de todos os arquivos gerados durante a sessão do usuário e apagá-los quando a sessão terminar.

Mãos a obra:
  • No IWUserSession, adicione uma proprieade de nome fFilesToDelete: TStringList para guardar os arquivos que a sua aplicação gerar. Uma propriedade privada é o ideal.

  • Adicione também um método público de nome AddFileToDelete para que você possa acioná-lo cada vez que gerar um arquivo em sua aplicação.
  • Adicione também um método privado de nome DeleteFiles, para que possamos deletar todos os arquivos guardados em fFilesToDelete.

Nossso IWUserSession resumido ficaria da seguinte forma:




DoDebug é uma rotina que uso para gerar um log de tudo que acontece
na minha aplicação. Note que a exceção gerada não é exibida para o usuário, pois
nesse caso não há necessida. Como bom desenvolvedor, sempre de praxe eu dou uma
conferida no arquivo de log pra ver se há algum problema na aplicação.

No evento OnDestroy de nosso IWUserSession, basta executar o método DeleteFiles. Ao fim da sessão do usuário, nossos arquivos serão então, deletados.

Durante o decorrer de sua aplicação, vá usando UserSession.AddFileToDelete(NomeDoArquivo) a medida que sua aplicação gerá-los.

Beleza? Agora sim...