Click here to Skip to main content
Email Password   helpLost your password?

Screenshot - MAPIdotnet1.jpg

Introduction

My introduction to MAPI was to make what I thought would be a simple application to sort through the Inbox and Sent items folder on my PocketPC phone. I also wanted to display messages that had a common sender and recipient, a similar concept to Gmail conversations, MSN conversations, etc. C# was the obvious language of choice, as it makes coding on the PocketPC (well, all platforms in general, really) a piece of cake. This article is the first in a series documenting several projects that complete what turned out to be a huge solution to make the application essentially replace the standard mail viewing program that comes with PocketPCs and Smartphones. This article provides and documents the .NET wrapper for MAPI, covering how and why its 99% .NET form came about.

Those of you who have been looking at using MAPI for PocketPC projects will have seen that there are several other projects around. Of these, I have experimented with a couple extensively, which also helped get me have a greater understanding of MAPI when learning. However, in the end, they failed me mainly for two reasons: They usually didn't compile out of the box (minor issue really) and, most importantly, they were always heavily C++ orientated.

Being heavily C++ orientated was a downfall, as it meant that any extensions required for the MAPI library would have to be done in C. As it turned out, compared to my final 99% .NET product, this had a performance hit. I attribute the performance hit to the fact that the C++ libraries were primarily intended to be used from a C++ project (which makes sense), with the .NET wrapper being another abstraction on top of that.

Library Overview

Available Messaging Technologies

In Win32 (PC and PocketPC), there are predominantly two data models used by Microsoft to store information about messages, contacts, tasks, appointments, etc. (Outlook items): Pocket Outlook Object Model (POOM) and Messaging Application Program Interface (MAPI). The latter only supports messages.

POOM is the more "modern" of the two, while MAPI has been around for years. Pocket devices (PocketPC, Smartphone, etc.) have supported a subset of the full MAPI for years, while POOM is only just starting to come around.

Choosing a Messaging Technology

The first part of my ambitious project was to be able to access messages in C# on my phone. So, starting with a blank slate, I did what I always do when I want to do something I haven't done before in .NET: I headed straight to the class libraries. When I saw the Microsoft.WindowsMobile.PocketOutlook namespace, I thought I'd hit gold, until I looked a little closer. This namespace is Microsoft's .NET wrapper for POOM objects. While a powerful little namespace for accessing contacts, tasks, appointments, etc., it does not support messages. So, POOM was out of the question.

I, therefore, had to resort to looking at the native (Win32) APIs for pocket devices to access messages, which led me to find MAPI. A quick squish over MAPI made me realise I had hit the right thing: MsgStores, Folders, and Messages.

My first implementation -- which I am only going to mention briefly -- was similar to other people's implementations of having a large C++ library. This resulted in a large C++ library and a large C# wrapper. As the project grew and the required functionality increased, the number of .NET wrapper functions ballooned beyond excess. The library also ran rather slowly, as huge numbers of strings were marshalled between C++ and C#. To add insult to injury, after prolonged use, there was an apparent memory leak, which meant having to close the program regularly.

The entire project was ditched for the more favoured and obvious approach: to do everything in the .NET world, including creating and destroying objects and marshalling data to .NET.

How MAPI Works

MAPI on Win32 and Pocket devices provides wrapper functions around some database that, in the background, stores all the MAPI items. For interest's sake, on PocketPC devices, mail is still stored in the old CE database (the name escapes me right now...), with talk of it being migrated to the CE SQL database soon. Because of the database back-end, MAPI has a database-ish feel to it in that, to access data for a given object, table columns are first populated and sorted. Then the required number of rows are requested. To really get a good understanding of how MAPI works, I recommend having a look over the API, which is rather confusing unless you bear in mind that it's a database wrapper.

Doing Everything in .NET

In deciding to do everything in C# (.NET), there was still the low-level native interaction with the MAPI. As it turns out, MAPI only has a minimal amount of individual functions (such as initiating the first transaction, MAPILogonEx, which gets an IMAPISession) and has interfaces for each message item such as a MsgStore, Folder, Message, etc. After exhaustively trying to call native interface functions from C#, I had to succumb to the fact that I couldn't make a 100% .NET MAPI wrapper. The result is a C++ wrapper that wraps the individual MAPI interface member functions. For example, for the IMAPISession member function HRESULT GetMsgStoresTable(ULONG ulFlags, IMAPITable ** lppTable), the following function is produced:

HRESULT IMAPISessionGetMsgStoresTable(IMAPISession *pSession,
        IMAPITable ** lppTable)
{
    return pSession->GetMsgStoresTable(0, lppTable);
}

MAPIdotnet Library Structure

The C# MAPI library has two levels of abstraction:

Internal MAPI Wrapper Classes

For each of the native MAPI interfaces, there is a corresponding .NET interface and class that is responsible for holding onto and disposing off the native MAPI pointer. Whenever a native interface is returned (e.g., opening a folder returns a IMAPIFolder pointer, or requesting a MAPI data table), the corresponding C# wrapper class is created, which holds onto that pointer to ensure it isn't lost. When a C# wrapper class loses its references (ready for garbage collection), the corresponding MAPI pointer is released to ensure that there are no memory leaks. These wrapper classes and interfaces are in the MAPIdotnet.cemapi namespace. The following code shows how the base interface for all items, IMAPIUnknown, keeps track of the interface pointer:

public interface IMAPIUnknown
{
    void Release();

    IntPtr Ptr { get; }
}
private abstract class MAPIUnknown : IMAPIUnknown
{
    [DllImport("MAPILib.dll", EntryPoint = "Release")]
    public static extern uint pRelease(IntPtr iUnknown);

    protected IntPtr ptr = IntPtr.Zero;

    public void Release()
    {
        if (this.ptr != IntPtr.Zero)
        {
            pRelease(this.ptr);
            this.ptr = IntPtr.Zero;
        }
    }

    public IntPtr Ptr { get { return this.ptr; } }

    ~MAPIUnknown() { Release(); }
}

The same tight control on pointers is used for getting property data from an item. Like all Win32 Microsoft APIs, MAPI has a heavy use of structures for getting and setting properties. Rather than trusting the .NET Marshaller to marshal an entire structure (which I have had problems with when the structure's content gets complicated), the pointers are used directly with individual marshalling calls made to reassemble the structure in C#. For example, a common MAPI structure is the SPropValue structure whose form is:

struct {
    ULONG ulPropTag;
    ULONG dwAlignPad;
    union _PV Value;
} SPropValue, *LPSPropValue;

When getting and setting the properties of an object (e.g., message, folder, etc.), you either receive or pass in an array of SPropValues. So, rather than trusting the C# Marshaller, the pointer is used directly. For an array of structures, the first structure is marshaled, and then the pointer is incremented and so on. The pointer is finally released as specified by MAPI. A snippet from the cemapi.MAPIProp wrapper class is given below as an example:

public IPropValue[] GetProps(PropTags[] tags)
{
    // Populate the tags
    uint[] t = new uint[tags.Length + 1];
    t[0] = (uint)tags.Length;
    for (int i = 0; i < tags.Length; i++)
        t[i + 1] = (uint)tags[i];

    IntPtr propVals = IntPtr.Zero;
    uint count = 0;

    // Call the native MAPI wrapper interface member wrapper:
    HRESULT hr = pIMAPIPropGetProps(this.ptr, t, out count, ref propVals);
    if (hr != HRESULT.S_OK)
        throw new Exception("GetProps failed: " + hr.ToString());

    IPropValue[] props = new IPropValue[count];
    uint pProps = (uint)propVals;

    // Iterate over the received property array, using the pointer offset
    for (int i = 0; i < count; i++)
    {
        pSPropValue lpProp =
            (pSPropValue)Marshal.PtrToStructure((IntPtr)(
            pProps + i * cemapi.SizeOfSPropValue), typeof(pSPropValue));
        props[i] = new SPropValue(lpProp);
    }
    // Free the pointer
    cemapi.MAPIFreeBuffer(propVals);
    return props;
}

Exposed Interfaces

The library exposes a single MAPI class and a series of interfaces that correspond to MAPI items. Each implementation of an exposed interface, in most cases, has a corresponding cemapi wrapper interface. The self-explanatory list is as follows:

Folder, Message, and MsgStore each inherit from Prop. Prop exposes common properties such as DisplayName, EntryID (session-specific IDs publicly used for comparing objects), and display Icon (although Icons don't exist for at least SMS messages in my experience). IMAPIMessageID and IMAPIFolderID extend from IEntryID which contain specific IDs for messages and folders, respectively. IDs can then be used to open folders and messages. Each of the exposed interface implementations provides more "human" ways of getting and setting information. For example, rather than having to do a database request for a message subject or time, the IMAPIMessage interface has string Subject { get; } and DateTime LocalDeliveryTime { get; } properties, which do all the necessary conversions.

Using the Code

The solution provided here has three projects: The minimal C++ MAPI wrapper (MAPIlib), the C# MAPI wrapper (MAPIdotnet), and a sample project for use on PocketPCs and Smartphones to display messages and some of their properties (PocketMail).

Screenshot - MAPIdotnet2.jpg

If you get Exceptions when it tries to access the native Win32 MAPIlib.dll, it may be due to it not being copied into the directory when you deploy the solution. To do this, add an "Existing Item" to the PocketMail project and navigate to MAPIlib/bin/[Release or Debug]/MAPIlib.dll. Don't click "Open". Click on the little down arrow next to the "Open" button and select "Open As Link." Then, in the properties box for that file, select "Build Action = Content" and "Copy to Output Directory = Always".

Warning: I am currently in the process of making item events that occur when something changes (e.g., a new message arrives, a message is deleted, a message subject is changed, etc.) more "elegant", so I recommend ignoring cemapi.IMAPIAdviseSink.

Message Store Events

An IMAPIMsgStore can now register for events such as new messages arriving, message/folder changes/moves/copies/deletes. IMAPIMsgStore exposes three events. As well as subscribing to the exposed events for folders and messages, the different event types must be masked by setting EventNotifyMask. A little note, you'll quickly realise that registering for every possible event is a nightmare as about half a dozen are risen every time something happens!

Points of Interest

Performance

As I was writing the library, I was constantly worried that doing practically everything in C# would mean it would run like a dog. As it turns out though, I was wrong. On my phone, I have over 1500 messages in my inbox, and to fetch each of these (which involves creating at least two classes for each one and getting all the information for them) takes just over a second. In comparison, to build the tree of nodes (not even display) for the PocketMail program provided takes about ten seconds. Then, to add the populated nodes to the TreeView (TreeView.Nodes.AddRange(TreeNode[] nodes)) takes over thirty seconds!

Ongoing Development

MAPIdotnet now has a dedicated project on SourceForge here. The project page gives access to an SVN repository with latest additions and new projects. If you're also interested, feel free to contact me to become a developer with commit access.

History

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
NewsInfo about Create Message Method
alexgr_79
1:36 27 Jan '10  
Hi,
I have a question: I need to import message from csv.
Is there way to create messages on phone with mapiDotNet?

Thanks
alessandro
GeneralRe: Info about Create Message Method
rwt33
9:36 27 Jan '10  
Reading some of the other questions and answers on this page will answer your question.
Robert
GeneralCreate Message
mohit sapru
2:37 15 Dec '09  
Hi Robert,

is there a way by which we can create a new sms with properties like sender, subject specified and save it to some specific folder?
Hope u get it.

Thanks,
Mohit

saps

GeneralRe: Create Message
rwt33
9:31 15 Dec '09  
At present I have only implemented methods for creating and sending new messages. MAPI does provide the functionality to create a new message and save it to a folder but you would have to implement this yourself.
Have fun!
Robert
GeneralRe: Create Message
mohit sapru
4:19 16 Dec '09  
I am not good with c++ so may be some body can help me out implimenting it or tell me how to proceede.

Thanks,
Mohit

saps

QuestionGeneric way to find SMS folder
mohit sapru
23:20 18 Nov '09  
Hi Again,

is there any way by which i can determine and access the SMS/MMS, INBOX folders in non-WWE roms.
i dont know if i am correct in doing this, i search for folder names by traversing through all folders found and matching their name to SMS and INBOX.

Please suggest if the way i am doing is correct and if not plz let me know and if correct plz suggest then how can we access these in non-WWE roms?

Thanks,
Mohit

saps

AnswerRe: Generic way to find SMS folder
rwt33
9:38 19 Nov '09  
There are unique methods for getting a message store's ReceiveFolder, SentMailFolder, OutboxFolder and TrashFolder in IMAPIMsgStore.
GeneralRe: Generic way to find SMS folder [modified]
mohit sapru
20:00 19 Nov '09  
Thanks Robert,

I will try that out.

[EDIT]
Sorry for my ignorance... but can u help me out how can we do this... using they way you are suggesting.
I am not able to find the way out...

for (int i = 0, length = stores.Length; i < length; i++)
{
IMAPIMsgStore store = stores[i];
if (store.DisplayName.ToUpper().Equals("SMS"))
{
folder = store.RootFolder.OpenFolder();
subFolders = folder.GetSubFolders((int)folder.NumSubFolders);
foreach (IMAPIFolderID fId in subFolders)
{
folders = fId.OpenFolder();
listFolder.Items.Add(folders.DisplayName.ToUpper().Trim());
listFolderMessage.Items.Add(folders.DisplayName.ToUpper().Trim());
if (folders.DisplayName.ToUpper().Equals("INBOX"))
{
folders.SortMessagesBySenderName(TableSortOrder.TABLE_SORT_DESCEND);
IMAPIMessage[] messages = folders.GetNextMessages(folders.NumSubItems);
FillContactsFromMessage(messages);
}
}
}
}


Mohit

saps

modified on Friday, November 20, 2009 3:37 AM

QuestionIs folder blocking possible....
mohit sapru
19:12 18 Nov '09  
Hi Robert,

using these wrappers i developed an app, for filtering sms rules...(XDA). well now people are requesting if folder lock is possible with it.
I am not sure for it since i have put all my focus in message interception using your wrappers.Confused

so if any one knows how to do that or is it possible plz let me know.

Thanks,
Mohit

saps

AnswerRe: Is folder blocking possible....
rwt33
9:34 19 Nov '09  
Hi Mohit,
I don't think it's possible to lock a folder in Windows Mobile.

Regards,
Robert
GeneralRe: Is folder blocking possible....
mohit sapru
20:01 19 Nov '09  
I will keep this question as open, may be some one finds out how to do it some other way. Smile

Regards,
Mohit

saps

GeneralRe: Is folder blocking possible....
Krupal Desai
20:59 14 Dec '09  
Well, I tried out some way to create hidden folder under message store. Big Grin Actually I don't know over and above to it, but does my job Smile

What I did is created a new folder at location parent location of root folder. hmmm.. here is a part of code


const string hiddenFolderName = "secret";

//Open folder to parent location of root folder :confused:
IMAPIFolder topFolder = storeSMS.RootFolder.OpenFolder().ParentFolder.OpenFolder();

//First check if so called "hidden" folder already exists or not?
foreach (IMAPIFolderID thifol in topFolder.GetSubFolders(topFolder.NumSubFolders))
{
if (thifol.OpenFolder().DisplayName.Equals(hiddenFolderName))
{
hiddenFolder = thifol.OpenFolder();
break;
}
}

//If its null, then just create it
if (hiddenFolder == null)
{
hiddenFolder = topFolder.CreateFolder(hiddenFolderName, false);
}


Now when I copy messages to this hiddenFolder it is not visible from pocket outlook UI. Thats what I wanted. Smile

I made a form just to view messages from this hidden folder. It works fine!

Also I added options to copy all msgs from hidden to some visible folder and vice-versa. This is useful when I backup messages via PIMBackup or some other tool.

Here comes the nightmare: Few days back, when I searched some text via inbuilt WM search, it brought up messages from this hiddenfolder Frown Frown Frown , but I'm sure, it won't b much difficult job to encrypt message text and put it there.. just to bypass it from searching.

Hope this helps to somebody Smile

@Mohit: I saw your filter sms app on xda, actually i wanted such app since long. It is very nice. Great job! If you had developed some where around last year, then I would havn't got a chance to develop it on my own. lol Laugh

Thanks a lot to Robert, for sharing this wonderful MAPI .NET wrapper Smile

Happy Coding!
GeneralHow to export sms
siliconesoul
7:57 17 Nov '09  
How to export inbox (all folders) to file (backup) ?
so it can be imported to another device
GeneralRe: How to export sms
rwt33
11:06 17 Nov '09  
You'd have to write a utility that read out each message and store it using a method of your choice.
If you're looking for a simple way to backup/restore messages on Windows Mobile there are already free utilities on the web (xda-developers.org mentions some) for this.

Robert
GeneralRe: How to export sms
siliconesoul
11:28 17 Nov '09  
Thanks Robert

Actually searching for free sms/contact export/import based on c# brought me this project closest Smile
I have little knowledge on win mobile development but can understand from this project that it can be used as starting point.

Also I'am bit surprised that there are no tools (free ones) for win mobile backup. It seems very basic operation for a advanced platform like win mobile. Was expecting to find export/import tool/code as example project Smile (or I'am I wrong with this so that's why there are lots of commercial ones).
GeneralRe: How to export sms
mohit sapru
2:31 15 Dec '09  
Hi,

Ever stumbled upon
PIM Backup[^]
this can backup your messages, contacts, appointments but only for windows mobile. if you want to export to some non wm device i am not sure for that

Thanks,
Mohit

saps

GeneralConverting PocketOutlook.ItemID => MAPIdotnet.IMAPIMessageID [modified]
Ronny Gydar
10:08 2 Nov '09  
I have a scenario where I use Microsoft.WindowsMobile.PocketOutlook.MessageInterception.MessageInterceptor to intercept all inncomming of specific types, and thereby have an PocketOutlook.ItemID (of type "MapiID") for the SMS.

Then using a complex ruleset, the code will determine if this SMS actually was for my program, and show it in a specific way, and delete it...or just disregard it.

And then instead of using "NotifyAndDelete" on MessageInterceptor (which could potentially end up deleting SMS's not meant for my program), I thought I could use "Notify"...and let my code do the "complex rulehandling"...and then use MAPIdotnet to delete SMS when/if neccessary. I already have tested that deleting an SMS with MAPIdotnet is easy to do...

...so in essense I guess what I am asking is...
how do I convert a PocketOutlook.ItemID to a MAPIdotnet.IMAPIMessageID? Smile

Have searched and not found anyone asking this (neither here or at SourceForge)...so either I am the only one with this scenario...or am I just a bit more "stupid" than everyone else! Laugh

modified on Monday, November 2, 2009 3:14 PM

GeneralRe: Converting PocketOutlook.ItemID => MAPIdotnet.IMAPIMessageID
rwt33
10:22 2 Nov '09  
Unfortunately that's not an easy one. I haven't personally tried it but your best bet is trying to find some form of underlying ID that relates a POOM object (PocketOutlook) to MAPI objects (MAPIdotnet). Failing that you might be able to write a method that searches for the specific object using the native MAPI routines. It would be relatively fast computationally but may take a bit of effort in the implementation. MAPIdotnet already uses MAPITables to get specific object data so that might be able to give you some implementation hints.
Good luck.

Robert
QuestionCan I move a message from one folder to another.
mohit sapru
0:15 2 Nov '09  
Hi,

I was trying to build one application where in i need to move a message from one folder to another.
Is it possible with your current wrapper? Sniff

saps

AnswerRe: Can I move a message from one folder to another.
rwt33
9:25 2 Nov '09  
Hi,
I can't recall if I've implemented that specific function but I do recall it is in the MAPI API so would require almost minimal effort to add the implementation.

Regards,
Robert
GeneralRe: Can I move a message from one folder to another.
mohit sapru
1:37 5 Nov '09  
Thanks Robert,

Using your wrapper i have created application to filter SMS based on contact or message content.
Link[Open In new Window]

saps

QuestionCan't see files in SourceForge!?
Ronny Gydar
6:38 11 Oct '09  
On SouceForge I can see files in other projects (the "big green downloadbutton", so forth), but for the MapiDotNet project I see no download button, and no files are shown when I select view all files.

I am logged in with my SourceForge account when I experience this.

Anyone know the reason for this!?

I have been trying to piece together a fix for the "mail-body" problem using the old code here, and some of the hints about the problem, with some extra code found on the net...but it would of course be a LOT easier just to get the latest version of the source! Smile
AnswerRe: Can't see files in SourceForge!?
rwt33
10:46 11 Oct '09  
To get the latest version you have to get it from the SVN repo, not "View all files".
When you first visit the SourceForge MAPIdotnet project you need to click on "Develop" which then shows all the other developer options. Of which one of them is "Code"->"SVN".

Look forward to seeing some good changes!
Rob
GeneralRe: Can't see files in SourceForge!?
Ronny Gydar
0:38 18 Oct '09  
Thanks Rob, that helped, got the latest version now!

Not able to get body in this version either, always empty when testing on my WM 6.1 Touch HD at least.

But in this version I see you have added both GUI and code for fetching body, so I guess that is one of the things your'e working on "as-we-speak"...as you also hint about in your reply!

Keep up the good work, seems like a lot of people have been missing an C# API for this stuff! Smile
GeneralI want updated version of MAPIdotnet files?
Ankit2502
2:19 9 Sep '09  
Hello Everyone,
I have older version files of MAPIdotnet and i got the E_NOTIMP exception while retrieveing message.Recepients and i read discussion about this and they told that this problem is solved in updated version but i am unable to get Updated Version i go through the lates version link on sourceforge.net but files are not present on this site.If anybody have updated filess then please send me that files.


Last Updated 28 Apr 2008 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010