Envio de E-Mails no Delphi Win32

Desafio – Clientes MAPI

O envio de emails sempre é um recurso necessário nas aplicações de hoje, sejam estas cliente/servidor, multicamadas. O e-mail é uma ferramenta excelente para notificar usuários, realizar envio de informações programadas e com o uso principal, permitir a comunicação entre pessoas! J

Primeiro vamos ao básico!

A forma mais simples de se programar um envio de email é utilizando funções como a ShellExecute, disponível na unit ShellAPI. Vamos ao primeiro exemplo. Abra o menu “Iniciar”/”Start” do Windows e acesse a opção “Executar”/”Run”. Ali digite mailto:emailteste@email.com e aperte o botão “OK”. Você vai verificar que o Windows vai procurar pelo cliente de e-mail configurado no seu computador para realizar o envio do email. Quanto a isto não importa qual o programa utilizado, OutlookExpress, Eudora, Outlook ou Mozilla ThunderBird por exemplo.

A principal questão de se querer fazer uso do cliente de email já instalado é a questão do histórico de mensagens. Sem isto, você precisaria ter na sua aplicação uma tabela para armazenar os emails enviados pelo usuário do sistema. Usando MAPI não temos este problema, pois estamos nos comunicando com o cliente de email do sistema.

Hein? MAPI? O que é isto? MAPI significa Messaging Application Programming Interface! É uma API com uma série de funcionalidades para se realizar por exemplo o envio de e-mails! Usando a API do Windows, fazemos uses da unit MAPI e utilizamos estruturas como  TMapiMessage, TMapiRecipDesc e PMapiFileDesc. Depois de tudo utilizamos a função MapiSendMail e com o retorno podemos tratar um erro ocorrido no envio da mensagem.

Parece difícil né? Bom, usar API do Windows sempre complica no início, até que se tenha domínio das mensagens, estruturas e funções a serem utilizadas. Veremos neste artigo como facilitar o uso e não cair direto na API do Windows.

Usando o ShellExecute para envio de E-Mail

Para o primeiro exemplo iremos montar uma tela para realizar um envio de mensagem, como se fosse uma caixa de nova mensagem do cliente de e-mail utilizado no seu sistema.

Veja a Figura 1 com uma tela da aplicação montada. Temos um conjunto de componentes TEdit e TLabel para os campos gerais do email, e um TMemo para o processo de escrita da mensagem. Na Figura 2 podemos verificar a tela que se abre do programa cliente de e-mail configurado no sistema, neste caso o Outlook Express.

img 

Figura 1: Tela da aplicação de exemplo de envio de email utilizando ShellExecute

img 

Figura 2: O diálogo que se abre do cliente de email, após a execução do ShellExecute

Veja o código para realizar este envio na Listagem 1:

 

Listagem 1: Código para realizar o envoi de e-mails utilizando ShellExecute

procedure TFormPrincipal.BtnEnviaEMailClick(Sender: TObject);

var

  StringShellExecute: String;

begin

  StringShellExecute := StringShellExecute +

   'mailto:' +

   edtPara.Text +

   '?cc=' + edtCC.Text +

   '&cco=' + edtCCO.Text +

   '&subject=' + edtAssunto.Text +

   '&body=' + memoMensagem.Lines.Text;

  ShellExecute(Self.Handle, 'open',PChar(StringShellExecute),'','',SW_SHOWNORMAL);

end;

 

Problemas que encontramos utilizando esta solução:

1.      Não é permitido o envio de anexos com esta solução

2.      As quebras de linha não são respeitadas. No exemplo foi colocado um código para inclusive impedir que o usuário digite a tecla Enter (caractere ASCII #13).

Mudando a tecnologia para MAPI

Uma boa notícia para se fazer uso da tecnologia MAPI é que alguém já passou por ali e já montou um componente para facilitar o nosso trabalho! E melhor ainda saber que este alguém é o IndyProject, que tem o apoio da Nevrona (RaveReports) e da Atozed (Intraweb e FinalBuilder).

Veremos neste exemplo como configurar o componente IdMessage, disponível na paleta de componentes “Indy Misc”. Este componente nos permite configurar um e-mail completo, seja para envio com texto ou HTML. No exemplo iremos fazer o envio utilizando somente texto. Com o uso do IdMessage não teremos mais problemas para quebrar linha nem teremos problemas para realizar envio de mensagens com anexos.

Veja na Figura 3 como fica a nossa nova interface do novo exemplo. Adicionei um componente TListBox para indicar os anexos.

img 

Figura 3: Tela utilizando o componente IdMessage para configuração da mensagem MAPI

Aproveitando um pouco o uso da Orientação a Objetos, criei uma classe para simplificar o processo de configuração do envio de e-mail e facilitar uma futura troca de componente. Estamos com isto minimizando acoplamento. Esta classe criada se chama TMailDTO e implementa os parâmetros necessários para se realizar o envio de mensagens. Veja na Listagem 2 a declaração desta classe:

 

Listagem 2: Código fonte da classe TMailDTO.

  TMailDTO = class(TPersistent)

  private

    FSubject: String;

    FToRecipients: TStringList;

    FCCRecipients: TStringList;

    FAttachments: TStringList;

    FCCoRecipients: TStringList;

    FBody: TStringList;

    FCharSet: String;

    FHighPriority: boolean;

    FContentType: String;

    FReplyTo: TStringList;

    FTempFile: String;

    procedure SetTempFile(const Value: String);

    procedure SetCharSet(const Value: String);

    procedure SetContentType(const Value: String);

    procedure SetHighPriority(const Value: boolean);

    procedure SetSubject(const Value: String);

  public

    property Subject : String read FSubject write SetSubject;

    property ToRecipients: TStringList read FToRecipients;

    property CCRecipients: TStringList read FCCRecipients;

    property CCoRecipients: TStringList read FCCoRecipients;

    property Body: TStringList read FBody;

    property ContentType: String read FContentType write SetContentType;

    property ReplyTo: TStringList read FReplyTo;

    property HighPriority: boolean read FHighPriority write SetHighPriority;

    property CharSet: String read FCharSet write SetCharSet;

    property Attachments : TStringList read FAttachments;

    property TempFile : String read FTempFile write SetTempFile;

    constructor Create;

  end;

 

Para facilitar o uso desta classe, criei um TDataModule e nele tenho uma função que realiza o envio de mensagens. Os parâmetros são recebidos em forma de TStringLists, que facilitam a interação com a interface do usuário. Estes parâmetros são adaptados e configurados no componente IdMessage. Com isto se cria uma abstração, facilitando o uso da função para envio usando MAPI. Veja na listagem 3 o código da função criada no DataModule. Neste DataModule é onde colocamos um componente do tipo TIdMessage. Veja o visual do DataModule na Figura 4:

img 

Figura 4: Visual do DataModule com o componente IdMessage

 

Listagem 3: Classe de controle para envio de mensagem utilizando MAPI

Interface

...

  TDMMAPIMail = class(TDataModule)

    IdMessage: TIdMessage;

  private

    procedure AddToAddressList(AnAddressList: TIDEMailAddressList;

      AStringList: TStringList);

    procedure AddToAttachmentList(AMessageParts: TIdMessageParts;

      AStringList: TStringList);

  public

    procedure SendMail(AMailDTO: TMailDTO);

  end;

...

Implementation

...

procedure TDMMAPIMail.AddToAddressList(AnAddressList: TIDEMailAddressList;

  AStringList: TStringList);

var

  tempStr: String;

  addressItem: TIdEMailAddressItem;

begin

  for tempStr in AStringList do

  begin

    if(Trim(tempStr) <> '') then

    begin

      addressItem := AnAddressList.Add;

      addressItem.Address := tempStr;

      addressItem.Name := tempStr;

    end;

  end;

end;

 

procedure TDMMAPIMail.AddToAttachmentList(AMessageParts: TIdMessageParts;

  AStringList: TStringList);

var

  attach: TIdAttachment;

  tempStr: String;

begin

  for tempStr in AStringList do

  begin

    if(Trim(tempStr) <> '') then

    begin

      attach := TIdAttachment.Create(AMessageParts);

      attach.FileName := ExtractFileName(tempStr);

      attach.StoredPathName := tempStr;

    end;

  end;

 

end;

 

procedure TDMMAPIMail.SendMail(AMailDTO: TMailDTO);

begin

  IdMessage.Subject := AMailDTO.Subject;

  IdMessage.CharSet := AMailDTO.CharSet;

  IdMessage.ContentType := AMailDTO.ContentType;

  IdMessage.Body.Text := AMailDTO.Body.Text;

  IdMessage.Headers.Add('X-Unsent: 1');

 

  AddToAddressList(IdMessage.Recipients,AMailDTO.ToRecipients);

  AddToAddressList(IdMessage.CCList,AMailDTO.CCRecipients);

  AddToAddressList(IdMessage.BccList,AMailDTO.CCoRecipients);

  AddToAddressList(IdMessage.ReplyTo,AMailDTO.ReplyTo);

 

  AddToAttachmentList(IdMessage.MessageParts,AMailDTO.Attachments);

 

  IdMessage.SaveToFile(AMailDTO.TempFile);

  ShellExecute(0, 'open', PChar(AMailDTO.TempFile), '','', SW_SHOWNORMAL);

end;

 

Uma questão importante nesta chamada para SendMail é a adição do Header “X-Unsent”, para indicar que a mensagem é nova e ainda não foi enviada! Sem isto a mensagem é aberta como se tivesse sido uma mensagem recebida!

Para finalizar, configurado o componente IdMessage o trabalho agora é gravar a mensagem em disco e fazer o arquivo ser aberto pelo cliente de email.

No teste estamos salvando o arquivo com a extensão eml que é a extensão que o Outlook Express salva as mensagens em disco. Feito isto “executamos” o mesmo usando a função ShellExecute (unit ShellAPI).

Conclusões

E chegamos ao final de mais uma dica. Agora não tem desculpa, você pode e deve agregar a funcionalidade de envio de emails na sua aplicação. Se você não precisa anexar arquivos, o simples uso do ShellExecute pode lhe dar uma boa produtividade no uso dos emails.

E precisando de um processo mais complexo, pode fazer uso do component IdMessage ou utilizar este DataModule disponível no exemplo para download!

 

Links

http://www.outlookcode.com/d/mapi.htm

Uso de MAPI no Outlook/Exchange

http://www.indyproject.org/
Página do projeto dos componentes Indy
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/exchanchor/htms/msexchsvr_mapi.asp
Página no MSDN sobre MAPI e Exchange