Programming, technology, and CRM – from a Belgian programmer exiled to Missouri
  • rss
  • Home
  • Soft Gallery
    • autosvnbackup.sh
    • VBScript Snippets
  • Contact Me
  • Welcome

MIME -> MAPI Conversion

Nicolas Galler | July 13, 2007

Last project we had a requirement to read an email (as a regular rfc822 email message) and save it to a msg file so it could be readily opened with Outlook.

There is no interface to import a message in the Outlook Object Model so the only way appears to be using MAPI. There is some documentation on how to save a message object to a .msg file here: http://support.microsoft.com/?kbid=171907. To parse the rfc822 message was a bit trickier to find out, though – we need to use the IConverterSession api, and the documentation is not top.

Well this was also my first Managed C++ project. Not that it is that different from regular C++. Should be possible to do this with P/Invoke too, I reckon, but why bother? (Edit: as it turns out, there is a decent reason to bother, because the VC++ program requires an additional redistributable install. Now why is there a redist needed in addition to the .NET redist, I don’t know, but it really seems silly)

Here is the code:

// This is the main DLL file.
#include "stdafx.h"
#include "MimeToMapi.h"
#include "IConverterSession.h"
#ifndef MAPI_NO_COINIT
# define MAPI_NO_COINIT 8
#endif

using namespace System;
using namespace SSSWorld::MimeToMapi;
using namespace System::Runtime::InteropServices;

MimeToMapi::MimeToMapi()
{
 String^ dirBefore = System::Environment::CurrentDirectory;
 // the MAPI_NO_COINIT is needed when calling from the CLR otherwise
 // MAPIInitialize randomly fails with "Unknown Flag" error
 MAPIINIT_0 MAPIINIT= { MAPI_INIT_VERSION, MAPI_MULTITHREAD_NOTIFICATIONS |  MAPI_NO_COINIT } ;
 if(FAILED((res = MAPIInitialize(&MAPIINIT))))
 {
  throw gcnew ApplicationException(String::Format("Failed to initialize MAPI: {0}", res));
 }
 System::Environment::CurrentDirectory = dirBefore;
}

MimeToMapi::~MimeToMapi()
{
 MAPIUninitialize();
}

void MimeToMapi::SaveAsMsg(System::String ^emlFilePath, System::String ^outputFilePath)
{
 LPMESSAGE pIMsg = NULL;
 LPSTORAGE pStorage = NULL;
    LPMSGSESS pMsgSession =  NULL;
 LPSTREAM stream = NULL;
 LPTSTR szDestPath = NULL;
 LPMAPISESSION session = NULL;
 HRESULT res = 0;

 // get memory allocation function
 LPMALLOC pMalloc = MAPIGetDefaultMalloc();

 IConverterSession * converter = NULL;

 stream = MimeToMapi::OpenStream(emlFilePath);
 //converter->SetEncoding(IET_RFC1522);
 try {
  CoCreateInstance(CLSID_IConverterSession, NULL, CLSCTX_INPROC_SERVER, IID_IConverterSession, (LPVOID*)&converter);

  szDestPath = static_cast(Marshal::StringToHGlobalUni(outputFilePath).ToPointer());
      // create compound file
  res = ::StgCreateDocfile(szDestPath,
                          STGM_READWRITE |
                          STGM_TRANSACTED |
                          STGM_CREATE, 0, &pStorage);

  if(FAILED(res))
   throw gcnew ApplicationException(String::Format("Error creating output file store {0}: {1}", outputFilePath, res));

  // Open an IMessage session.
  res = ::OpenIMsgSession(pMalloc, 0, &pMsgSession);
   if(FAILED(res))
   throw gcnew ApplicationException(String::Format("Error opening msgSession: {0}", res));

  // Open an IMessage interface on an IStorage object
  res = ::OpenIMsgOnIStg(pMsgSession,
                        MAPIAllocateBuffer,
                        MAPIAllocateMore,
                        MAPIFreeBuffer,
                        pMalloc,
                        NULL,
                        pStorage,
                        NULL, 0, 0, &pIMsg);
  if(FAILED(res))
   throw gcnew ApplicationException(String::Format("Error creating message on store: {0}", res));

  converter->SetEncoding(IET_QP);
  converter->SetSaveFormat(SAVE_RFC822);
  converter->MIMEToMAPI(stream, pIMsg, NULL, 0);

  pIMsg->SaveChanges(0);
 } finally {
  stream->Release();
  if(pIMsg)
   pIMsg->Release();
  if(pStorage)
   pStorage->Release();
  if(converter)
   converter->Release();
  if(szDestPath)
   Marshal::FreeHGlobal((IntPtr)szDestPath);
  if(pMsgSession)
   ::CloseIMsgSession(pMsgSession);
 }
}

LPSTREAM MimeToMapi::OpenStream(String^ source)
{
 LPTSTR szSourcePath = NULL;
 LPSTREAM stream;
 HRESULT res;

 try {
  // NOTE - although the documentation states that OpenStreamOnFile accepts an LPTSTR string,
  // it does not seem to accept the StringToHGlobalUni results
  szSourcePath = static_cast(Marshal::StringToHGlobalAnsi(source).ToPointer());
  if(FAILED(res = OpenStreamOnFile(MAPIAllocateBuffer,
    MAPIFreeBuffer,
    STGM_READ,
    szSourcePath,
    NULL,
    &stream))){
   throw gcnew ApplicationException(String::Format("Error opening file {0}: Error code {1}",
     source, res));
  }
  return stream;
 } finally {
  if(szSourcePath)
   Marshal::FreeHGlobal((IntPtr)szSourcePath);
 }
}
Categories
Programming
Comments rss
Comments rss
Trackback
Trackback

« NHibernate does not read uncommitted data! Formatting localized date in the browser »

Leave a Reply

Click here to cancel reply.

Categories

  • Dojo (1)
  • Experiments (4)
  • Force.com (2)
  • Interesting (1)
  • Javascript (3)
  • MSCRM (1)
  • Programming (63)
  • Rant (3)
  • Saleslogix (41)
  • Tricks (8)
  • Uncategorized (32)

Post History

  • 2011
    • January (3)
    • February (2)
    • March (1)
  • 2010
    • January (3)
    • March (3)
    • April (2)
    • August (2)
    • October (4)
    • November (1)
    • December (2)
  • 2009
    • March (2)
    • April (1)
    • May (3)
    • June (3)
    • July (1)
    • September (3)
    • October (2)
    • December (5)
  • 2008
    • January (9)
    • February (4)
    • March (9)
    • April (1)
    • May (5)
    • June (8)
    • July (1)
    • August (2)
    • September (1)
    • November (1)
    • December (3)
  • 2007
    • January (3)
    • February (7)
    • March (1)
    • April (3)
    • May (6)
    • June (2)
    • July (1)
    • August (2)
    • September (5)
    • October (3)
    • November (5)
    • December (4)
  • 2006
    • January (2)
    • September (1)
    • November (3)
    • December (4)
  • 2005
    • April (1)

Meta

  • Log in
  • Entries RSS
  • Comments RSS
  • WordPress.org
rss Comments rss valid xhtml 1.1 design by jide powered by Wordpress get firefox