Search code examples
delphioutlookfiremonkeyole

Collecting emails from Outlook Inbox with Delphi


I wrote some code to read emails from Outlook Inbox and collect attachments.

This is working just fine. I am using Outlook 2019/Office 365.

I can use both Mail.SenderEmailAddress or Mail.Sender.Address to get the email address of the sender.

When deploying my application to another computer with Outlook 2016, I get this error:

EOleError: Method 'Sender' not supported by automation object

Same for Mail.SenderEmailAddress

Outlook 2016 or 2019 have the same code stream. See Outlook versions, build numbers and other trivia.

Could you help understanding why I get such error on my client's computer and how can I fix that problem?

Are you aware of a free/commercial library or components that could do that smoothly?

This is somehow linked with my other question about Sending Outlook Email with Delphi.

try
  Outlook:=GetActiveOleObject('Outlook.Application') ;
except
  Outlook:=CreateOleObject('Outlook.Application') ;
end;

try
  oNameSpace := Outlook.GetNamespace('MAPI');
  oNameSpace.Logon;

  Inbox:= oNameSpace.GetDefaultFolder(6);
  iNbMail:= Inbox.Items.Count;

  for i:= iNbMail downto 1 do
  begin
    if VarIsNull(Inbox.Items[i]) or VarIsEmpty(Inbox.Items[i])  then
      Continue;

    Mail:= Inbox.Items[i];
    EmailAddress:= Mail.Sender.Address;
    // EmailAddress:= Mail.SenderEmailAddress;
          
    UnReadFlag:= Mail.UnRead;
    iNbAttach := Mail.Attachments.Count;

    for j := iNbAttach downto 1 do
    begin
      Attachment:= Mail.Attachments[j];
      if ExtractFileExt(Attachment.FileName) = '.pdf' then
      begin
        SaveName:= TPath.Combine(InboxFolder, Attachment.FileName);
        Attachment.SaveAsFile(SaveName);
      end;
    end;
    Mail.UnRead:= False;
  end;
finally
  Outlook:= Unassigned;
  oNameSpace:= Unassigned;
  Inbox:= Unassigned;
  Mail:= Unassigned;
end;

Solution

  • Inbox folder in Outlook is not restricted to contain only mail messages. When iterating through its items you can encounter various item classes like MailItem, PostItem, MeetingItem, TaskRequestItem and many others. All of these items support different sets of properties and not all of them have Sender, SenderEmailAddress or Attachments property. If you're interested only in mail items then you need to check item's Class property:

    const
      olMail = $0000002B;
    
    { ... }
    
    for i := iNbMail downto 1 do
    begin
      Mail := Inbox.Items[i];
      if Mail.Class <> olMail then
        Continue;
    
      { here we can assume we're working with MailItem instance }
      
    end;
    

    I doubt that Outlook ever returns null or empty item, so the check you do in your code is pointless. If you're interested in other item classes then check out OlObjectClass enumeration.

    I'm not sure why you prefer to use late-binding, because Delphi already comes with imported type library Outlook2010.pas to automate Outlook in strongly typed manner. The library is located in OCX\Servers subfolder of installation folder. In case you need to support pre-2010 Outlook versions you can use unit OutlookXP or even Outlook2000 instead. The code fore iterating mail items using the type library could look like this:

    uses
      System.SysUtils, System.Variants, Winapi.ActiveX, Outlook2010;
    
    function GetOutlookApplication: OutlookApplication;
    var
      ActiveObject: IUnknown;
    begin
      if Succeeded(GetActiveObject(OutlookApplication, nil, ActiveObject)) then
        Result := ActiveObject as OutlookApplication
      else
        Result := CoOutlookApplication.Create;
    end;
    
    procedure ProcessInboxItems;
    var
      Outlook: OutlookApplication;
      Inbox: Folder;
      Index: Integer;
      LItems: Items;
      LMailItem: MailItem;
    begin
      Outlook := GetOutlookApplication;
      Outlook.Session.Logon(EmptyParam, EmptyParam, EmptyParam, EmptyParam);
    
      Inbox := Outlook.Session.GetDefaultFolder(olFolderInbox);
      LItems := Inbox.Items;
      for Index := LItems.Count downto 1 do
      begin
        if Supports(LItems.Item(Index), MailItem, LMailItem) then
        begin
          { do whatever you wish with LMailItem }
        end;
      end;
    end;