27 de jul. de 2010

Enviando e-mails com Indy através de uma conta do GMail

Além de toda a parafernália que a gente já sabe, para enviar emails pro Gmail, você precisa acrescentar as seguinte linhas:
SSL := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
with SSL do begin
  Destination := SMTPHost + ':' + IntToStr(SMTPPort);
  Host := SMTPHost;
  MaxLineAction := maException;
  Port := SMTPPort;
  DefaultPort := 0;
  SSLOptions.Method := sslvTLSv1;
  SSLOptions.Mode := sslmUnassigned;
  SSLOptions.VerifyMode := [];
  SSLOptions.VerifyDepth := 0;
end;
SMTP.IOHandler := SSL;
SMTP.UseTLS := utUseExplicitTLS;
SMTP.UserName := SMTPUser;
SMTP.Password := SMTPPassword;

Porquê? Porque para enviar emails via Gmail, só via conexão segura, SSL (sslvTLSv1) .Nesse caso, as DLLs do OpenSSL tem de estar disponiveis pra sua aplicação.

Pra baixar o OpenSSL que rode com o Indy, vá em http://indy.fulgan.com/SSL/ e baixe de acordo com sua versão do Indy. Eu uso a versão OpenSSL 0.9.8h juntamente com o Indy que vem no Delphi 2007 e funciona bem. A unica limitação que achei foi fazer funcionar numa DLL ISAPI sob o IIS, pelo fato de que o IIS já tem seu próprio SSL.

Bom proveito.

26 de jan. de 2010

Carregando uma imagem no banco de dados

Faz tempo que não tem postagem nova, então vamos lá. Uma dica que resolve uma pergunta recorrente em alguns fórums de Delphi.

Como fazer pra gravar uma imagem num campo Blob? Fiz uma função para isso, a seguir:
procedure LoadPictureToDataset(FileName: TFileName; Dataset: TDataset; PictureFieldName: String);
begin
if StrIsEmpty(FileName) then
raise Exception.Create('Nome de arquivo não pode ser vazio.');
Assert(Dataset <> Nil);
Assert(Dataset.FindField(PictureFieldName) <> Nil, 'Nome de campo inválido: ' + PictureFieldName);
Assert(not (Dataset.FieldByName(PictureFieldName) is TGraphicField), 'Campo ' + PictureFieldName + ' não suporta imagens');
TBlobField(Dataset.FieldByName(PictureFieldName)).LoadFromFile(FileName);
end;

30 de jun. de 2009

Select do Select do Select

Você sabia que pode fazer um select em um resultado de outro select dentro do mesmo SQL? Provavelmente muita gente não conhece esse recurso. Até a um bom tempo atrás eu também não conhecia.

Esse recurso permite fazer coisas interessantes sobre result sets de SQLs complexos. Você pode, por exemplo, agrupar dados, fazer joins com outras tabelas, usar funções aggregates, dentre outras coisas.

Tomemos como exemplo o sequinte SQL hipotético:
select id_cliente, nome,
  case
    when situacao = 'I' then 1
    when situacao = 'X' then 2
    when situacao = 'A' then 3
  end id_situacao
from cliente
Digamos que você queira contar quantos clientes existem para cada nova situação. Fazer isso no SQL acima o tornaria um pouco mais complexo. Usando o recurso do select sobre um result set, torna a tarefa bem mais fácil:
select count(t1.id_situacao), s.ds_situacao from
(
select id_cliente, nome,
  case
    when situacao = 'I' then 1
    when situacao = 'X' then 2
    when situacao = 'A' then 3
  end id_situacao
from cliente
) t1
inner join situacao s on s.id_situacao = t1.id_situacao
Essa sintaxe acima funciona tanto no MS-SQL Server quanto no Oracle e acredito que também funcione no Firebird.

Bom proveito...

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%.