You have mail - OEAPI and COM
Written by Mike James   
Monday, 14 September 2009
Article Index
You have mail - OEAPI and COM
Wrapping the Interface
YHM application

Working with Outlook Express or Windows Mail is an interesting exercise in COM interop and even if you aren't interested in implementing a "You Have Mail" function finding out how to work with the OE folder store is well worth while.

The YHM application is designed to look inside the OE inbox folder and pop-up a message if a new email arrives with a particular set of characteristics. It isn’t a 100% complete project because I leave open how you want to specify the email you are waiting for and exactly what you want to do if it arrives. The basic mechanism for scanning Outlook Express mailbox is fully described and you can add to it your own code to, say, send an SMS, ring a bell or create any multimedia extravaganza you care to create to signal the email’s arrival.

Notice that the project works with Outlook Express – not full Outlook - and has been tested on Windows XP, although there is no reason why it shouldn't work under Vista or Windows 7 with Windows Mail (the new name for Outlook Express). It doesn’t work under Windows XP 64-bit because of problems with COM interop but these might also be fixable.

The story so far

This project is based on the use of the Outlook Express API or OEAPI. This is an old-fashioned COM interface based API that doesn’t provide any easy to use features.

You can find documentation by searching the Microsoft website for “Windows Mail API” or for “IStoreNamespace”. In Hacking Outlook - COM Interop we look at how this API can be converted into a C# object that could be used from any .NET language. That project only converts the IStoreNamespace interface into an object because this is sufficient to inspect the message store and discover what sorts of messages were stored in each folder. The current project needs to use not only IStoreNamespace but IStoreFolder.

Details of converting an Interface to a class are covered in Hacking Outlook, however, there are some differences that are worth explaining. The biggest difference is the IStoreFolder interface is created by one of the functions within the IStoreNamespace interface – this complication is surprisingly easy to deal with but some entirely standard techniques proved far from straightforward.

The additional interface, IStoreFolder is created within the same class file that was used to create IStoreNamespace and for this you need the file that contains the OE class from the previous project – it’s in the CodeBin along with the code for this project.

Most of the detailed information about the API and the way that everything works can only be discovered by reading the C++ header file msoeapi.h which is installed in the “Includes” directory of the Windows SDK. As this is a header file intended for C or C++ programmers it isn’t of direct use, but by reading it you can find out exactly how the interfaces have been defined. The problem is, of course, to interpret a header file you have to know C/C++ but it is very similar to C# and you can look up anything you are unsure of.

Getting started

The first step is to enter the start of the IStoreFolder Interface definition. It doesn’t have to be complete at this stage, just enter the functions that the Interface defines listed in the order that they occur:

[Guid(
"E70C92AC-4BFD-11d1-8A95-00C04FB951F3"),
InterfaceType(
ComInterfaceType.InterfaceIsIUnknown)]
public interface IStoreFolder
{
void GetFolderProps();
void GetMessageProps();
void FreeMessageProps();
void DeleteMessages();
void SetLanguage();
void MarkMessagesAsRead();
void SetFlags();
void OpenMessage();
void SaveMessage();
void BatchLock();
void BatchFlush();
void BatchUnlock();
void CreateStream();
void CommitStream();
void RegisterNotification();
void UnregisterNotification();
void Compact();
void GetFirstMessage();
void GetNextMessage();
void GetMessageClose();
}

You can’t actually use any of the functions of the interface at this stage because you have to add the details of their parameters and how these are to be converted, or “marshalled” in C# jargon, to and from .NET data types.

However, you don’t have to complete the definitions of all of the functions in the interface just the ones you actually want to use. In this case we need just five. The first is:

void GetFolderProps();

which gets the details of the folder, how many emails it contains for example. The next two do more or less the same job:

void GetFirstMessage();
void GetNextMessage();

which get all of the details of a message, including who sent it and when. Finally we need two “cleanup” utilities which have to be called to complete an inspection of the messages in a folder:

void FreeMessageProps();
void GetMessageClose();

Converting the interface specifications to C# is fairly straightforward:

void GetFolderProps(
[In, MarshalAs(UnmanagedType.U4)]
Int32 dwReserved,
[Out, MarshalAs(UnmanagedType.Struct)]
out _FOLDERPROPS pProps);

[PreserveSig]
Int32 GetFirstMessage(
[In, MarshalAs(UnmanagedType.U4)]
Int32 dwFlags,
[In, MarshalAs(UnmanagedType.U4)]
Int32 dwMsgFlags,
[In, MarshalAs(UnmanagedType.U4)]
UInt32 dwMsgIdFirst,
[In, Out, MarshalAs(
UnmanagedType.Struct)]
ref _MESSAGEPROPS pProps,
[Out, MarshalAs(UnmanagedType.U4)]
out UInt32 phEnum
);

[PreserveSig]
Int32 GetNextMessage(
[In, MarshalAs(UnmanagedType.U4)]
UInt32 hEnum,
[In, MarshalAs(UnmanagedType.U4)]
Int32 dwFlags,
[In, Out, MarshalAs(
UnmanagedType.Struct)]
ref _MESSAGEPROPS pProps
);

void FreeMessageProps(
[In, Out, MarshalAs(
UnmanagedType.Struct)]
ref _MESSAGEPROPS pProps);
void GetMessageClose(
[In, MarshalAs(UnmanagedType.U4)]
UInt32 hEnum
);

The _FOLDERPROPS struct was defined as part of the interface defined in the previous project but we still need to define a _MESSAGEPROPS structure:

[StructLayout(LayoutKind.Sequential,
Pack = 1, CharSet = CharSet.Ansi)]
public struct _MESSAGEPROPS
{
public Int32 cbSize;
public Int32 dwReserved;
public Int32 dwMessageId;
public Int32 dwLanguage;
public Int32 dwState;
public Int32 cbMessage;
public Int32 priority;
public System.Runtime.InteropServices.
ComTypes.FILETIME ftReceived;
public System.Runtime.InteropServices.
ComTypes.FILETIME ftSent;
[MarshalAs(UnmanagedType.LPStr)]
public string pszSubject;
[MarshalAs(UnmanagedType.LPStr)]
public string pszDisplayTo;
[MarshalAs(UnmanagedType.LPStr)]
public string pszDisplayFrom;
[MarshalAs(UnmanagedType.LPStr)]
public string pszNormalSubject;
public Int32 dwFlags;
public Int32 pStmOffsetTable;
};

<ASIN:0735618755>

<ASIN:0596001037>

<ASIN:1590590112>



Last Updated ( Monday, 14 September 2009 )