23 de novembro de 2012

Integrando o Outlook em aplicações Delphi - Parte II

No último post, eu mostrei os conceitos básicos envolvidos na integração do Outlook com aplicações Delphi. O exemplo apresentado lá abordou a manipulação dos contatos cadastrados no Outlook bem como a criação de novos contatos. Neste post eu abordo o aspecto mais utilizado da ferramenta, que é lidar com envio e recebimento de emails.

Enviar um email com o Outlook de dentro de um programa Delphi é uma operação relativamente simples. Assim como no caso dos contatos, o ponto de partida é objeto Outlook.Application. Aqui, no entanto, não é necessário trabalhar com Namespaces. Basta criar uma instância de MailItem (este é o tipo de objeto do Outlook que representa uma nova mensagem), fornecer-lhe informações relevantes para o envio e, então, efetivamente enviá-la. O código a seguir exemplifica esses passos:
procedure TForm1.btnEnviarClick(Sender: TObject);
var OutlookApp: TOutlookApplication;
email : MailItem;
begin
OutlookApp := TOutlookApplication.Create(Nil);

email := OutlookApp.CreateItem(olMailItem) As MailItem;
email.Subject := 'Envio automático';
email.BodyFormat := olFormatHTML;
email.HTMLBody := 'Este email é um <b>teste</b> para envio <b><span style="color:maroon;">automático</span></b>.';
email.Importance := olImportanceNormal;

email.Recipients.Add('balaiotecnologico@gmail.com');
if (email.Recipients.ResolveAll) then
email.Send
else begin
Application.MessageBox('Um ou mais destinatários não puderam ser resolvidos.'#13+
'Reveja a informação e tente novamente.'
, 'Alerta', MB_ICONWARNING);
email.Display(true);
end;

OutlookApp.Disconnect;
OutlookApp.Free;
end;
Veja que a criação da instância da mensagem é feita diretamente no objeto que representa a aplicação Outlook. A função CreateItem usada para esse fim é genérica e permite criar qualquer item dentro do Outlook. Assim, o valor olMailItem passado como parâmetro garante que o que está sendo criado é uma nova mensagem.

Após a criação, eu formato a mensagem fornecendo valores para propriedades relevantes como o assunto (Subject), a prioridade da mensagem (Importance), o texto dela (Body ou HTMLBody, dependendo do formato escolhido) e os destinários que a receberão (Recipients).

Opcionalmente, o método ResolveAll da lista de destinatários é chamado para verificar se os endereços incluídos são válidos. Em caso negativo, a própria tela de envio de email do Outlook é apresentada para que o usuário faça as correções necessárias antes de enviar o email manualmente.

Uma vez que a verificação foi feita com sucesso, a função Send é chamada para efetivamente fazer o envio. Por padrão, uma cópia da mensagem é armazenada na pasta Sent Items. Se não quiser guardar a cópia, use a propriedade DeleteAfterSubmit, ajustando-a para true. É possível também indicar outra pasta para guardar a cópia, modificando-se o valor da propriedade SaveSentMessageFolder.

Repare que não informei quem é o remetente da mensagem. Normalmente, o Outlook é configurado para se logar automaticamente quando a aplicação é executada, fazendo com que o remetente seja este usuário que fez o logon. É possível forçar outro logon por código, usando a função Logon de forma explícita e passando a ela as credenciais a serem utilizadas.

O código finaliza desconectando do Outlook e liberando a memória que esta sendo usada por ele.

Neste exemplo, passei o valor olFormatHTML na propriedade BodyFormat para permitir a inserção de código HTML diretamente em HTMLBody, o que flexibiliza a formatação da mensagem.

Embora eu não tenha tratado no código, é possível também incluir anexos no email. Um MailItem possui a propriedade Attachments para conter a lista de anexos. Antes do envio, podemos adicionar um ou mais anexos:
email.Attachments.Add('c:\ERP\abc71.jpg', EmptyParam, EmptyParam, EmptyParam);
O primeiro parâmetro é o caminho completo do arquivo a ser anexado. Os demais parâmetros são opcionais; passei EmptyParam a eles para que a função assuma valores internos padronizados para eles.

Para encerrar esse tópico, falo de um problema recorrente pra quem automatiza envio de email com o Outlook: a inclusão da assinatura padrão do usuário no corpo da mensagem. Da forma como foi exposto até aqui, apenas o texto incluído em HTMLBody será enviado, sem qualquer decoração extra que o usuário tenha configurado - como a assinatura ou um papel de carta. Isso acontece porque esses recursos não são inerentes ao MailItem mas sim ao editor associado a ele. Para acionar o editor, precisamos apenas invocar o método GetInspector. Um inspector é o responsável pela edição do item atual e, ao invocar o método, todo o ambiente para edição é criado, incluindo a formatação padrão para o item - neste caso, um email.

Isso preencherá o HTMLBody com um HTML completo, englobando a assinatura e outras formatações. Então, teremos que localizar a tag BODY desse HTML para acrescentar nosso texto personalizado no local correto sem perder a formatação padrão. O quadro abaixo mostra como obter esse efeito:
var { ... }
email : MailItem;
insp : Inspector;
idx: integer;
begin
{ ... }
insp := email.GetInspector;

idx := Pos ('<body', email.HTMLBody);
idx := PosEx ('>', email.HTMLBody, idx);
email.HTMLBody := MidStr (email.HTMLBody, 1, idx) +
'Este email é um <b>teste</b> para envio <b><span style="color:maroon;">automático</span></b>.' +
MidStr (email.HTMLBody, idx+1, Length(email.HTMLBody));

if (email.Recipients.ResolveAll) then
email.Send;
{...}
Nessa abordagem, apesar de o editor ser criado e sua configuração padrão ser respeitada, ele não é exibido para usuário. Se for necessário, chame o Display do MailItem para exibí-lo.

Comentei no post anterior mas não custa lembrar: todas as operações descritas neste post exigem que o Outlook esteja instalado no computador que for executá-las.

6 de novembro de 2012

Integrando o Outlook em aplicações Delphi - Parte I

Embora eu não tenha números exatos, o Outlook é o programa de email mais utilizado em desktops. Parte desse sucesso se deve à ampla adoção da ferramenta no ambiente corporativo, já que ela também centraliza funções de calendário, acompanhamento de tarefas e agenda de contatos, permitindo compartilhar facilmente as informações.

Um outro aspecto positivo é que o Outlook é componentizado, o que abre caminho para criação de extensões e integrações com outros sistemas. Neste post, mostro como utilizar os recursos COM do Outlook para integrá-lo a uma aplicação Delphi.

Do mesmo modo que as outras aplicações do Microsoft Office (como o Excel), todos os recursos disponíveis do Outlook devem se acessados a partir de um objeto centralizador que representa a própria aplicação. Neste caso, precisamos obter uma instância de Outlook.Application, mapeada pelo Delphi na unit OutlookXP, junto com as demais interfaces existentes para se trabalhar com a ferramenta.

O quadro abaixo mostra um exemplo dos passos básicos necessários para interagir com o Outlook. O código apresentado percorre os contatos registrados na pasta local do Outlook, listando informações de cada um deles numa tabela:
procedure TForm1.btnListarContatosClick(Sender: TObject);
var i : integer;
OutlookApp: TOutlookApplication;
ns : NameSpace;
pasta: MAPIFolder;
contato : _ContactItem;
begin
StringGrid1.RowCount := 2;
OutlookApp := TOutlookApplication.Create(Nil);

ns := OutlookApp.GetNamespace('MAPI');
pasta := ns.GetDefaultFolder(olFolderContacts);

for i := 1 to pasta.Items.Count do
begin
if (i > 1) then
StringGrid1.RowCount := StringGrid1.RowCount + 1;

contato := pasta.Items.Item(i) As _ContactItem;

StringGrid1.Cells [1,i] := contato.FullName;
StringGrid1.Cells [2,i] := contato.Email1Address;
StringGrid1.Cells [3,i] := contato.EntryID;
StringGrid1.Cells [4,i] := contato.CompanyName;
StringGrid1.Cells [5,i] := contato.BusinessAddress;
end;

OutlookApp.Disconnect;
OutlookApp.Free;
end;
Vamos destrinchar esse código. Após criar uma instância para acessar a aplicação (classe TOutlookApplication), o trecho recupera um tipo de objeto denominado Namespace. Atualmente, apenas o namespace MAPI (API de Mensagens do Windows) está disponível, concentrando todos os registros, isto é, os emails, contatos, tarefas e entradas do calendário. Um Namespace é organizado em pastas dispostas hierarquicamente, podendo haver pastas dentro de pastas para armazenar os itens.

Há algumas formas de acessar as pastas do Outlook. A propriedade Folders do Namespace lista todas as pastas, incluindo as compartilhadas com outros usuários no servidor. As subpastas eventualmente existentes são acessíveis por meio de propriedade Folders própria. E como saber que tipo de item uma pasta contém? Em princípio, pode-se consultar a propriedade DefaultItemType para se ter uma ideia. Mas, certas pastas (como a DeletedItems) podem conter itens de tipos diferentes misturados. Neste caso, recomenda-se checar a propriedade Class do item ou fazer um cast dinâmico para o tipo desejado e ver se a conversão foi bem sucedida.

Outra forma de acesso é requerer ao Namespace a pasta padrão para um determinado tipo de item, como no código mostrado acima. Lá, uso GetDefaultFolder para recuperar exclusivamente a pasta que armazena meus contatos. Por fim, se eu possuir a identificação única da pasta (seu entryID, posso recuperá-la diretamente com a função GetItemFromID do Namespace.

Voltando ao código, agora tenho uma instância da pasta de contatos para trabalhar. Como toda pasta, esta possui uma lista Items que dá acesso ao conteúdo específico - no exemplo, os contatos cadastrados. Então, percorro a lista para prencher a tabela com as informações desejadas de cada um dos contatos.

Para concluir, desconecto minha instância do Outlook e removo-a da memória para liberar os recursos utilizados.

Note que não foi preciso informar usuário e senha para fazer logon. Isso acontece porque o Outlook pode ser configurado para se conectar automaticamente com o mesmo usuário do Windows. Se for preciso, pode-se logar manualmente usando a função Logon do Namespace.

Aproveitando os mesmos conceitos, podemos criar automaticamente um novo contato, conforme mostra o trecho de código a seguir:
contato := pasta.Items.Add(olContactItem) As _ContactItem;
contato.FirstName := 'Balaio';
contato.LastName := 'Tecnológico';
contato.CompanyName := 'ABC71 Soluções Em Informática';
contato.Save;
contato.Display(1);
Como se vê, a criação é bastante simples. Primeiro, chamo o método Add da lista de itens da pasta, fornecendo-lhe o tipo de item que deve ser criado - um item de contato, neste caso. Após prover algumas informações básicas do contato, salvo o registro. A última linha não é imprescindível; ela apenas exibe no próprio Outlook o contato recém-criado para que o usuário possa complementar as informações, se desejar.

De forma similar, conseguimos trabalhar com tarefas e compromissos no calendário. Também é possível fazer o envio de email aproveitando a infraestrutra já configurada do Outlook, operação que mostro no próximo post.

Obviamente, todos os recursos apresentados aqui exigem que haja uma cópia do Outlook instalado no computador local para funcionar.