Solved

Delphi 7 - Importing Accounts, Emails etc from Outlook and Outlook Express

Posted on 2006-10-19
4
1,506 Views
Last Modified: 2008-01-09
Dear Experts,

Hope this is not to big question.

I need to import all folders, emails, accounts from
1. outlook
2. outlook express.

a) Is there a component that can asssist me?
b) If not then is it possible. I would like to see examples and tutorials or what else
there is to help me achieve this.
NB: for Outlook AND Outlook Express.

0
Comment
Question by:Marius0188
4 Comments
 
LVL 9

Expert Comment

by:alkisg
ID: 17772684
Just some starting points:
For Express, you'll need the Windows Address Book API Reference:
http://windowssdk.msdn.microsoft.com/en-us/library/ms629440(VS.80).aspx
For Outlook, you'll need the standard com objects that VBA uses (Outlook.application etc).
0
 

Author Comment

by:Marius0188
ID: 17787352
I would like to get some guide lines for using each of these.
Actually some example code will be appreciated a lot!

Thanks for help so far.
Do you have any further knowledge of implementing and using these objects / api?

I am also willing to increase the points for complete help.
If it's possible to increase the points at all.

:)
0
 
LVL 9

Accepted Solution

by:
bernani earned 500 total points
ID: 17983100

Hi,

The question how to access Outlook express msg seem often asked so the units reproduced here and may be of interest for the EE members.

I Don't remember where I downloaded those units and demos.
One for Outlook : by Perr Lothar. e-mail: DELETETHISUPPERCASEDPARTlothar.perr@gmx.net
One for Outlook Express.: Walther Estergaard DELETETHISUPPERCASEDPARTwalther_e@yahoo.com

I've got an (old) unit called OutlookTools (the zip doesnt contain no license text , no readme, nothing. The only info can be seen in the declaration of the about box. The tools seems come from by Perr Lothar. e-mail: lothar.perr@gmx.net.  I suppose in this case I can reproduce the content here without to infringe copyright).

The component:

unit OutLookTools;

interface

uses
  Windows, Classes, ComCtrls, SysUtils, Dialogs, ComObj,Graphics, Controls, Forms, DsgnIntf;

{

Outlook Toolbox

by Perr Lothar
e-mail: lothar.perr@gmx.net

}


const
// OlOutlookBarViewType
  olLargeIcon      = 0;
  olSmallIcon      = 1;
// OlDaysOfWeek
  olFriday = 32;
  olMonday = 2;
  olSaturday = 64;
  olSunday = 1;
  olThursday = 16;
  olTuesday = 4;
  olWednesday = 8;
//OlSortOrder
  olAscending = 1;
  olDescending = 2;
  olSortNone = 0;
//OlItemType
  olAppointmentItem = 1;
  olContactItem = 2;
  olDistributionListItem = 7;
  olJournalItem = 4;
  olMailItem = 0;
  olNoteItem = 5;
  olPostItem = 6;
  olTaskItem = 3;
//OlDefaultFolders
  olFolderCalendar = 9;
  olFolderContacts = 10;
  olFolderDeletedItems = 3;
  olFolderDrafts = 16;
  olFolderInbox = 6;
  olFolderJournal = 11;
  olFolderNotes = 12;
  olFolderOutbox = 4;
  olFolderSentMail = 5;
  olFolderTasks = 13;

const
  msoControlButton = 1;
  msoButtonIcon = 1;
  msoButtonCaption = 2;
  msoButtonIconAndCaption = 3;

type
  TToolbar =  Variant;
  TContact =  Variant;
  TContacts = Variant;

  TAboutProperty = class(TPropertyEditor)
  private
    procedure Edit; override;
    function GetAttributes: TPropertyAttributes; override;
    function GetValue: string; override;
  end;

type
  TCustomEnumWindowsProc = procedure(WinHandle : HWND);

  TWindowInfo = class(TObject)
  public
    Handle : HWND;
  end;

  TOutlookConnect = class(TComponent)
  private
    FAbout:TAboutProperty;
    MyOlApp:Variant;  // Instanz von OLE-Object Outlook.Application
    MyNameSpace:Variant; // Namespace von Outlook
    Active: Boolean;  // Angemeldet?
    MyOLEObject:String;
    MyOLENameSpace:String;
    fOnConnected: TNotifyEvent;
    fOnDisConnected: TNotifyEvent;
    Constructor Create(AOwner : TComponent); override;  //Initialisierung
    Destructor Destroy; override; // Objekt zerstören
    procedure ConnectOutlook(Connect:Boolean);
  protected
  public
    function Contact(Index:Variant):Variant;
    function ContactCount:Word;
    procedure ShowContact(Index:Word);
    function GetSelection(Index:Word):Variant;
    function GetSelectionCount:Word;
    function CreateContact:Variant;
    procedure DeleteContact(MyContact:Variant);
    function FindContact(FindWhat:String):Variant;
    function OutlookApplication: Variant;
    function OutlookNameSpace: Variant;
    function OutlookActiveExplorer: Variant;
    function CommandBars:Variant;
    function Contacts:Variant;
    function Calendar:Variant;
    function DeletedItems:Variant;
    function Drafts:Variant;
    function Inbox:Variant;
    function Journal:Variant;
    function Notes:Variant;
    function Outbox:Variant;
    function SentMail:Variant;
    function Tasks:Variant;
  published
    property About : TAboutProperty read FAbout write FAbout;
    property Connected : Boolean read Active write ConnectOutlook;
    property OLEObject : String read MyOLEObject write MyOLEObject;
    property OLENameSpace : String read MyOLENameSpace write MyOLENameSpace;
    property OnConnect: TNotifyEvent read fOnConnected write fOnConnected;
    property OnDisConnect: TNotifyEvent read fOnDisConnected write fOnDisConnected;
  end;

procedure Register;

implementation

{$r Outlooktools.res}

procedure TOutlookConnect.ConnectOutlook(Connect:Boolean);
begin
  if Connect then
  begin
    try
       MyOlApp:=CreateOleObject(OLEObject);
       MyNameSpace:=MyOlApp.GetNamespace(OLENameSpace);
       if (Assigned(fOnConnected)) then
         fOnConnected(Self);
    except
      raise Exception.Create('Outlook registration failed');
    end;
  end else
  begin
    MyOlApp:=NULL;
    MyNameSpace:=NULL;
    if (Assigned(fOnDisConnected)) then
      fOnDisConnected(Self);
  end;
  Active:=Connect;
end;

function TOutlookConnect.Contact(Index:Variant) : Variant;
begin
  if not Active then
    raise Exception.Create('No connection to outlook');
  try
    Result:=myNameSpace.GetDefaultFolder(olFolderContacts).Items[Index];
  except
    raise Exception.Create('Cannot import Item');
  end;
end;

function TOutlookConnect.Contacts : Variant;
begin
  if not Active then
    raise Exception.Create('No connection to outlook');
  try
    Result:=myNameSpace.GetDefaultFolder(olFolderContacts);
  except
    raise Exception.Create('Cannot access to Contacts');
  end;
end;

function TOutLookConnect.ContactCount:Word;
begin
  if not Active then
    raise Exception.Create('No connection to outlook');
  try
    Result:=myNameSpace.GetDefaultFolder(olFolderContacts).Items.Count;
  except
    Result:=0;
  end;
end;

function TOutlookConnect.GetSelection(Index:Word): Variant;
begin
  if not Active then
    raise Exception.Create('No connection to outlook');
  try
    Result:=MyOlApp.ActiveExplorer.Selection(Index);
  except
    raise Exception.Create('No item selected');
  end;
end;

function TOutlookConnect.GetSelectionCount:Word;
begin
  if not Active then
    raise Exception.Create('No connection to outlook');
  try
    Result:=MyOlApp.ActiveExplorer.Selection.Count;
  except
    result:=0;
  end;
end;

function TOutlookConnect.CreateContact:Variant;
var
  MyContact:Variant;
begin
  if not Active then
    raise Exception.Create('No connection to outlook');
  try
    MyContact:=myNameSpace.GetDefaultFolder(olFolderContacts).Items.Add;
    Result:=MyContact;
  except
    raise Exception.Create('Cannot create contact');
  end;
end;

procedure TOutlookConnect.DeleteContact(MyContact:Variant);
begin
  if not Active then
    raise Exception.Create('No connection to outlook');
  try
    MyContact.Delete;
  except
    raise Exception.Create('Cannot delete item');
  end;
end;

function TOutlookConnect.FindContact(FindWhat:String):Variant;
begin
  if not Active then
    raise Exception.Create('No connection to outlook');
  try
    Result:=myNameSpace.GetDefaultFolder(olFolderContacts).Items.Find(FindWhat);
  except
    raise Exception.Create('Error finding Item');
  end;
end;

function TOutlookConnect.OutlookApplication: Variant;
begin
  Result:=MyOlApp;
end;

function TOutlookConnect.OutlookNameSpace: Variant;
begin
  Result:=MyNameSpace;
end;

function TOutlookConnect.OutlookActiveExplorer: Variant;
begin
  Result:=MyOlApp.ActiveExplorer;
end;

function TOutlookConnect.CommandBars : Variant;
begin
  if not Active then
    raise Exception.Create('No connection to outlook');
  Result:=MyOlApp.ActiveExplorer.CommandBars;
end;

Procedure TOutLookConnect.ShowContact(Index:Word);
var
  MyContact:Variant;
begin
  if not Active then
    raise Exception.Create('No connection to outlook');
  try
    MyContact:=myNameSpace.GetDefaultFolder(olFolderContacts).Items[Index];
    MyContact.Display;
  except
    raise Exception.Create('Cannot display contact');
  end;
end;

function TOutlookConnect.Calendar:Variant;
begin
  if not Active then
    raise Exception.Create('No connection to outlook');
  Result:=myNameSpace.GetDefaultFolder(olFolderCalendar);
end;

function TOutlookConnect.DeletedItems:Variant;
begin
  if not Active then
    raise Exception.Create('No connection to outlook');
  Result:=myNameSpace.GetDefaultFolder(olFolderDeletedItems);
end;

function TOutlookConnect.Drafts:Variant;
begin
  if not Active then
    raise Exception.Create('No connection to outlook');
  Result:=myNameSpace.GetDefaultFolder(olFolderDrafts);
end;

function TOutlookConnect.Inbox:Variant;
begin
  if not Active then
    raise Exception.Create('No connection to outlook');
  Result:=myNameSpace.GetDefaultFolder(olFolderInbox);
end;

function TOutlookConnect.Journal:Variant;
begin
  if not Active then
    raise Exception.Create('No connection to outlook');
  Result:=myNameSpace.GetDefaultFolder(olFolderJournal);
end;

function TOutlookConnect.Notes:Variant;
begin
  if not Active then
    raise Exception.Create('No connection to outlook');
  Result:=myNameSpace.GetDefaultFolder(olFolderNotes);
end;

function TOutlookConnect.Outbox:Variant;
begin
  if not Active then
    raise Exception.Create('No connection to outlook');
  Result:=myNameSpace.GetDefaultFolder(olFolderOutbox);
end;

function TOutlookConnect.SentMail:Variant;
begin
  if not Active then
    raise Exception.Create('No connection to outlook');
  Result:=myNameSpace.GetDefaultFolder(olFolderSentMail);
end;

function TOutlookConnect.Tasks:Variant;
begin
  if not Active then
    raise Exception.Create('No connection to outlook');
  Result:=myNameSpace.GetDefaultFolder(olFolderTasks);
end;

constructor TOutlookConnect.Create(AOwner : TComponent);
begin
  inherited Create(AOwner);
  if Active then ConnectOutlook(True);
  if OLEObject='' then OLEObject:='Outlook.Application';
  if OLENameSpace='' then OLENameSpace:='MAPI';
{  Application.MessageBox(
              'Outlook 2000 Toolbox'+#10#13+
              '________________________________________________'+#10#13#13+
              '(c) 1999 by perr-Kommunikation & EDV'+#10#13#13+
              'by Lothar Perr'+#10#13+
              'Landstrasse 84'+#10#13+
              '4020 Linz'+#10#13+
              'email: lothar.perr@gmx.net'+#10#13+
              'Version 1.1'+#10#13,
              'OutlookConnect version 1.1', MB_OK+ MB_ICONINFORMATION);}
end;

destructor TOutlookConnect.Destroy;
begin
  ConnectOutlook(False);
  inherited Destroy;
end;

{ TAboutProperty }

procedure TAboutProperty.Edit;
begin
  Application.MessageBox(
              'Outlook 2000 Toolbox'+#10#13+
              '________________________________________________'+#10#13#13+
              '(c) 1999 by perr-Kommunikation & EDV'+#10#13#13+
              'by Lothar Perr'+#10#13+
              'Landstrasse 84'+#10#13+
              '4020 Linz'+#10#13+
              'email: lothar.perr@gmx.net'+#10#13+
              'Version 1.1'+#10#13,
              'OutlookConnect version 1.1', MB_OK+ MB_ICONINFORMATION);
end;

function TAboutProperty.GetAttributes: TPropertyAttributes;
begin
  GetAttributes:=[paDialog, paReadOnly];
end;

function TAboutProperty.GetValue: string;
begin
  GetValue:='(About)';
end;


procedure Register;
begin
  RegisterComponents('Outlook', [TOutlookConnect]);  // na ja, e schon wissen...
  RegisterPropertyEditor(TypeInfo(TAboutProperty), TOutlookConnect, 'ABOUT', TAboutProperty);
end;

end.

{Function TOutLookConnect.CreateToolBar(Name:String; Position:Byte; Visible:Boolean):Variant;
var
  customBar:Variant;
begin
  if not Active then
    raise Exception.Create('No connection to outlook');
  CustomBar := MyOlApp.ActiveExplorer.CommandBars.Add(Name);
  CustomBar.Position := Position;
  CustomBar.visible:= Visible;
  result:=CustomBar.Index;
end;

function TOutlookConnect.CommandBar(Index:Variant) : Variant;
begin
  if not Active then
    raise Exception.Create('No connection to outlook');
  Result:=MyOlApp.ActiveExplorer.CommandBars(Index);
end;

function TOutLookConnect.CommandBarCount:Word;
begin
  if not Active then
    raise Exception.Create('No connection to outlook');
  Result:=myNameSpace.GetDefaultFolder(olFolderContacts).Items.Count;
end;

}


There is a demo:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  Grids, ExtCtrls, OutLookTools, StdCtrls, Menus, Buttons;

type
  TForm1 = class(TForm)
    Panel2: TPanel;
    StringGrid1: TStringGrid;
    Panel1: TPanel;
    Label1: TLabel;
    Panel3: TPanel;
    ShowContact: TButton;
    Button1: TButton;
    CheckBox1: TCheckBox;
    Button2: TButton;
    Button3: TButton;
    Button4: TButton;
    Edit1: TEdit;
    Button5: TButton;
    Button6: TButton;
    Button7: TButton;
    Button8: TButton;
    Button9: TButton;
    OutlookConnect1: TOutlookConnect;
    procedure FormCreate(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure StringGrid1DblClick(Sender: TObject);
    procedure ShowContactClick(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure CheckBox1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure Button4Click(Sender: TObject);
    procedure Button5Click(Sender: TObject);
    procedure Button6Click(Sender: TObject);
    procedure Button7Click(Sender: TObject);
    procedure Button8Click(Sender: TObject);
    procedure Button9Click(Sender: TObject);
  private
    { Private-Deklarationen }
    procedure Fill;
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.Fill;
var
  Counter,Counter2:Word;
  MyContact:TContact;
begin
  StringGrid1.RowCount:=OutlookConnect1.ContactCount+1;
  for Counter:=1 to OutlookConnect1.ContactCount do
  begin
    MyContact:=OutLookConnect1.Contact(Counter);
    with StringGrid1 do
    begin
      Cells[0,Counter]:=IntToStr(Counter);
      Cells[1,Counter]:=MyContact.FirstName;
      Cells[2,Counter]:=MyContact.LastName;
      Cells[3,Counter]:=MyContact.FullName;
      Cells[4,Counter]:=MyContact.MobileTelephonenumber;
      Cells[5,Counter]:=MyContact.EntryId;
    end;
    Application.ProcessMessages;
  end;
  Label1.Caption:=IntToStr(OutlookConnect1.ContactCount)+' Kontakte gefunden';
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  with StringGrid1 do
  begin
    Cells[0,0]:='Index';
    Cells[1,0]:='FirstName';
    Cells[2,0]:='LastName';
    Cells[3,0]:='FullName';
    Cells[4,0]:='MobileTelephonenumber';
    Cells[5,0]:='EntryID';
  end;
  CheckBox1.Checked:=OutlookConnect1.Connected;
end;

procedure TForm1.FormShow(Sender: TObject);
begin
  if OutlookConnect1.Connected then Fill;
end;

procedure TForm1.StringGrid1DblClick(Sender: TObject);
begin
  ShowContactClick(Self);
end;

procedure TForm1.ShowContactClick(Sender: TObject);
begin
  OutlookConnect1.ShowContact(StringGrid1.Row);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  OutlookConnect1.Calendar.Display;
end;

procedure TForm1.CheckBox1Click(Sender: TObject);
begin
  OutlookConnect1.Connected:=CheckBox1.Checked;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  Fill;
end;

procedure TForm1.Button3Click(Sender: TObject);
var
  Counter:Word;
begin
  ShowMessage(IntToStr(OutLookConnect1.GetSelectionCount)+' Selectedd Items');
  try
    ShowMessage(OutLookConnect1.GetSelection(1));
  except
  end;
end;

procedure TForm1.Button4Click(Sender: TObject);
var
  MyContact:TContact;
begin
  MyContact:=OutlookConnect1.CreateContact;
  MyContact.FirstName:=Edit1.Text;
  MyContact.Save;
end;

procedure TForm1.Button5Click(Sender: TObject);
begin
  OutlookConnect1.FindContact(Edit1.Text).Display;
end;


procedure TForm1.Button6Click(Sender: TObject);
begin
  OutLookConnect1.GetSelection(1).Delete;
end;

procedure TForm1.Button7Click(Sender: TObject);
begin
  OutlookConnect1.Notes.Display;
end;

procedure TForm1.Button8Click(Sender: TObject);
begin
  OutlookConnect1.Notes.Items(1).Display;
end;

procedure TForm1.Button9Click(Sender: TObject);
var
  MyMail:Variant;
begin
  MyMail:=OutlookConnect1.Outbox.Items.Add;
  MyMail.Display;
  MyMail.To:='lothar.perr@gmx.net';
  MyMail.Save;
end;

end.


_______________________

For Outlook Express the tools are coming from Walther Estergaard - walther_e@yahoo.com

{
======================================================
This component can be freely used and/or distributed
in any product, commercial or not, giving to me the
credit for the same.

I am not and I won't be made responsible for the use
of this unit to carry out illegal or incorrect acts.

Walther Estergaard
walther_e@yahoo.com
======================================================
}


unit uoexpress;

interface

uses
  Windows, SysUtils, Registry, classes, dialogs;

type

  THeaderData = record
    position: longint;
    DataLength: longint;
    HeaderLength: WORD;
    FlagCount: WORD;
  end;

  Poe5_FolderInfo = ^Toe5_FolderInfo;
  Toe5_FolderInfo = record
    FolderID: longint;
    ParentFolderID: longint;
    FolderName: string;
    FolderFile: string;
  end;

  Poe5_MessageInfo = ^Toe5_MessageInfo;
  Toe5_MessageInfo = record
    Msg: longint;
    MsgFlags: longint;
    AccountID: string;
    Account: string;
    MessageID: string;
    Sent: TFileTime;
    Received: TFileTime;
    Subject: string;
    From: string;
    From_Reply: string;
    Receipt: string;
    Reply_To: string;
    References: string;
    NewsGroup: string;
    size: longint;
    position: longint;
    HeaderPosition: longint;
  end;

  Toe5_FileHeader = record
    IndexItemsCount: integer;
    Account: string;
    ServerID: string;
  end;

  Toe5_IndexHeader = record
    FilePos: longint;
    Unknown1: longint;
    PrevIndex: longint;
    NextIndex: longint;
    Count: longint;
    Unknown: longint;
  end;

  Poe5_IndexItem = ^Toe5_IndexItem;
  Toe5_IndexItem = record
    HeaderPos: longint;
    ChildIndex: longint;
    Unknown: longint;
  end;

  Toe5_MsgItem = record
    FilePos: longint;
    Unknown: longint;
    ItemSize: longint;
    NextItem: longint;
    MsgContent: array[0..511] of Char;
  end;


function Get_OE_Directory: string;
procedure Read_OE_File(const FileName: string; var flist: TList);
procedure Read_OE_Message(Filename:string; position: longint; var msg: widestring);
function FiletimeToDatetime(const date: TFileTime): TDateTime;
procedure ReadHeaderInfo(Filename:string; position: longint; var str: widestring);

procedure Read_OE_Message2(Filename:string; position: longint; var stream: TStringStream);

var
  oeFile: TFileStream;
  finfo: Toe5_FileHeader;
  tmpList: TList;

const
  DOWNLOADED = $1;
  MARKED = $20;
  READED = $80;
  DOWNLOAD_LATER = $100;
  NEWS_MSG = $800;  // to verify
  ATTACHMENTS = $4000;
  REPLY = $80000;
  INSPECT_CONVERSATION = $400000;
  IGNORE_CONVERSATION = $800000;
  // IMPORTED = ?

implementation

function FiletimeToDatetime(const date: TFileTime): TDateTime;
var
  st: TSystemTime;
  ft: TFileTime;
begin
  FileTimeToLocalFileTime(date, ft);
  FileTimeToSystemTime(ft, st);
  Result:=SystemTimeToDateTime(st);
end;

function Get_OE_Directory: string;
var
  Reg: TRegistry;
  tmpstr: string;
  userID: string;
begin
  tmpstr:='';
  Reg := TRegistry.Create;
  try
    Reg.RootKey := HKEY_CURRENT_USER;
    if Reg.OpenKey('\Software\Microsoft\Outlook Express', False) then
      tmpstr := Reg.ReadString('Store Root');
    if tmpstr = '' then begin
      if Reg.OpenKey('\Identities', False) then begin
        userID := Reg.ReadString('Default User ID');
        if Reg.OpenKey('\Identities\'+userID+'\Software\Microsoft\Outlook Express\5.0', False) then
          tmpstr := Reg.ReadString('Store Root');
      end;
    end;
  finally
    // added to get the right directory in WinNT and W2000
      

    if (tmpstr <> '') then begin
      SetLength(userID, 1024);
      if (ExpandEnvironmentStrings(pointer(tmpstr), @userID[1], 1024) > 0) then
        result := pchar(userID)
      else
        result := tmpstr;
    end; { if (tmpstr) }
    Reg.CloseKey;
    Reg.Free;
  end; { try }
end;


procedure GetFileInfo;
var
  hcount: integer;
begin
  if oeFile <> nil then begin
    with oeFile do begin
      Seek($C4, soFromBeginning);
      Read(hcount, sizeof(hcount));
      finfo.IndexItemsCount:=hcount;
      //Seek( $24C1, soFromBeginning);
      //read finfo.FServerID;
      //Seek( $25C1, soFromBeginning);
      //Read finfo.FAccount;
    end;
  end;
end;

procedure ReadFolderInfo(position: longint; var flist: TList);
var
  i: integer;
  oe5_FolderInfo: Poe5_FolderInfo;
  HeaderData: THeaderData;
  isFolder: boolean;
  Flags: integer;
  DataSize: integer;
  Size: integer;
  FlagsBuffer: pointer;
  pFlagsBuffer: ^DWORD;
  DataBuffer: pointer;
  pDataBuffer: ^Byte;
begin
  oeFile.Seek(position, soFromBeginning);
  oeFile.Read(HeaderData , sizeof(HeaderData));
  if position = HeaderData.position then begin
    New(oe5_FolderInfo);
    Flags:=HeaderData.FlagCount and $FF;
    DataSize:=HeaderData.DataLength-(Flags * sizeof(DWORD));
    Size:=sizeof(DWORD)*Flags;
    GetMem(FlagsBuffer, Size);
    oeFile.Read(FlagsBuffer^, Size);
    pFlagsBuffer:=FlagsBuffer;
    isFolder:=False;
    for i:= 0 to Flags-1 do begin
      isFolder:=((pFlagsBuffer^ and $FF) = $3) or ((pFlagsBuffer^ and $FF) = $6);
      if isFolder then break;
      inc(pFlagsBuffer);
    end;
    if isFolder then begin
      Size:=sizeof(Byte)*DataSize;
      GetMem(DataBuffer, Size);
      oeFile.Read(DataBuffer ^, Size);
      pFlagsBuffer:=FlagsBuffer;
      for i:= 0 to Flags-1 do begin
        pDataBuffer:=DataBuffer;
        case (pFlagsBuffer^ and $FF) of
          $2: begin
                inc(pDataBuffer, pFlagsBuffer^ shr 8);
                oe5_FolderInfo^.FolderName:=StrPas(PChar(pDataBuffer));
              end;
          $3: begin
                inc(pDataBuffer, pFlagsBuffer^ shr 8);
                oe5_FolderInfo^.FolderFile:=StrPas(PChar(pDataBuffer));
              end;
          $80: begin
                oe5_FolderInfo^.FolderID:= pFlagsBuffer^ shr 8;
              end;
          $81: begin
                oe5_FolderInfo^.ParentFolderID:= pFlagsBuffer^ shr 8;
              end;
        end;
        inc(pFlagsBuffer);
      end;
      FreeMem(DataBuffer);
      flist.Add(oe5_FolderInfo);
    end else
      Dispose(oe5_FolderInfo);
    FreeMem(FlagsBuffer);
  end;
end;
//==============================================================================

procedure ReadHeaderInfo(Filename:string; position: longint; var str: widestring);
const
  CR = #10;
var
  i: integer;
  HeaderData : THeaderData;
  Flags: integer;
  DataSize: integer;
  Size: integer;
  FlagsBuffer: pointer;
  pFlagsBuffer: ^DWORD;
  DataBuffer: pointer;
  pDataBuffer: ^Byte;
  line1, line2: string;
  x: byte;
  count: integer;
begin
  oeFile:=TFileStream.Create(IncludeTrailingBackslash(Get_OE_Directory)+
      FileName, fmOpenRead);
  oeFile.Seek(position, soFromBeginning);
  oeFile.Read(HeaderData , sizeof(HeaderData));
  if position = HeaderData.position then begin
    Flags:=HeaderData.FlagCount and $FF;
    DataSize:=HeaderData.DataLength-(Flags * sizeof(DWORD));
    Size:=sizeof(DWORD)*Flags;
    GetMem(FlagsBuffer, Size);
    oeFile.Read(FlagsBuffer^, Size);
    Size:=sizeof(Byte)*DataSize;
    GetMem(DataBuffer, Size);
    oeFile.Read(DataBuffer ^, Size);
    pFlagsBuffer:=FlagsBuffer;

    str:=str+'Flags = '+ IntToStr(Flags)+CR;
    str:=Str+CR;
    for i:= 0 to Flags-1 do begin
      str:=str+Format('%2.2x', [pFlagsBuffer^ and $FF])+': '+
         Format('%8.8x', [pFlagsBuffer^ shr 8]);
      case (pFlagsBuffer^ and $FF) of
      $1: str:=str+' ==> Offset to Message Flags';
        $2: str:=str+' ==> Sent Date';
        $3: str:=str+' ==> Filename';
        $5: str:=str+' ==> Subject';
        $4: str:=str+' ==> Message position';
        $6: ;
        $7: str:=str+' ==> Message-ID';
        $8: str:=str+' ==> Subject';
        $9: str:=str+' ==> From:';
        $A: str:=str+' ==> References';
        $B: str:=str+' ==> Newsgroup';
        $D: str:=str+' ==> From:';
        $E: str:=str+' ==> Reply-To:';
        $12: str:=str+' ==> Received Date';
        $13: str:=str+' ==> To:';
        $14: ;
        $1A: str:=str+' ==> Account';
        $1B: str:=str+' ==> Account-ID';
        $80: str:=str+' ==> Message Number';
        $81: str:=str+' ==> Message Status';
        $84: str:=str+' ==> Message Position';
        $91: str:=str+' ==> Message size';
      end;
      str:=str+CR;
      inc(pFlagsBuffer);
    end;
    str:=str+CR+'Size of Header = '+IntToStr(Size)+CR;
    line1:='';
    line2:='';
    count:=0;
    pDataBuffer:=DataBuffer;
    for i := 0 to Size-1 do begin
      x:=pDataBuffer^;
      line1:=line1+Format('%2.2x', [x])+' ';
      if x=0 then
        line2:=line2+'   '
      else
        line2:=line2+' '+chr(x)+' ';
      inc(count);
      if count = 16 then begin
        str:=str+line1+cr+line2+cr+cr;
        line1:='';
        line2:='';
        count:=0;
      end;
      inc(pDataBuffer);
    end;

    FreeMem(DataBuffer);

    FreeMem(FlagsBuffer);
  end;
  oeFile.Free;
end;

//==============================================================================

procedure ReadMessageInfo(position: longint; var flist: TList);
var
  i: integer;
  oe5_MessageInfo: Poe5_MessageInfo;
  HeaderData : THeaderData;
  Flags: integer;
  DataSize: integer;
  Size: integer;
  FlagsBuffer: pointer;
  pFlagsBuffer: ^DWORD;
  DataBuffer: pointer;
  pDataBuffer: ^Byte;
begin
  oeFile.Seek(position, soFromBeginning);
  oeFile.Read(HeaderData , sizeof(HeaderData));
  if position = HeaderData.position then begin
    New(oe5_MessageInfo);
    ZeroMemory(oe5_MessageInfo, sizeof(Toe5_MessageInfo));
    oe5_MessageInfo.HeaderPosition:=position;
    Flags:=HeaderData.FlagCount and $FF;
    DataSize:=HeaderData.DataLength-(Flags * sizeof(DWORD));
    Size:=sizeof(DWORD)*Flags;
    GetMem(FlagsBuffer, Size);
    oeFile.Read(FlagsBuffer^, Size);

    Size:=sizeof(Byte)*DataSize;
    GetMem(DataBuffer, Size);
    oeFile.Read(DataBuffer ^, Size);
    pFlagsBuffer:=FlagsBuffer;

    for i:= 0 to Flags-1 do begin
      pDataBuffer:=DataBuffer;
      case (pFlagsBuffer^ and $FF) of
        $1: begin  // Offset to Message Flags
              inc(pDataBuffer, pFlagsBuffer^ shr 8);
              oe5_MessageInfo^.MsgFlags:=pDataBuffer^;
              inc(pDataBuffer);
              oe5_MessageInfo^.MsgFlags:=oe5_MessageInfo^.MsgFlags+((pDataBuffer^)*256);
              inc(pDataBuffer);
              oe5_MessageInfo^.MsgFlags:=oe5_MessageInfo^.MsgFlags+((pDataBuffer^)*65536);
            end;
        $2: begin  // Sent
              inc(pDataBuffer, pFlagsBuffer^ shr 8);
              oe5_MessageInfo^.Sent:=PFileTime(pDataBuffer)^;
            end;
        $4: begin  // message position
              inc(pDataBuffer, pFlagsBuffer^ shr 8);
              oe5_MessageInfo^.position:=PDWORD(pDataBuffer)^;
            end;
        $12: begin  // Received
              inc(pDataBuffer, pFlagsBuffer^ shr 8);
              oe5_MessageInfo^.Received:=PFileTime(pDataBuffer)^;
            end;
        $7: begin  // message-ID
              inc(pDataBuffer, pFlagsBuffer^ shr 8);
              oe5_MessageInfo^.MessageID:=StrPas(PChar(pDataBuffer));
            end;
        $8: begin  // Subject
              inc(pDataBuffer, pFlagsBuffer^ shr 8);
              oe5_MessageInfo^.Subject:=StrPas(PChar(pDataBuffer));
            end;
        $9: begin  // From_reply
              inc(pDataBuffer, pFlagsBuffer^ shr 8);
              oe5_MessageInfo^.From_Reply:=StrPas(PChar(pDataBuffer));
            end;
        $A: begin  // References
              inc(pDataBuffer, pFlagsBuffer^ shr 8);
              oe5_MessageInfo^.References:=StrPas(PChar(pDataBuffer));
            end;
        $B: begin  // Newsgroup
              inc(pDataBuffer, pFlagsBuffer^ shr 8);
              oe5_MessageInfo^.NewsGroup:=StrPas(PChar(pDataBuffer));
            end;
        $D: begin  // From
              inc(pDataBuffer, pFlagsBuffer^ shr 8);
              oe5_MessageInfo^.From:=StrPas(PChar(pDataBuffer));
            end;
        $E: begin  // Reply to
              inc(pDataBuffer, pFlagsBuffer^ shr 8);
              oe5_MessageInfo^.Reply_To:=StrPas(PChar(pDataBuffer));
            end;
        $13: begin  // Receipt (to:)
              inc(pDataBuffer, pFlagsBuffer^ shr 8);
              oe5_MessageInfo^.Receipt:=StrPas(PChar(pDataBuffer));
            end;
        $1A: begin  // Account
              inc(pDataBuffer, pFlagsBuffer^ shr 8);
              oe5_MessageInfo^.Account:=StrPas(PChar(pDataBuffer));
            end;
        $1B: begin  // Account ID
              inc(pDataBuffer, pFlagsBuffer^ shr 8);
              oe5_MessageInfo^.AccountID:=StrPas(PChar(pDataBuffer));
            end;
        $80: oe5_MessageInfo^.Msg:= pFlagsBuffer^ shr 8; //Message number

        $81: oe5_MessageInfo^.MsgFlags:= pFlagsBuffer^ shr 8;  // Message Flags

        $84: oe5_MessageInfo^.position:= pFlagsBuffer^ shr 8;  // Position

        $91: oe5_MessageInfo^.size:= pFlagsBuffer^ shr 8;  // SIZE??

      end;
      inc(pFlagsBuffer);
    end;

    FreeMem(DataBuffer);
    flist.Add(oe5_MessageInfo);

    FreeMem(FlagsBuffer);
  end;
end;

procedure ReadIndex(position: longint; var flist: TList; folders: boolean);
var
  iheader: Toe5_IndexHeader;
  icount: integer;
  pIndexItem: Poe5_IndexItem;
  i: integer;
  Size: Integer;
  Buffer: PChar;
begin
  if oeFile <> nil then begin
    oeFile.Seek(position, soFromBeginning);
    oeFile.Read(iheader, sizeof(iheader));
    if iheader.FilePos <> position then begin
      // break;
    end else begin
      tmpList.Add(Pointer(position));

      if iheader.NextIndex > 0 then
        if tmpList.IndexOf(Pointer(iheader.NextIndex))=-1 then
          ReadIndex(iheader.NextIndex, flist, folders);

      if iheader.PrevIndex > 0 then
        if tmpList.IndexOf(Pointer(iheader.PrevIndex))=-1 then
          ReadIndex(iheader.PrevIndex, flist, folders);

      icount:=iheader.Count shr 8;
      if icount > 0 then begin
        Size:=sizeof(Toe5_IndexItem)*icount;
        GetMem(Buffer, Size);
        oeFile.Seek(iheader.FilePos + sizeof(iheader), soFromBeginning);
        oeFile.Read(Buffer^, Size);
        pIndexItem:=Poe5_IndexItem(buffer);
        for i:=0 to icount-1 do begin
          if pIndexItem^.HeaderPos > 0 then begin
            if folders then
               // get folder information
               ReadFolderInfo(pIndexItem^.HeaderPos, flist)
            else
               // get message information
               ReadMessageInfo(pIndexItem^.HeaderPos, flist);
          end;
          if pIndexItem^.ChildIndex > 0 then
            if tmpList.IndexOf(Pointer(pIndexItem^.ChildIndex))=-1 then
              ReadIndex(pIndexItem^.ChildIndex, flist, folders);
          inc(pIndexItem);
        end;
        FreeMem(Buffer);
      end;
    end;
  end;
end;


//===================================================================================
procedure Read_OE_Message(Filename:string; position: longint; var msg: widestring);
var
  oe5_MsgItem: Toe5_MsgItem;
begin
  if position>0 then begin
    oeFile:=TFileStream.Create(IncludeTrailingBackslash(Get_OE_Directory)+
      FileName, fmOpenRead);
    GetFileInfo;
    msg:='';
    if finfo.IndexItemsCount > 0 then begin
      oeFile.Seek(position, soFromBeginning);
      While True do begin
        oeFile.Read(oe5_MsgItem, sizeof(oe5_MsgItem));
        if oe5_MsgItem.FilePos <> position then break;
        msg:=msg + copy(oe5_MsgItem.MsgContent, 1, oe5_MsgItem.ItemSize);
        position:=oe5_MsgItem.NextItem;
        if position = 0 then break;
        oeFile.Seek(position, soFromBeginning);
      end;
    end;
    oeFile.Free;
  end;
end;



procedure Read_OE_Message2(Filename:string; position: longint; var stream: TStringStream);
var
  oe5_MsgItem: Toe5_MsgItem;
  etime: cardinal;
begin
  if position>0 then begin
    oeFile:=TFileStream.Create(IncludeTrailingBackslash(Get_OE_Directory)+
      FileName, fmOpenRead);
    GetFileInfo;
    etime := GetTickCount;
    if finfo.IndexItemsCount > 0 then begin
      oeFile.Seek(position, soFromBeginning);
      While True do begin
        oeFile.Read(oe5_MsgItem, sizeof(oe5_MsgItem));
        if oe5_MsgItem.FilePos <> position then break;
        stream.Write(oe5_MsgItem.MsgContent, oe5_MsgItem.ItemSize);
        position:=oe5_MsgItem.NextItem;
        if position = 0 then break;
        oeFile.Seek(position, soFromBeginning);
      end;
    end;
    oeFile.Free;
    etime := GetTickCount - etime;
    //ShowMessage(IntToStr(etime));
  end;
end;

//===================================================================================

procedure Read_OE_File(const FileName: string; var flist: TList);
var
  position: longint;
begin
  if flist <> nil then begin
     oeFile:=TFileStream.Create(IncludeTrailingBackslash(Get_OE_Directory)+
       FileName, fmOpenRead);
     GetFileInfo;
     if finfo.IndexItemsCount > 0 then begin
       tmpList:=TList.Create;
       oeFile.Seek( $30, soFromBeginning);
       oeFile.Read(position, sizeof(position));
       ReadIndex(position, flist, FileName='folders.dbx');
       tmpList.Free;
     end;
     oeFile.Free;
  end;
end;

end.


The doc:

HOW TO READ OUTLOOK EXPRESS FILES (.DBX)



1)      How these files are organized

The message headers are stored in an orderly way using tables. The table is divided in two parts, the header and the entries. The position or offset from BOF of the first table is stored in offset $30 from BOF (also, the number of message headers is stored in offset $C4 form BOF).

The header contain the number of entries in the table, and the position of the next or previous table. I use this structure to read the table header:

Toe5_IndexHeader = record
    FilePos: longint;  {this is the offset of the structure from BOF, use for control}
    Unknown1: longint; { ??? }
    PrevIndex: longint;  {position or offset from BOF of previous table}
    NextIndex: longint;  {position or offset from BOF of next table}
    Count: longint; { number of entries in the table, decode or convert using count shr 8 }
    Unknown2: longint; { ??? }
end;

To get the real number of entries in the table once you read the header, need to convert the count value using < count shr 8 > .

Each entry contain the position or offset (always from BOF) of the Message Header, and  the position or offset of other index table (this table is used to maintain the message threading, that is, any message header pointed by this table is a child of this message). Here is the structure of an entry:

Toe5_IndexItem = record
    HeaderPos: longint;  {position or offset from BOF of the message header}
    ChildIndex: longint; {position or offset from BOF of a child index table}
    Unknown: longint;
end;

I’ve found that the best way (at least for me) to read the tables is using a recursive function, here is how they work:

ReadTable(position)
Begin
     Add position to a visited list
     Read Toe5_IndexHeader

     If  Toe5_IndexHeader.NextIndex <> 0 then
        If not Toe5_IndexHeader.NextIndex in visited list then
           ReadTable(Toe5_IndexHeader.NextIndex)

     If  Toe5_IndexHeader.PrevIndex <> 0 then
        If not Toe5_IndexHeader.PrevIndex in visited list then
           ReadTable(Toe5_IndexHeader.PrevIndex)
     
     For each entry of the table do
          Read Toe5_IndexItem
          Get MessageHeader pointed by Toe5_IndexItem.HeaderPos
          If Toe5_IndexItem.ChildIndex <>0 then
             If not Toe5_IndexItem.ChildIndex in visited list then
                ReadTable(Toe5_IndexItem.ChildIndex)  
End


Message Header:
The message header contain the relevant information of the message, outlook express use this to avoid access the message until this is necessary . This structure is divided in three parts, a) header of the structure, b) a table of DWORD, and c) a data block

a)      the header of the structure is what you read first, this is necessary to determine the size of the other two parts. In the header is stored the size of the structure ( the three parts), the size of the table and data part summed, and the number of elements in the table ( I call this elements FLAGS).

THeaderData = record
    position: longint;  {this is the offset of the structure from BOF, use for control}
    DataLength: longint; {size of the table and data}
    HeaderLength: WORD;  {size of the three parts}
    FlagCount: WORD;  {number of elements in the table}
end;

To get the size of the table use Flagcount * sizeof(DWORD), and to get the size of the data use DataLength – size of the table.

b)      each element (flag) in the table need to be decoded, to obtain the Id and the value of the flag: to get the Id use element and $FF, to get the value use element shr 8.

c)      The data block contain information like received date, sent date, subject,  receipt (to:), from:, references, account, etc.; to read these information use the flags, here is an example of the flags and the data block:

Flags = 16

80: 00000074
81: 00000081
02: 00000000
84: 0002ECA0
05: 00000008
06: 00000025
07: 0000002D
08: 0000006E
0D: 0000008B
0E: 000000A5
90: 00000003
91: 0000376F
12: 000000D4
13: 000000DC
14: 00000102
1C: 0000012A

Data Block:

00 72 F3 E4 58 22 C0 01 41 63 74 69 76 65 57 65
    r  ó  ä  X  "  À  _  A  c  t  i  v  e  W  e

62 20 44 65 76 65 6C 6F 70 65 72 20 65 58 54 52
 b     D  e  v  e  l  o  p  e  r     e  X  T  R

41 20 23 38 00 60 E2 F2 9C 90 35 C0 01 3C 4F 46
 A     #  8     `  â  ò  œ    5  À  _  <  O  F

45 31 44 36 35 46 38 37 2E 32 39 39 31 37 34 31
 E  1  D  6  5  F  8  7  .  2  9  9  1  7  4  1

42 2D 4F 4E 38 35 32 35 36 39 35 46 2E 30 30 35
 B  -  O  N  8  5  2  5  6  9  5  F  .  0  0  5

43 33 46 41 36 40 70 69 6E 6E 61 63 6C 65 70 75
 C  3  F  A  6  @  p  i  n  n  a  c  l  e  p  u

62 6C 69 73 68 69 6E 67 2E 63 6F 6D 3E 00 41 63
 b  l  i  s  h  i  n  g  .  c  o  m  >     A  c

74 69 76 65 57 65 62 20 44 65 76 65 6C 6F 70 65
 t  i  v  e  W  e  b     D  e  v  e  l  o  p  e

72 20 65 58 54 52 41 20 23 38 00 41 63 74 69 76
 r     e  X  T  R  A     #  8     A  c  t  i  v

65 57 65 62 20 44 65 76 65 6C 6F 70 65 72 20 65
 e  W  e  b     D  e  v  e  l  o  p  e  r     e

58 54 52 41 00 61 63 74 69 76 65 77 65 62 64 65
 X  T  R  A     a  c  t  i  v  e  w  e  b  d  e

76 65 6C 6F 70 65 72 65 78 74 72 61 40 70 69 6E
 v  e  l  o  p  e  r  e  x  t  r  a  @  p  i  n

6E 61 63 6C 65 70 75 62 6C 69 73 68 69 6E 67 2E
 n  a  c  l  e  p  u  b  l  i  s  h  i  n  g  .

63 6F 6D 00 00 23 5E 0F 8B 22 C0 01 41 63 74 69
 c  o  m        #  ^  _  ‹  "  À  _  A  c  t  i

76 65 57 65 62 20 44 65 76 65 6C 6F 70 65 72 20
 v  e  W  e  b     D  e  v  e  l  o  p  e  r    

65 58 54 52 41 20 53 75 62 73 63 72 69 62 65 72
 e  X  T  R  A     S  u  b  s  c  r  i  b  e  r

20 00 3C 41 63 74 69 76 65 57 65 62 20 44 65 76
       <  A  c  t  i  v  e  W  e  b     D  e  v

65 6C 6F 70 65 72 20 65 58 54 52 41 20 53 75 62
 e  l  o  p  e  r     e  X  T  R  A     S  u  b

73 63 72 69 62 65 72 20 3E 00 88 00 00 00 01 00
 s  c  r  i  b  e  r     >     ˆ           _    

02 4E 00 00 00 00 F9 37 00 00 02 4E 00 00 01 00
 _  N              ù  7        _  N        _    

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
                                               

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
                                               

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
                                               

00 00 00 00 00 00 40 05 00 00 F9 37 00 00 00 00
                   @  _        ù  7            

00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00
                   _                            

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
                                               

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
                                               

00 00 00 00 00 00 6D 70
                   m  p

Now let's see which signifies each flag:
For “Folders.dbx”:

$2 : the value is the offset from the begin of the data block to the name of the folder (null terminated string)

$3 : the value is the offset from the begin of the data block to the name of the dbx file that store this folder (null terminated string)

$6 : the value is not important, if a folder have this flag then is what I call special folders, these folders doesn’t have a correspondent file and are used for a matter of organization

$80 : the value is the Id of the folder

$81 : the value is the Id of the parent folder, (the flag $80 of the parent folder)

for the other files:

$1: the value is the offset from the begin of the data block to the status (or flags) of the message

$2: the value is the offset from the begin of the data block to the “Sent Date”, the date is
stored in a TFileTime type

$4: sometimes, a DWORD is not sufficient to store the flag and the position of the message in the file, then the position is stored in the data block, this id is the offset from the begin of the data block of the position of the message (DWORD)

$7: the value is the offset from the begin of the data block to the MessageID of the message (null terminated string)

$8: the value is the offset from the begin of the data block to the Subject of the message (null terminated string)

$9: the value is the offset from the begin of the data block to the “From Reply” of the message (null terminated string)

$A: the value is the offset from the begin of the data block to the References of the message (null terminated string)

$B: the value is the offset from the begin of the data block to the NewsGroup of the message (null terminated string)

$D: the value is the offset from the begin of the data block to the “From:” data (null terminated string)

$E: the value is the offset from the begin of the data block to the “Reply to:” data (null terminated string)

$12: the value is the offset from the begin of the data block to the “Received Date”, the date is stored in a TFileTime type


$13: the value is the offset from the begin of the data block to the “Receipt (To:)” data (null terminated string)

$1A: the value is the offset from the begin of the data block to the “Account” data (null terminated string)

$1B: the value is the offset from the begin of the data block to the “AccountID” data (null terminated string)

$80: The value is the Message number

$81: the value is used to store the status (or flags) of the message

$84: the value is the position of the message in the file

$91: the value is the size of the message

Messages:

Each mail or news message is stored in blocks of 512 bytes with a header, that is, the message is divided in several blocks, then a header is added to each block containing the next information: size of the data block, size of the used part of the data block, and position of the next block. I use the next structure to read the blocks including the header:

Toe5_MsgItem = record
    FilePos: longint;  {this is the offset of the structure from BOF, use for control}
    Unknown: longint;  {size of the data block, I think}
    ItemSize: longint;  {used part of the data block}
    NextItem: longint;  {position or offset of the next block from BOF, 0 if is the final block}
    MsgContent: array[0..511] of Char;  {data block that contain the message}
end;



2)      Deleted messages

When a message is deleted, first is added to the “Deleted elements” folder, and therefore to the correspondent dbx file for deleted elements; then the space used by the message is added to a list of free space, thus when a new message is added, outlook express use this space first. The position of the first element in the list is stored in the offset $48 of the dbx file.
Each element of this list is divided in two parts: a header and the block of free space. Here is the structure of the header:

Toe5_FreeSpace = record
    FilePos: longint;  {this is the offset of the structure from BOF, use for control}
    ElementSize: longint;  {size of the structure, header and free space}
    FreeSpaceSize: longint;  {size of the free space}
    PreviousElement: longint;  {this is the offset of the previous element from BOF }
    NextElement: longint;  {this is the offset of the next element from BOF }
end;


3)      Dates

All the dates in the message header are TFileTime type, and based on the Coordinated Universal Time (UTC), you need to convert to your local time.
Here is an example how to do this:

function FiletimeToDatetime(const date: TFileTime): TDateTime;
var
  st: TSystemTime;
  localft: TFileTime;
begin
  FileTimeToLocalFileTime(date, localft);
  FileTimeToSystemTime(localft, st);
  Result:=SystemTimeToDateTime(st);
end;


4)      Get the status of a message

To get the status of a message use the value of the flag $81, here is how to do:

x := flag $81 value
If (x AND constant) <> 0 then …..


and here are some constants:
DOWNLOADED = $1
MARKED = $20   (small flag icon)
READED = $80
DOWNLOAD_LATER = $100
NEWS_MSG = $800      ç need to verify this
ATTACHMENTS = $4000
REPLY = $80000
INSPECT_CONVERSATION = $400000  (small glasses icon)
IGNORE_CONVERSATION = $800000


Finally, sorry for my English, and please, send modifications, suggestions, comments,
critics, etc to walther_e@yahoo.com


Walther Estergaard
walther_e@yahoo.com

The demo:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ComCtrls, ExtCtrls, Menus, ToolWin;

type
  TForm1 = class(TForm)
    Splitter1: TSplitter;
    Panel1: TPanel;
    ListView1: TListView;
    RichEdit1: TRichEdit;
    ListBox1: TListBox;
    Splitter2: TSplitter;
    PopupMenu1: TPopupMenu;
    SaveDialog1: TSaveDialog;
    Savetofile1: TMenuItem;
    ToolBar1: TToolBar;
    ToolButton1: TToolButton;
    ToolButton2: TToolButton;
    HeaderInfo1: TMenuItem;
    MessageAtributes1: TMenuItem;
    ToolButton3: TToolButton;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure ListBox1Click(Sender: TObject);
    procedure ListView1Click(Sender: TObject);
    procedure Savetofile1Click(Sender: TObject);
    procedure PopupMenu1Popup(Sender: TObject);
    procedure HeaderInfo1Click(Sender: TObject);
    procedure MessageAtributes1Click(Sender: TObject);
  private
    { Private declarations }
    dbxfile: String;
    FolderList: TList;
    MessageList: TList;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

uses uoexpress;

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
var
  x:integer;
  pfolder: Poe5_FolderInfo;
begin
  dbxfile:='folders.dbx';
  FolderList:=TList.Create;
  MessageList:=TList.Create;
  Read_OE_File(dbxfile, FolderList);
  ListBox1.Items.Clear;
  if FolderList.Count > 0 then
    for x:=0 to FolderList.Count-1 do begin
      pfolder:=FolderList.Items[x];
      ListBox1.Items.Add(pfolder^.FolderName);
    end;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  MessageList.Free;
  FolderList.Free;
end;

procedure TForm1.ListBox1Click(Sender: TObject);
var
  pfolder: Poe5_FolderInfo;
  x: integer;
  pmessage: Poe5_MessageInfo;
  ListItem: TListItem;
begin
  pfolder:=FolderList.Items[ListBox1.ItemIndex];
  dbxfile:=pfolder^.FolderFile;
  MessageList.Clear;
  RichEdit1.Clear;
  ListView1.Items.Clear;

  if dbxfile<>'' then
    Read_OE_File(dbxfile, MessageList);
  if MessageList.Count>0 then begin
    for x:=0 to MessageList.Count-1 do begin
      pmessage:=MessageList.Items[x];
      ListItem:= ListView1.Items.Add;
      ListItem.Caption:= pmessage^.From;
      ListItem.SubItems.Add(pmessage^.Reply_To);
      ListItem.SubItems.Add(pmessage^.Receipt);
      ListItem.SubItems.Add(pmessage^.Subject);
      ListItem.SubItems.Add(DateTimeToStr(FiletimeToDatetime(pmessage^.Received)));
      ListItem.SubItems.Add(IntToStr(pmessage^.Position));
      ListItem.SubItems.Add(IntToStr(pmessage^.Size));
    end;
  end;

end;

procedure TForm1.ListView1Click(Sender: TObject);
var
  buffer: pointer;
  x: longint;
  sstream: TStringStream;
begin
  Screen.Cursor:=crHourGlass;
  RichEdit1.Clear;
  sstream:=TStringStream.Create('');
  x:=Poe5_MessageInfo(MessageList.Items[ListView1.Selected.Index])^.position;
  Read_OE_Message2(dbxfile, x, sstream);
  sstream.Position := 0;
  RichEdit1.Lines.BeginUpdate;
  RichEdit1.Lines.LoadFromStream(sstream);
  RichEdit1.Lines.EndUpdate;
  sstream.Free;
  ToolButton1.Enabled:=(trim(RichEdit1.Text)<>'');
  Screen.Cursor:=crDefault;
end;

procedure TForm1.Savetofile1Click(Sender: TObject);
begin
  if ListView1.Items.Count > 0 then begin
    ListView1Click(Self);
    if Trim(RichEdit1.Text)<>'' then begin
      SaveDialog1.InitialDir:=ExtractFilePath(Application.ExeName);
      if SaveDialog1.Execute then
        RichEdit1.Lines.SaveToFile(SaveDialog1.FileName);
    end;
  end;
end;

procedure TForm1.PopupMenu1Popup(Sender: TObject);
begin
  Savetofile1.Enabled := RichEdit1.Lines.Count > 0;
end;

procedure TForm1.HeaderInfo1Click(Sender: TObject);
var
  str: WideString;
  x: longint;
begin
  RichEdit1.Clear;
  x:=Poe5_MessageInfo(MessageList.Items[ListView1.Selected.Index])^.HeaderPosition;
  str:= 'Current header position is: '+IntToStr(x)+#10+#10;
  ReadHeaderInfo(dbxfile, x, str);
  RichEdit1.Lines.Add(str);
end;

procedure TForm1.MessageAtributes1Click(Sender: TObject);
var
  str: WideString;
  x: longint;
begin
  RichEdit1.Clear;
  str:='';
  x:=Poe5_MessageInfo(MessageList.Items[ListView1.Selected.Index])^.MsgFlags;
  if (x and NEWS_MSG) <> 0 then str:=str+'NEWS MESSAGE'+#10;
  if (x and DOWNLOAD_LATER) <> 0 then str:=str+'DOWNLOAD LATER'+#10;
  if (x and DOWNLOADED) <> 0 then str:=str+'DOWNLOADED'+#10;
  if (x and READED) <> 0 then str:=str+'READED'+#10;
  if (x and MARKED) <> 0 then str:=str+'MARKED'+#10;
  if (x and ATTACHMENTS) <> 0 then str:=str+'ATTACHMENTS'+#10;
  if (x and REPLY) <> 0 then str:=str+'REPLY'+#10;
  if (x and INSPECT_CONVERSATION) <> 0 then str:=str+'INSPECT CONVERSATION';
  if (x and IGNORE_CONVERSATION) <> 0 then str:=str+'IGNORE CONVERSATION';
  //if (x and IMPORTED) <> 0 then str:=str+'IMPORTED'+#10;
  RichEdit1.Lines.Add(str);
end;

end.


__________________________________________

Hope this can help. Please credit the writers of the code if you use it and contact them if you need more info regarding licensing or more informations (I don't know if emails are still valid).


0

Featured Post

Better Security Awareness With Threat Intelligence

See how one of the leading financial services organizations uses Recorded Future as part of a holistic threat intelligence program to promote security awareness and proactively and efficiently identify threats.

Join & Write a Comment

Creating an auto free TStringList The TStringList is a basic and frequently used object in Delphi. On many occasions, you may want to create a temporary list, process some items in the list and be done with the list. In such cases, you have to…
Introduction I have seen many questions in this Delphi topic area where queries in threads are needed or suggested. I know bumped into a similar need. This article will address some of the concepts when dealing with a multithreaded delphi database…
Excel styles will make formatting consistent and let you apply and change formatting faster. In this tutorial, you'll learn how to use Excel's built-in styles, how to modify styles, and how to create your own. You'll also learn how to use your custo…
In this seventh video of the Xpdf series, we discuss and demonstrate the PDFfonts utility, which lists all the fonts used in a PDF file. It does this via a command line interface, making it suitable for use in programs, scripts, batch files — any pl…

747 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

15 Experts available now in Live!

Get 1:1 Help Now