11 de dez. de 2007

MSDN Magazine Dezembro/2007

A MSDN Magazine de Dezembro já está disponível no site da Microsoft. Destaque para uso do WCF, WPF e LINQ com o Office e, na coluna Cutting Edge, uma visão geral do Microsoft AJAX.

15 de nov. de 2007

Problemas com Indy 10

Atualizei hoje meu artigo Como enviar email usando Indy 10. Indy 10 é uma versão que está em desenvolvimento. Entre o dia que escrevi o artigo e hoje, a implementação dos mecanismos de autenticação do servidor SMTP sofreu algumas alterações que simplemente o código exemplo original deixou de funcionar. Pessoalmente, acho problemático adotar o Indy 10 em código de produção, já que é frequente ocorrer esse tipo de problema. A versão que acompanha tanto o BDS2006 como o BDS2007 tem incontáveis bugs (isso não significa que a versão mais recente não tenha bugs). O recomendável é usar a versão snapshot disponível no site indy.fulgan.com (arquivo Indy10.zip). Para atualizar o Indy, primeiro desinstale o Indy que veio com o Delphi, usando para isso o programa de instalação do Delphi. Para instalar a nova versão, siga as instruções disponíveis na página Indy 10 Installation Instructions do site oficial do Indy.

13 de out. de 2007

MSDN Magazine Novembro/2007

A MSDN Magazine de Novembro já está disponível no site da Microsoft. Destaque para segurança.

Outro destaque é que agora você pode baixar a versão CHM da revista em português.

MSDN Magazine Outubro/2007

A MSDN Magazine de Outubro está disponível no site da Microsoft. Destaque para otimização de código gerenciado em máquinas com vários núcleos.

3 de out. de 2007

Horário de Verão 2007/2008

O Horário Brasileiro de Verão começa à zero hora do dia 14 de outubro, quando os relógios deverão ser adiantados em uma hora, e devem ficar assim, até às 24 horas do dia 16 de fevereiro de 2008, quando os relógios deverão ser atrasados em uma hora.

O horário de verão abrange o Distrito Federal os estados do Rio Grande do Sul, Santa Catarina, Paraná, São Paulo, Rio de Janeiro, Espírito Santo, Minas Gerais, Goiás, Mato Grosso e Mato Grosso do Sul.

Atualizado em 13/10/2007

Para ajustar o horário de verão do Windows, no site da Microsoft tem um aviso informando que os usuários devem solicitar o envio de um hotfix (KB 943000).

Para quem tem pressa, o melhor é dar uma olhada no KB 317211, Como configurar as datas do horário de verão para o Brasil, que explica vários métodos para fazer o ajuste necessário. Note que esse KB refere-se ao horário de verão 2006/2007, portanto as datas que devem ser utilizadas não são as citadas no KB.

15 de ago. de 2007

MSDN Magazine Setembro/2007

A MSDN Magazine de Setembro  já está disponível no site da Microsoft. Destaque para sockets no framework 3.5 (incluindo P2P) e testes unitários.

12 de ago. de 2007

MSDN Magazine Agosto/2007

A MSDN Magazine de Agosto  já está disponível no site da Microsoft. Destaque para o novo padrão de empacotamento de dados OPC (Open Packaging Conventions) e um comparativo entre VSTO e VBA na automação do Office.

2 de jul. de 2007

MSDN Magazine Julho/2007

A MSDN Magazine de Julho  já está disponível no site da Microsoft. Destaque para compartilhamento de código entre aplicativos desktop e móveis e suporte ao reconhecimento de fala do Windows Vista.

10 de mai. de 2007

MSDN Magazine Junho/2007

A MSDN Magazine de Junho  já está disponível no site da Microsoft. Destaque para as novidades nas ferramentas C#, VB e C++ e o novo SilverLight, uma ferramenta para desenvolvimento web/multiplataforma.

13 de abr. de 2007

MSDN Magazine Maio/2007

A MSDN Magazine de Maio  já está disponível no site da Microsoft. Destaque para JavaScript e o novo XNA Game Studio Express, a ferramenta de desenvolvimento gratuita para o XBox.

6 de abr. de 2007

Como enviar email usando Indy 10?

Este exemplo mostra como enviar emails usando Indy10.

 1 var
 2   Email: TIdMessage;
 3 begin
 4   // SMTP é um TIdSMTP
 5   // POP3 é um TIdPOP3
 6   // UserPassProvider é um TIdUserPassProvider
 7 
 8   Email := TIdMessage.Create(nil);
 9   try
10 
11     // ...ajusta remetente, destinatário, etc da mensagem...
12     Email.Subject := '...assunto...';
13     Email.From.Address := 'remetente@mailinator.com';
14     Email.Recipients.EMailAddresses := 'destinatario@mailinator.com';
15     Email.Body.Text := '...texto da mensagem...';
16 
17     // conecta com o servidor POP3
18     // alguns provedores exigem isso
19     POP3.Host := 'seu-servidor-pop3';
20     POP3.Username := 'seu-username';
21     POP3.Password := 'sua-senha-secreta';
22     POP3.Connect;
23 
24     try
25       SMTP.Host := 'seu-servidor-smtp';
26 
27       // alguns servidores SMTP exigem login
28       SMTP.AuthType := satSASL;
29       UserPassProvider.Username := 'seu-username';
30       UserPassProvider.Password := 'sua-senha-secreta';
31 
32       SMTP.Connect;
33       try
34         SMTP.Send(Email);
35       except
36         on E: Exception do begin
37           // o envio falhou, trata o erro
38         end;
39       end;
40       SMTP.Disconnect;
41     finally
42       POP3.Disconnect;
43     end;
44   finally
45     Email.Free;
46   end;
47 end;
Pontos importantes: 
  • Nem todo servidor SMTP exige que se conecte antes ao servidor POP3: nesse caso, tudo que é relacionado com o servidor POP3 pode ser omitido (linhas 19 a 22 e linha 42). Note que quando isso é necessário, deve-se permanecer conectado ao servidor POP3 enquanto estiver usando o servidor SMTP.
  • Nem todo servidor SMTP requer autenticação: nesse caso, use SMTP.AuthType = atNone (linha 28) e não tem necessidade de username/senha (linhas 29 e 30).
  • Para fazer autenticação no servidor SMTP, vá na aba Indy SASL e coloque no form um componente TIdUserPassProvider e pelo menos um dos componentes de mecanismo de autenticação (os componentes com nome iniciado com TIdSASL); geralmente o componente TIdSASLLogin é suficiente.
  • Atribua o componente TIdUserPassProvider à propriedade UserPassProvider de cada componente de mecanismo de autenticação que você pos no form.
  • Selecione o componente TIdSMTP que está no form e, no Object Inspector, ative o editor da propriedade SASLMechanisms e inclua na lista direita do editor os mecanismos listados na lista esquerda; os mecanismos mostrados correspondem aos componentes TIdSASL que você pôs no form.
  • Procure permanecer conectado nos servidores POP3 e SMTP o mínimo de tempo possível. A maioria desses servidores irá desconectar você por inatividade ou por tempo.
  • Este exemplo não funciona com o GMail e outros servidores que usam SSL.

Atualizado em 15/11/2007 Indy 10 é uma versão que está em desenvolvimento. Este código funciona usando a versão snapshot (Indy10.zip disponível no site indy.fulgan.com) de 15/11/2007; a versão snapshot é atualizada todos os dias, de modo que não posso garantir que vá funcionar sempre. De qualquer forma, o exemplo foi atualizado e está funcionando novamente.

19 de mar. de 2007

Como fazer um broadcast via UDP usando Indy 9?

UDP é um protocolo de transporte de dados que se caracteriza por não garantir a entrega dos dados, o que permite entregar rapidamente os dados. Um uso frequente para UDP é transmissão de áudio ou vídeo, que é um caso onde a perda de um pacote de dados não compromete o resultado final.

Uma outra característica do protocolo UDP é a possibilidade de transmitir um mesmo pacote de dados para todas as máquina da rede em uma única operação, o que é conhecido como broadcast. Nesse caso, os dados são enviados por UDP para um endereço especial da rede que replicará o mesmo pacote de dados para todos os endereços da mesma subrede.

Um uso possível seria localizar numa rede local todas as máquinas que estão executando seu programa. Para isso, uma cópia do programa poderia iniciar um broadcast na rede pedindo que todas as outras cópias (em uso) do programa se identifiquem, por exemplo, informando o nome da máquina e o IP.

Usando os componentes TIdUDPClient e TIdUDPServer, o código seria algo assim:

1 type 2 TSeuForm = class(...) 3 // coloque um TIdUDPClient e TIdUDPServer no form 4 // por exemplo... 5 UDPClient: TIdUDPClient; 6 UDPServer: TIdUDPServer; 7 ... 8 procedure FormCreate(Sender: TObject); 9 procedure FormDestroy(Sender: TObject); 10 procedure UDPServerUDPRead(Sender: TObject; AData: TStream; 11 ABinding: TIdSocketHandle); 12 ... 13 private 14 procedure Ping; 15 ... 16 17 const 18 NOSSA_PORTA = 49152; // pode ser qualquer valor entre 49152 e 65535 19 20 // rotina de PING 21 // em algum lugar do programa isso é executado 22 procedure TSeuForm.Ping; 23 var 24 S: string; 25 IP: string; 26 X: Integer; 27 begin 28 // não queremos responder nosso próprio comando 29 UDPServer.Active := False; 30 31 // timeout da resposta, em milissegundos 32 UDPClient.ReceiveTimeout := 5000; 33 34 // manda o comando para todas as máquinas da rede 35 UDPClient.Broadcast('PING', NOSSA_PORTA); 36 37 // espera pela resposta 38 while True do begin 39 40 S := UDPClient.ReceiveString(IP, X); 41 if S = '' then begin 42 // ocorreu timeout 43 Break; 44 end; 45 // faz alguma coisa com os dados... 46 // - S tem o nome da máquina que respondeu 47 // - IP tem o IP da máquina que respondeu 48 end; 49 50 UDPServer.Active := True; 51 end; 52 53 // evento OnUDPRead do UDPServer 54 procedure TSeuForm.UDPServerUDPRead(Sender: TObject; AData: TStream; 55 ABinding: TIdSocketHandle); 56 var 57 Server: TIdUDPServer; 58 S: String; 59 begin 60 Server := Sender as TIdUDPServer; 61 62 // recupera o que recebeu 63 SetLength(S, AData.Size); 64 AData.Read(S[1], AData.Size); 65 66 // é o nosso comando de PING? 67 if S = 'PING' then begin 68 69 // envia a resposta 70 S := Server.LocalName; 71 ABinding.SendTo(ABinding.PeerIP, ABinding.PeerPort, S[1], Length(S)); 72 end; 73 end; 74 75 // evento OnCreate do form 76 procedure TSeuForm.FormCreate(Sender: TObject); 77 begin 78 UDPServer.DefaultPort := NOSSA_PORTA; 79 UDPServer.Active := True; 80 ... 81 end; 82 83 // evento OnDestroy do form 84 procedure TSeuForm.FormDestroy(Sender: TObject); 85 begin 86 UDPServer.Active := False; 87 ... 88 end;

Para evitar conflitos com outros programas e serviços do Windows, procure escolher uma porta (linha 18) entre 49152 e 65535. Independente da porta escolhida, lembre-se que essa porta precisa estar liberada para UDP no seu firewall.

O valor de timeout usado na rotina de ping (linha 32) precisa ser ajustado de acordo com o tamanho da rede. Um valor muito pequeno pode fazer com que a rotina de ping desista antes que todas as máquinas respondam e um valor muito alto pode congelar momentaneamente o programa. Não tem outra forma de aguardar pela resposta das máquinas já que, a princípio, a rotina não sabe quantas máquinas vão responder.

Broadcast deve ser usado com moderação. O broadcast utiliza um endereço especial da rede para transmitir os dados que faz com que o mesmo pacote de dados seja enviado a todos os endereços possíveis da rede. Como isso consome recursos da rede, não é boa idéia utilizar isso sem que seja absolutamente necessário. É tentador colocar a rotina de ping num timer que executa a cada segundo, mas o administrador da sua rede não vai ficar muito feliz com essa idéia.

Eu não entendo muito de redes, mas é bom saber que alguns roteadores podem impedir o broadcast.

Links relacionados

16 de mar. de 2007

MSDN Magazine Abril/2007

A MSDN Magazine de Abril  já está disponível no site da Microsoft. Destaque para efeitos especiais com o novo gerenciador de janelas Aero Glass do Windows Vista e um pequeno analisador/parser XML em C++.

28 de fev. de 2007

Como enviar email em C# usando o GMail?

Complementando meu post anterior sobre como enviar email usando C#, eis um exemplo interessante que encontrei uns tempos atrás (adaptado para facilitar o entendimento) mostrando como enviar email pelo GMail...

1 using System; 2 using System.Net; 3 using System.Net.Mail; 4 5 namespace GMailSample { 6 7 class SimpleSmtpSend { 8 9 static void Main(string[] args) { 10 11 SmtpClient cliente = new SmtpClient("smtp.gmail.com", 587 /* TLS */); 12 cliente.EnableSsl = true; 13 14 MailAddress remetente = new MailAddress( 15 "sua.conta@gmail.com", "Seu Nome"); 16 MailAddress destinatario = new MailAddress( 17 "fulano@mailinator.com", "Fulano"); 18 19 MailMessage mensagem = new MailMessage(remetente, destinatario); 20 21 mensagem.Body = "Exemplo de mensagem via GMail"; 22 mensagem.Subject = "Teste do Gmail com SSL e Credenciais"; 23 24 NetworkCredential credenciais = new NetworkCredential( 25 "sua.conta@gmail.com", /* login */ 26 "sua.senha.secreta", /* senha */ 27 ""); 28 29 cliente.Credentials = credenciais; 30 31 Console.WriteLine("Enviando..."); 32 33 try { 34 cliente.Send(mensagem); 35 Console.WriteLine("OK"); 36 } 37 catch (Exception e) { 38 Console.WriteLine("Exceção:" + e.ToString()); 39 } 40 } 41 } 42 }
Toda a comunicação com os servidores do GMail (tanto SMTP como POP3) é tunelada através de uma conexão criptografada.
Esse exemplo usa a porta 587 do GMail. Quem usa o Outlook Express para acessar o GMail pode verificar que o OE usa a porta 465 enquanto quem tem o Thunderbird usa a porta 587 (como o exemplo).
No caso do GMail, a porta 465 é usada para comunicação via SSL enquanto a porta 587 é via TLS. TLS é uma versão mais moderna do SSL; uma explicação mais detalhada sobre SSL/TLS você encontra na Wikipedia (em inglês) ou no Google.
FONTE: Mariya Atanasova's Blog



21 de fev. de 2007

Como enviar email?

O exemplo seguinte usa .NET 2.0 e pode ser usado em projetos WinForms ou ASP.NET. Ele mostra o envio de email usando um cliente SMTP.

1 using System;
2 using System.Net;
3 using System.Net.Mail;
4 
5 ...
6 // monta a mensagem
7 MailAddress to = new MailAddress("email-do-destinatario");
8 MailAddress from = new MailAddress("email-do-remetente");
9 MailMessage mensagem = new MailMessage(from, to);
10 mensagem.Subject = "Teste";
11 mensagem.Body = "Mensagem de teste";
12 
13 SmtpClient cliente = new SmtpClient("seu-servidor-SMTP");
14 
15 // dados para autenticação
16 cliente.Credentials =
17 new NetworkCredential("seu-username", "sua-senha-secreta");
18 
19 // envia a mensagem
20 try {
21 cliente.Send(mensagem);
22 }
23 catch (Exception ex) {
24 // trata a exceção
25 }
26 ...

Sobre o código:

  • As linhas 7 a 11 montam a mensagem.
  • A linha 13 cria o cliente SMTP
  • A linha 16 define as credenciais (nome de usuário e senha) para a autenticação no servidor SMTP
  • A linha 21 faz o envio da mensagem
  • Na linha 24 deve ir o código para tratamento de eventuais erros no envio.

Pontos importantes:

  • Nem todo servidor SMTP requer autenticação: nesse caso, as linhas 16 e 17 devem ser omitidas.
  • Alguns servidores SMTP exigem que se conecte antes ao servidor POP3. Nesse caso, é preciso usar um cliente POP3, o que, infelizmente, o framework não disponibiliza; será preciso usar uma biblioteca de terceiros (assunto para um futuro tópico).
  • Este exemplo não funciona com o GMail e outros servidores que usam SSL.

Tópico atualizado em 09/05/2007: pequena correção na criação da mensagem.

18 de fev. de 2007

Como determinar onde gerou uma exceção?

Uma das coisas mais desagradáveis é testar um programa, instalar ele no cliente e ele cair com uma exceção no meio da demonstração. Na máquina de desenvolvimento, a IDE gentilmente mostra onde a exceção foi gerada. Mas no cliente, não.

Na JCL (Jedi Code Library) existe uma unit chamada JclDebug que deve ser usada em conjunto com o mecanismo do JCL para tratamento de exceções não tratadas. Esse mecanismo consiste basicamente em determinar onde a exceção foi gerada (em qual linha, função/procedure e unit) bem como gerar um dump da pilha do programa, o que permite ter uma idéia da sequência de processamento que resultou na exceção.

A documentação da JCL não é muito clara sobre como usar esse mecanismo. O problema maior é saber como preparar o executável para poder usar o mecanismo. Esta é a "receita do bolo":

  1. Na instalação do JCL, marque a opção IDE experts|Debug extension e marque a opção Sample exception dialogs.
  2. De volta a IDE, abra seu projeto
  3. File|New|Other...|Dialogs
  4. Dê um clique duplo no ícone do Exception Dialog
  5. Salve seu projeto dando um File|Save all
  6. Marque a opção Project|Insert JCL Debug Data
  7. Dê um Project|Build (um dialog box adicional do JCL irá aparecer se o build for bem sucedido)
Se você optar por não utilizar a janela de exceção do JCL, não precisa fazer os passos 3-4-5. Entretanto, vale a pena testar pelo menos uma vez essa caixa de diálogo e dar uma olhada como o relatório de erro é criado.
  • Para isto funcionar, nunca compacte o executável (com compactadores do tipo UPX). Os compactadores de executável costumam alterar os endereços de carga dos módulos, de modo que o endereço de uma função/procedure quando o executável é gerado fica diferente do endereço quando o executável é executado. Resultado: as informações capturadas na exceção estão todas erradas!
  • Exceções que ocorrem muito cedo (na inicialização das units) ou muito tarde (na finalização das units) costumam não ser capturadas por qualquer mecanismo de captura de exceções que você imaginar; para evitar isso, vá no fonte do seu DPR e garanta que a unit que inicializa seu mecanismo de captura de exceções seja a primeita unit da lista (difícil de fazer, especialmente se você usa a unit ShareMem).
  • Se você desabilitar a opção Project/Insert JCL Debug Data, terá de gerar um mapa completo de linkagem do executável e distribuir esse arquivo (.MAP) junto com seu programa. Mas é muito mais cômodo deixar a opção habilitada.
  • Se quiser um dump da pilha extremamento detalhado, vá em Project| Options, aba Compiler e marque a opção Use debug DCUs

Links relacionados

14 de fev. de 2007

MSDN Magazine Março/2007

A MSDN Magazine de Março  já está disponível no site da Microsoft. Destaque para o IIS 7.0 e o MS Build.

13 de fev. de 2007

Como fazer busca incremental num combobox?

Este exemplo mostra como fazer uma busca incremental num combobox.

1 procedure TSeuForm.SeuComboBoxKeyPress(Sender: TObject; var AKey: Char); 2 var 3 ComboBox: TComboBox; 4 TextoDigitado: string; 5 TextoSelecionado: string; 6 TextoDoItem: string; 7 Texto: string; 8 Achou: Boolean; 9 Inicio: Integer; 10 I: Integer; 11 begin 12 ComboBox := Sender as TComboBox; 13 14 // recupera o que foi digitado 15 16 Inicio := ComboBox.SelStart; 17 18 // trata a tecla 19 TextoDigitado := Copy(ComboBox.Text, 1, ComboBox.SelStart); 20 if AKey = Chr(VK_BACK) then begin 21 22 if ComboBox.SelLength = 0 then begin 23 24 Delete(TextoDigitado, Length(TextoDigitado), 1); 25 end; 26 end 27 else begin 28 TextoDigitado := TextoDigitado + AKey; 29 end; 30 31 TextoSelecionado := Copy(ComboBox.Text, 32 ComboBox.SelLength + ComboBox.SelStart + 1, MaxInt); 33 34 Texto := TextoDigitado + TextoSelecionado; 35 if Texto <> '' then begin 36 37 // atualiza a posição de início da seleção 38 if AKey = Chr(VK_BACK) then begin 39 40 if Inicio > 0 then begin 41 42 Dec(Inicio); 43 end; 44 end 45 else if AKey <> Chr(VK_BACK) then begin 46 47 Inc(Inicio); 48 end; 49 50 // descarta a tecla 51 AKey := #0; 52 53 if Inicio = 0 then begin 54 55 ComboBox.Text := ''; 56 ComboBox.ItemIndex := -1; 57 end 58 else begin 59 60 // busca o texto 61 62 Achou := False; 63 for I := 0 to Pred(ComboBox.Items.Count) do begin 64 65 TextoDoItem := Copy(ComboBox.Items[I], 1, Length(Texto)); 66 Achou := AnsiCompareText(TextoDoItem, Texto) = 0; 67 if Achou then begin 68 69 ComboBox.Text := ComboBox.Items[I]; 70 ComboBox.ItemIndex := I; 71 72 Break; 73 end; 74 end; 75 76 if Achou then begin 77 78 // destaca a parte não digitada da string 79 ComboBox.SelStart := Inicio; 80 ComboBox.SelLength := Length(ComboBox.Text) - Inicio; 81 end 82 else begin 83 84 // beepa para avisar que não encontrou 85 Beep; 86 end; 87 end; 88 end; 89 end;

8 de fev. de 2007

Como medir a memória utilizada por um aplicativo Delphi?

Com frequência vejo pessoas tentando medir o consumo de memória de um programa feito em Delphi usando o Gerenciador de Tarefas do Windows. Por motivos técnicos, essa não é a melhor forma de medir o consumo de memória do programa.

A grosso modo, o Gerenciador de Tarefas mede o máximo de memória seu programa alocou, não o quanto ele está usando; para piorar a memória que é liberada de volta para o Windows mas que ainda não foi utilizada por outro aplicativo é contabilizada nesse total pois essa memória disponível é considerada pelo Windows como disponível para uso pelo seu programa, embora ela possa ser usada por qualquer programa.

Gerenciador de Memória

Um dos motivos que a informação do Gerenciador de Tarefas estar errada é que um programa em Delphi aloca indiretamente memória do Windows, através de um gerenciador de memória do runtime do Delphi.

Quando o programa solicita memória para o gerenciador, ele aloca blocos de memória para o programa. Os blocos tem tamanho fixo de modo que para cada pedido de memória o gerenciador aloca a quantidade necessária de blocos contínuos para satisfazer o pedido do programa. Logo, um bloco é a menor quantidade de bytes que o gerenciador aloca. Por exemplo, imagine que cada bloco tem 32 bytes; se o programa pede 4 bytes, o gerenciador irá alocar um bloco; se o programa pedir 40 bytes, o gerenciador irá alocar dois blocos.

Pode parecer desperdício, mas isso é feito para evitar a fragmentação da memória. A fragmentação ocorre quando, apesar de haver memória livre para atender a um pedido de memória, não existem blocos contínuos suficientes para atender à solicitação. Caso o gerenciador fique sem blocos disponíveis para atender uma solicitação do programa, ele irá solicitar mais memória ao Windows.

Quando o programa devolve memória para o gerenciador, ele não devolve totalmente ao Windows a memória que você alocou porque ele imagina que o programa vai requisitar novamente mais memória. Isso é feito para otimizar os tempos de alocação de memória.

Vamos imaginar que inicialmente o programa alocou 100 blocos de memória do gerenciador. O programa cria algum form e precisa de mais 20 blocos de memória; o gerenciador irá providenciar 20 blocos de memória do Windows para poder atender a solicitação; nesse momento, o programa tem alocado 120 blocos de memória.

Quando o form é destruído e a memória devolvida ao gerenciador, ele irá marcar os 20 blocos de memória que foram usados pelo form como sendo disponíveis. O programa continua usando 120 blocos. Imagine que o programa crie outro form que precise de 10 blocos; como tem 20 disponíveis, o gerenciador não pede memória para o Windows, ele usa 10 dos 20 disponíveis.

Delphi 2006 e FastMM

Até o Delphi 2005, todas as versões do Delphi utilizaram o mesmo gerenciador de memória (eventualmente, com pequenas alterações). Mas o modo como é implementado o gerenciador de memória sempre permitiu o surgimento de ferramentas de terceiros para depurar a alocação de memória (localizar vazamentos/leaks, sobrescrita, etc) e até mesmo gerenciadores de memória alternativos.

Um desses gerenciadores de memória alternativos é o FastMM. Esse gerenciador é um dos mais rápidos e baseia-se na idéia de que um programa costuma alocar com mais frequencia pequenas quantidades de memória do que gransdes quantidades. Partindo dessa idéia, o FastMM usa três diferentes estratégias de gerenciamento de memória, dependendo da quantidade de memória que o programa pede. O resultado é que programas que criam/destroem muitos objetos tem desempenho melhor com o FastMM.

A Borland reconheceu a qualidade do FastMM e adotou o FastMM como gerenciador padrão de memória do Delphi 2006. Para quem não tem o Delphi 2006 mas gostaria de experimentar, visite a página do FastMM no SourceForge.net (baixe a versão 4.x mais recente, as versões antigas 2.x e 3.x estão lá apenas para quem mantem programas que usam essas versões)

Medir a memória utilizada

Para quem usa Delphi 2005 ou anterior é simples saber quantos blocos o gerenciador de memória pegou do Windows e quanto de memória seu programa está consumindo através do gerenciador de memória:

1 var
2   Blocos: Integer;
3   Bytes: Integer;
4 ...
5   Blocos := AllocMemCount;
6   Bytes := AllocMemSize;

Para quem usa Delphi 2006 ou usa o FastMM ao invés do gerenciador de memória padrão, o cálculo é um pouco mais trabalhoso:

1 var 2 Estado: TMemoryManagerState; 3 Blocos: Integer; 4 Bytes: Integer; 5 I: Integer; 6 ... 7 Blocos := 0; 8 Bytes := 0; 9 10 GetMemoryManagerState(Estado); 11 12 for I := 0 to High(Estado.SmallBlockTypeStates) do begin 13 14 Inc(Blocos, Estado.SmallBlockTypeStates[I].AllocatedBlockCount); 15 Inc(Bytes, Estado.SmallBlockTypeStates[I].AllocatedBlockCount 16 * Estado.SmallBlockTypeStates[I].UseableBlockSize); 17 end; 18 19 Inc(Blocos, Estado.AllocatedMediumBlockCount); 20 Inc(Bytes, Estado.TotalAllocatedMediumBlockSize); 21 22 Inc(Blocos, Estado.AllocatedLargeBlockCount); 23 Inc(Bytes, Estado.TotalAllocatedLargeBlockSize); 24 ...

Quem estiver usando o FastMM, deve incluir no uses da unit que contem esse código a unit FastMM4 (ou FastMM3, se for o caso).

Uma diferença do FastMM para o gerenciador de memória usado até o Delphi 2005 é que o FastMM tem diferentes estratégias de gerenciamento da memória conforme o tamanho do bloco de memória a ser alocado. Por isso, o código para calcular o consumo de memória é um pouco mais complicado.

Em ambos os casos, Blocos é o total de blocos de memória alocados pelo gerenciador e Bytes é a quantidade de memória que o programa está usando.

Links relacionados

7 de fev. de 2007

Como desabilitar o ghosting do Windows?

Desde o Windows 2000 o Windows é capaz de detectar aplicações que estejam congeladas/travadas. Essa detecção se baseia na idéia de que um aplicativo está congelado/travado se...

  • não está inicializando E
  • não está esperando por entrada de dados do usuário E
  • está a cinco segundos sem processar a fila de mensagens (via PeekMessage)

É graças a essa capacidade que o Windows consegue dar o aviso de que "tal aplicativo não está respondendo".

O Windows XP SP1 adicionou a capacidade de ghosting: ao detectar que um aplicativo não está respondendo (está congelado/travado), o Windows cria uma janela fantasma na mesma posição, com o mesmo tamanho e os mesmos atributos da janela do aplicativo que não está respondendo. Isso permite ao usuário minimizar, maximizar e mover a janela ou até mesmo fechar o aplicativo.

Caso o aplicativo volte a responder, o Windows restaura a janela do aplicativo na mesma posição, com o mesmo tamanho e os mesmos atributos da janela fantasma que foi criada enquanto o aplicativo não respondia.

Sem a janela fantasma, o que o usuário vê na maior parte dos casos é uma janela em branco ou contendo partes de outras janelas que eventualmente tenham sidos postas sobre a janela do aplicativo que não está respondendo. Teoricamente, a janela fantasma dá ao usuário a impressão que o aplicativo está funcionando "normalmente".

Infelizmente, no caso do Delphi, isso causa problemas com forms exibidos usando ShowModal quando o Windows restaura a janela do aplicativo. O form mostrado com ShowModal pode ficar atrás do form principal do aplicativo.

Se estiver tendo esse tipo de problema, inclua a procedure seguinte na unit do seu form principal (nem precisa ser método do form) e execute-a no evento OnCreate do form principal:

1 procedure DesabilitarGhostingDoWindows; 2 var 3 User32: HMODULE; 4 DisableProcessWindowsGhosting: TProcedure; 5 begin 6 User32 := GetModuleHandle('USER32'); 7 if User32 <> 0 then begin 8 DisableProcessWindowsGhosting := 9 GetProcAddress(User32, 'DisableProcessWindowsGhosting'); 10 if Assigned(DisableProcessWindowsGhosting) then begin 11 DisableProcessWindowsGhosting; 12 end; 13 end; 14 end;

Tópicos relacionados no MSDN...

6 de fev. de 2007

Como percorrer as pastas do sistema de arquivos?

Este exemplo mostra como percorrer as pastas do sistema de arquivos:

1 using System; 2 using System.IO; 3 4 public class Exemplo { 5 6 public static void PercorrerPasta(string oNomeDaPasta) { 7 8 DirectoryInfo dir = new DirectoryInfo(oNomeDaPasta); 9 10 // processa eventuais subdiretórios 11 string[] subdirs = dir.GetDirectories(oNomeDaPasta); 12 foreach (string subdir in subdirs) { 13 14 PercorrerPasta(subdir); 15 } 16 17 FileInfo[] files = dir.GetFiles(); 18 foreach (FileInfo file in files) { 19 20 // processa o arquivo encontrado 21 } 22 } 23 24 [STAThread] 25 public static void Main() { 26 PercorrerPasta('c:\'); 27 } 28 }

1 de fev. de 2007

Floating Point Overflow ao Imprimir com Delphi

Existem relatos frequentes sobre a ocorrência de erros de floating point overflow ao imprimir a partir de programas feitos em Delphi. Algumas pessoas chegam a afirmar que existe incompatibilidade entre Delphi e determinada marca de impressora.

Esse problema resulta de uma "incompatibilidade" no modo como o Delphi trata a FPU e os outros compiladores. O padrão do Delphi gera exceção no caso de problemas em cálculos envolvendo ponto flutuante enquanto os outros compiladores ignoram esses problemas (os cálculos resultam em NaN, INF, etc).

Quando se faz uma impressão, o programa em Delphi precisa usar uma DLL fornecida pelo fabricante da impressora (o driver da impressora). Embora drivers de impressão possam ser feitos em Delphi, é pouco provável que essa DLL tenha sido feita em Delphi. O resultado é que a DLL feita assumindo que erros envolvendo ponto flutuante podem ser ignorados é executada por um programa que assume o oposto. Assim, erros que normalmente a DLL ignoraria passam a não ser mais ignorados.

Se estiver fazendo impressão usando o objeto Printer, a solução seria algo assim...

1 var 2 Sav8087CW: Word; 3 ... 4 Sav8087CW := Default8087CW; 5 Set8087CW(0x133F); 6 try 7 Printer.EndDoc; 8 finally 9 Set8087CW(Sav8087CW); 10 end; 11 ...

Note que o driver da impressora geralmente só é usado quando TPrinter.EndDoc é executado. Não sei dizer se esse comportamento pode ser alterado por alguma configuração do Windows.

21 de jan. de 2007

Como criar emails em formato HTML usando Indy 9?

Exemplo de envio de email em formato HTML usando Delphi e Indy 9:

 1 var
 2   Email: TIdMessage;
 3   Texto: TIdText;
 4   Html: TIdText;
 5   Anexo: TIdAttachment;
 6 begin
 7   Email := TIdMessage.Create(nil);
 8   try
 9     // ...ajusta remetente, destinatário, etc...
10 
11     // define o tipo do conteúdo da mensagem
12     Email.ContentType := 'multipart/mixed';
13 
14     // cria a parte texto - pode estar em branco
15     Texto := TIdText.Create(Email.MessageParts);
16     Texto.Body.Text :=
17       'This message contains HTML and images.';
18     Texto.ContentType := 'text/plain';
19 
20     // cria a parte HTML
21     Html := TIdText.Create(Email.MessageParts);
22     Html.Body.Text := '<html><body>'
23       + 'Mensagem de <b>Teste</b><br />'
24       + '<img src="cid:imagem.jpg" />'
25       + '</body></html>';
26     Html.ContentType := 'text/html';
27 
28     // inclui no email a imagem usada na parte HTML
29     Anexo := TIdAttachment.Create(
30       Email.MessageParts, 'c:\pasta\imagem.jpg');
31     Anexo.ContentType := 'image/jpg';
32     Anexo.Headers.Add('Content-ID: <imagem.jpg>');
33 
34     // ... envia o email...
35   finally
36     Email.Free;
37   end;
38 end;
Pontos importantes:
  • A parte em formato text/plain deve estar presente embora não precise conter texto. Existe um bug no Indy que, na ausência dessa parte, faz com que o email seja gerado vazio.
  • Para ter imagens embutidas no email, inclua o header Content-ID e use na tag img o modificador cid: ao especificar a origem da imagem.

12 de jan. de 2007

Como enviar emails usando Indy 9?

Exemplo básico de envio de email usando Delphi e Indy 9:

1 var 2 Email: TIdMessage; 3 begin 4 // SMTP é um TIdSMTP 5 // POP3 é um TIdPOP3; 6 7 Email := TIdMessage.Create(nil); 8 try 9 10 // ...ajusta remetente, destinatário, etc da mensagem... 11 12 POP3.Host := 'seu-servidor-pop3'; 13 POP3.Username := 'seu-username'; 14 POP3.Password := 'sua-senha-secreta'; 15 16 POP3.Connect; 17 18 try 19 SMTP.Host := 'seu-servidor-smtp'; 20 SMTP.AuthType := atLogin; 21 SMTP.Username := 'seu-username'; 22 SMTP.Password := 'sua-senha-secreta'; 23 24 SMTP.Connect; 25 try 26 SMTP.Send(Email); 27 except 28 on E: Exception do begin 29 // envio falhou 30 end; 31 end; 32 SMTP.Disconnect; 33 finally 34 POP3.Disconnect; 35 end; 36 finally 37 Email.Free; 38 end; 39 end;

Pontos importantes:
  • Nem todo servidor SMTP requer autenticação: nesse caso, TIdSMTP.AuthType, TIdSMTP.Username e TIdSMTP.Password podem ser omitidos.
  • Nem todo servidor SMTP exige que antes se conecte ao servidor POP3: nesse caso, tudo que é relacionado com o servidor POP3 pode ser omitido. Note que quando isso é necessário, deve se permanecer conectado ao servidor POP3 enquanto estiver usando o servidor SMTP.
  • Permamenecer conectado aos servidores POP3 e SMTP o mínimo de tempo possível. A maioria desses servidores irá desconectar o cliente por inatividade ou por tempo.
  • Este exemplo não funciona com o GMail e outros servidores que usam SSL