Amélie
Amélie

Reputation: 119

Outlook : Move only read mails to a folder

To move all mails in Outlook I use the following code:

var
  App, NS, Inbox, Items, OtherFolder, Item: OleVariant;
  i: Integer;
begin
  App := CreateOleObject('Outlook.Application');
  NS := App.GetNamespace('MAPI');
  NS.Logon;

  Inbox := NS.GetDefaultFolder(olFolderInbox);    
  OtherFolder := Inbox.Parent.Folders('Eléments supprimés');

  Items := Inbox.Items;
  for i := Items.Count downto 1 do
  begin
    Item := Items.Item(i);
    Item.Move(OtherFolder);
  end;
end.

Is there a way to move only all read mails to the folder?

Upvotes: 1

Views: 289

Answers (1)

Peter Wolf
Peter Wolf

Reputation: 3830

The simple answer is to use Item.UnRead to determine whether the item has been read, however:

  1. Not all items in a folder are necessarily instances of MailItem that supports the UnRead property. You're using late binding to automate MS Outlook and the code will crash if you try to access UnRead property on an item that doesn't support it.
  2. There are more effective ways to do it.

To demonstrate the options let's first move away from late binding which makes things somewhat complicated and slower. Add unit Outlook2010 to your uses clause. The source code of the unit is located in OCX\Servers subfolder of RAD Studio installation location. The skeleton of code to process mail items using early binding could then look like this:

uses
  System.SysUtils, System.Variants, Outlook2010;

var
  App: OutlookApplication;
  InboxFolder, OtherFolder: Folder;
begin
  App := CoOutlookApplication.Create;
  App.Session.Logon(EmptyParam, EmptyParam, EmptyParam, EmptyParam);
  InboxFolder := App.Session.GetDefaultFolder(olFolderInbox);
  OtherFolder := (InboxFolder.Parent as Folder).Folders.Item('Eléments supprimés');
  ProcessFolder(InboxFolder, procedure(const Item: MailItem)
  begin
    Item.Move(OtherFolder);
  end);
end.

The naïve implementation of ProcessFolder would be:

type
  TMailItemProc = reference to procedure(const Item: MailItem);

procedure ProcessFolder(const AFolder: Folder; MailItemProc: TMailItemProc);
var
  ItemsToProcess: Items;
  Item: MailItem;
  Index: Integer;
begin
  ItemsToProcess := AFolder.Items;
  for Index := ItemsToProcess.Count downto 1 do
    if Supports(ItemsToProcess.Item(Index), MailItem, Item) and (not Item.UnRead) then
      MailItemProc(Item);
end;

This approach sucks as it has to go through each item in the folder and check its UnRead status. This can cause performance issues when the number of items in the folder is high. But we can do better. Let Outlook do the hard work. And there are multiple ways to do it. The following snippets were inspired by article How to get unread mail in Outlook.

Using Items.Restrict method:

procedure ProcessFolder(const AFolder: Folder; MailItemProc: TMailItemProc);
var
  ItemsToProcess: Items;
  Item: MailItem;
  Index: Integer;
begin
  ItemsToProcess := AFolder.Items.Restrict('[UnRead]=false');
  for Index := ItemsToProcess.Count downto 1 do
    if Supports(ItemsToProcess.Item(Index), MailItem, Item) then
      MailItemProc(Item);
end;

Using Items.Find and Items.FindNext methods:

procedure ProcessFolder(const AFolder: Folder; MailItemProc: TMailItemProc);
var
  ItemsToProcess: Items;
  FoundItem: IDispatch;
  Item: MailItem;
begin
  ItemsToProcess := AFolder.Items;
  FoundItem := ItemsToProcess.Find('[UnRead]=false');
  while Assigned(FoundItem) do
  begin
    if Supports(FoundItem, MailItem, Item) then
      MailItemProc(Item);
    FoundItem := ItemsToProcess.FindNext;
  end;
end;

Using Folders.GetTable method:

procedure ProcessFolder(const AFolder: Folder; MailItemProc: TMailItemProc);
var
  Session: NameSpace;
  Table: OutlookTable;
  LRow: Row;
  FoundItem: IDispatch;
  Item: MailItem;
begin
  Session := AFolder.Session;
  Table := AFolder.GetTable('[UnRead]=false', EmptyParam);
  while not Table.EndOfTable do
  begin
    LRow := Table.GetNextRow;
    FoundItem := Session.GetItemFromID(VarToStr(LRow.Item('EntryId')), EmptyParam);
    if Supports(FoundItem, MailItem, Item) then
      MailItemProc(Item);
  end;
end;

Upvotes: 2

Related Questions