Outlook Message Format Library

  • Thread starter Thread starter Steven Nagy
  • Start date Start date
S

Steven Nagy

Hi all,

I need to be able to take messages that have been saved from Outlook
(*.msg) and read them into a C# application (think of it as workflow
management). But these msg files are pretty funky.
All my research only turned up stuff about Automating Exchange and MAPI
which I know nothing about. I would prefer in this instance to think of
it more as a "black box" scenario where I can have a nice managed
library where I can load a msg file and have nicely exposed properties
about the message. We would then save the important information
(sender, subject, body, importance) to a database table and discard the
actual message file.

I can pay for such a component if it exists, but can develop one with
the right guidance as well.

Can you please provide advice to help me solve my problem?

Many thanks,
Steven Nagy
 
Hi Steven,
I had to deal with *.msg files as well a while back. Turns out they are
actually so-called compound files (a single file containing several
streams/subdirectories).
First you need the StgOpenStorage(Ex) function from the ole32.dll to
open these files, then
you can use the IStream/IStorage Interfaces to work with the contents
(IStream is already implemented in the .NET Framework, IStorage
unfortunately isn't).
I can't release the classes I created to work with these messages, but
I can provide you with my IStorage-Interface and StgOpenStorage
definitions, hopefully these will enable you to open the *.msg files
and take a look at their contents.
Its actually pretty straightforward from there, certain properties are
always stored in certain subdirectories/streams, so you need to figure
out the names of the streams you need and read their contents.
For further assistance with the IStream/IStorage interfaces you can
take a look at the msdn, they are pretty well documented.

Sincerely,
Kevin Wienhold

Definitions:

[ComImport,ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("0000000b-0000-0000-C000-000000000046")]
public interface IStorage
{
[PreserveSig]
[return: MarshalAs(UnmanagedType.Error)]
uint CreateStream(
[In, MarshalAs(UnmanagedType.LPWStr)] string pwcsName,
[In] int grfMode,
[In] int reserved1,
[In] int reserved2,
[Out] out UCOMIStream ppstm);


[PreserveSig]
[return: MarshalAs(UnmanagedType.Error)]
uint OpenStream(
[In, MarshalAs(UnmanagedType.LPWStr)] string pwcsName,
[In] IntPtr reserved1,
[In] int grfMode,
[In] int reserved2,
[Out] out UCOMIStream ppstm);


[PreserveSig]
[return: MarshalAs(UnmanagedType.Error)]
uint CreateStorage(
[In, MarshalAs(UnmanagedType.LPWStr)] string pwcsName,
[In] int grfMode,
[In] int reserved1,
[In] int reserved2,
[Out] out IStorage ppstg);


[PreserveSig]
[return: MarshalAs(UnmanagedType.Error)]
uint OpenStorage(
[In, MarshalAs(UnmanagedType.LPWStr)] string pwcsName,
[In, MarshalAs(UnmanagedType.Interface)] IStorage pstgPriority,
[In] int grfMode,
[In] string[] snbExclude,
[In] int reserved,
[Out] out IStorage ppstg);


[PreserveSig]
[return: MarshalAs(UnmanagedType.Error)]
uint CopyTo(
[In] int ciidExclude,
[In] Guid[] rgiidExclude,
[In] string[] snbExclude,
[In, MarshalAs(UnmanagedType.Interface)] IStorage pstgDest);


[PreserveSig]
[return: MarshalAs(UnmanagedType.Error)]
uint MoveElementTo(
[In, MarshalAs(UnmanagedType.LPWStr)] string pwcsName,
[In, MarshalAs(UnmanagedType.Interface)] IStorage pstgDest,
[In, MarshalAs(UnmanagedType.LPWStr)] string pwcsNewName,
[In] int grfFlags);


[PreserveSig]
[return: MarshalAs(UnmanagedType.Error)]
uint Commit(
[In] int grfCommitFlags);


[PreserveSig]
[return: MarshalAs(UnmanagedType.Error)]
uint Revert();


[PreserveSig]
[return: MarshalAs(UnmanagedType.Error)]
uint EnumElements(
[In] int reserved1,
[In] IntPtr reserved2,
[In] int reserved3,
[Out] out IEnumSTATSTG ppenum);


[PreserveSig]
[return: MarshalAs(UnmanagedType.Error)]
uint DestroyElement(
[In, MarshalAs(UnmanagedType.LPWStr)] string pwcsName);


[PreserveSig]
[return: MarshalAs(UnmanagedType.Error)]
uint RenameElement(
[In, MarshalAs(UnmanagedType.LPWStr)] string pwcsOldName,
[In, MarshalAs(UnmanagedType.LPWStr)] string pwcsNewName);


[PreserveSig]
[return: MarshalAs(UnmanagedType.Error)]
uint SetElementTimes(
[In, MarshalAs(UnmanagedType.LPWStr)] string pwcsName,
[In] ref FILETIME pctime,
[In] ref FILETIME patime,
[In] ref FILETIME pmtime);


[PreserveSig]
[return: MarshalAs(UnmanagedType.Error)]
uint SetClass(
[In] ref Guid clsid);


[PreserveSig]
[return: MarshalAs(UnmanagedType.Error)]
uint SetStateBits(
[In] uint grfStateBits,
[In] uint grfMask);


[PreserveSig]
[return: MarshalAs(UnmanagedType.Error)]
uint Stat(
[Out] out STATSTG pstatstg,
[In] uint grfStatFlag);



}


[DllImport("Ole32.dll")]
private static extern int StgOpenStorage
(
[MarshalAs(UnmanagedType.LPWStr)] string wcsName,
IStorage pstgPriority,
int grfMode, // access method
IntPtr snbExclude, // must be NULL
int reserved, // reserved
out IStorage storage // returned storage
);
 
Thanks Kevin. This is definately a good place for me to start.
Great well rounded response!

Hi Steven,
I had to deal with *.msg files as well a while back. Turns out they are
actually so-called compound files (a single file containing several
streams/subdirectories).
First you need the StgOpenStorage(Ex) function from the ole32.dll to
open these files, then
you can use the IStream/IStorage Interfaces to work with the contents
(IStream is already implemented in the .NET Framework, IStorage
unfortunately isn't).
I can't release the classes I created to work with these messages, but
I can provide you with my IStorage-Interface and StgOpenStorage
definitions, hopefully these will enable you to open the *.msg files
and take a look at their contents.
Its actually pretty straightforward from there, certain properties are
always stored in certain subdirectories/streams, so you need to figure
out the names of the streams you need and read their contents.
For further assistance with the IStream/IStorage interfaces you can
take a look at the msdn, they are pretty well documented.

Sincerely,
Kevin Wienhold

Definitions:

[ComImport,ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("0000000b-0000-0000-C000-000000000046")]
public interface IStorage
{
[PreserveSig]
[return: MarshalAs(UnmanagedType.Error)]
uint CreateStream(
[In, MarshalAs(UnmanagedType.LPWStr)] string pwcsName,
[In] int grfMode,
[In] int reserved1,
[In] int reserved2,
[Out] out UCOMIStream ppstm);


[PreserveSig]
[return: MarshalAs(UnmanagedType.Error)]
uint OpenStream(
[In, MarshalAs(UnmanagedType.LPWStr)] string pwcsName,
[In] IntPtr reserved1,
[In] int grfMode,
[In] int reserved2,
[Out] out UCOMIStream ppstm);


[PreserveSig]
[return: MarshalAs(UnmanagedType.Error)]
uint CreateStorage(
[In, MarshalAs(UnmanagedType.LPWStr)] string pwcsName,
[In] int grfMode,
[In] int reserved1,
[In] int reserved2,
[Out] out IStorage ppstg);


[PreserveSig]
[return: MarshalAs(UnmanagedType.Error)]
uint OpenStorage(
[In, MarshalAs(UnmanagedType.LPWStr)] string pwcsName,
[In, MarshalAs(UnmanagedType.Interface)] IStorage pstgPriority,
[In] int grfMode,
[In] string[] snbExclude,
[In] int reserved,
[Out] out IStorage ppstg);


[PreserveSig]
[return: MarshalAs(UnmanagedType.Error)]
uint CopyTo(
[In] int ciidExclude,
[In] Guid[] rgiidExclude,
[In] string[] snbExclude,
[In, MarshalAs(UnmanagedType.Interface)] IStorage pstgDest);


[PreserveSig]
[return: MarshalAs(UnmanagedType.Error)]
uint MoveElementTo(
[In, MarshalAs(UnmanagedType.LPWStr)] string pwcsName,
[In, MarshalAs(UnmanagedType.Interface)] IStorage pstgDest,
[In, MarshalAs(UnmanagedType.LPWStr)] string pwcsNewName,
[In] int grfFlags);


[PreserveSig]
[return: MarshalAs(UnmanagedType.Error)]
uint Commit(
[In] int grfCommitFlags);


[PreserveSig]
[return: MarshalAs(UnmanagedType.Error)]
uint Revert();


[PreserveSig]
[return: MarshalAs(UnmanagedType.Error)]
uint EnumElements(
[In] int reserved1,
[In] IntPtr reserved2,
[In] int reserved3,
[Out] out IEnumSTATSTG ppenum);


[PreserveSig]
[return: MarshalAs(UnmanagedType.Error)]
uint DestroyElement(
[In, MarshalAs(UnmanagedType.LPWStr)] string pwcsName);


[PreserveSig]
[return: MarshalAs(UnmanagedType.Error)]
uint RenameElement(
[In, MarshalAs(UnmanagedType.LPWStr)] string pwcsOldName,
[In, MarshalAs(UnmanagedType.LPWStr)] string pwcsNewName);


[PreserveSig]
[return: MarshalAs(UnmanagedType.Error)]
uint SetElementTimes(
[In, MarshalAs(UnmanagedType.LPWStr)] string pwcsName,
[In] ref FILETIME pctime,
[In] ref FILETIME patime,
[In] ref FILETIME pmtime);


[PreserveSig]
[return: MarshalAs(UnmanagedType.Error)]
uint SetClass(
[In] ref Guid clsid);


[PreserveSig]
[return: MarshalAs(UnmanagedType.Error)]
uint SetStateBits(
[In] uint grfStateBits,
[In] uint grfMask);


[PreserveSig]
[return: MarshalAs(UnmanagedType.Error)]
uint Stat(
[Out] out STATSTG pstatstg,
[In] uint grfStatFlag);



}


[DllImport("Ole32.dll")]
private static extern int StgOpenStorage
(
[MarshalAs(UnmanagedType.LPWStr)] string wcsName,
IStorage pstgPriority,
int grfMode, // access method
IntPtr snbExclude, // must be NULL
int reserved, // reserved
out IStorage storage // returned storage
);

Steven said:
Hi all,

I need to be able to take messages that have been saved from Outlook
(*.msg) and read them into a C# application (think of it as workflow
management). But these msg files are pretty funky.
All my research only turned up stuff about Automating Exchange and MAPI
which I know nothing about. I would prefer in this instance to think of
it more as a "black box" scenario where I can have a nice managed
library where I can load a msg file and have nicely exposed properties
about the message. We would then save the important information
(sender, subject, body, importance) to a database table and discard the
actual message file.

I can pay for such a component if it exists, but can develop one with
the right guidance as well.

Can you please provide advice to help me solve my problem?

Many thanks,
Steven Nagy
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Back
Top