---
title: "Convert MSG to EML in C#"
url: https://recursiveai.org/onemail/cookbook/convert-msg-to-eml/
description: "Convert an Outlook .msg file to .eml in C#, with attachments, both bodies and a report of every property that didn’t carry over. Runnable code with OneMail 0.3.0-preview.2."
version: "0.3.0-preview.2"
---

# Convert MSG to EML in C#

Open the `.msg` with `MsgFile.TryOpen`, convert its message with `MapiToMimeConverter`, and write the result. The conversion follows Microsoft’s published mapping between Outlook properties and MIME (MS-OXCMAIL). It also returns a loss report naming everything that didn’t carry over. The program below converts a public test message from Apache POI.

```shell
dotnet new console -o MsgToEml && cd MsgToEml
dotnet add package RecursiveAI.OneMail --version 0.3.0-preview.2
curl -LO https://raw.githubusercontent.com/apache/poi/REL_5_4_1/test-data/hsmf/attachment_test_msg.msg
```

Put this in `Program.cs` and run `dotnet run`:

```csharp
using RecursiveAI.OneMail;
using RecursiveAI.OneMail.Conversion;
using RecursiveAI.OneMail.Conversion.OxcMail;
using RecursiveAI.OneMail.Msg;

if (!MsgFile.TryOpen("attachment_test_msg.msg", out MsgFile? msg, out var defects))
{
    Console.WriteLine($"Not an Outlook message: {string.Join(", ", defects)}");
    return;
}
using (msg)
{
    // MAPI to MIME by Microsoft's rules (MS-OXCMAIL); Convert never throws on content
    var result = new MapiToMimeConverter().Convert(msg.Message);

    using (FileStream eml = File.Create("attachment_test_msg.eml"))
        result.Value.WriteTo(eml);
    long size = new FileInfo("attachment_test_msg.eml").Length;
    Console.WriteLine($"Wrote attachment_test_msg.eml, {size:N0} bytes");

    // Everything that did not carry over is in the loss report
    foreach (var group in result.Report.GroupBy(e => e.Kind))
        Console.WriteLine($"{group.Key}: {group.Count()} properties");
}

// Read it back as any mail client would
IMailItem copy = Mail.Parse(File.ReadAllBytes("attachment_test_msg.eml"));
Console.WriteLine($"{copy.Subject} | {copy.From[0]} | {copy.Sent?.Value:u}");
foreach (IMailAttachment a in copy.Attachments)
    Console.WriteLine($"  {a.FileName} ({a.Size:N0} bytes)");
```

```text
Wrote attachment_test_msg.eml, 35,184 bytes
Approximated: 5 properties
Internal: 46 properties
Dropped: 46 properties
test pièce jointe 1 | Nicolas1 23456 <nicolas1.23456@free.fr> | 2009-04-22 14:36:33Z
  test-unicode.doc (24,064 bytes)
  pj1.txt (89 bytes)
```

The `.eml` has the sender, recipient, date, subject, a plain text and an HTML body, and both attachments. The HTML was wrapped inside the message’s RTF body, and the converter unwraps it. Python’s `email` package and the `onemail` tool read the file too.

## The loss report

`result.Report` lists every source property that wasn’t carried exactly, each with a `Kind`, a rule `Code` and a reason:

- **Dropped** here are mostly attachment bookkeeping that MIME has no field for (`NoInternetMapping`), such as each attachment’s creation time.
- **Approximated** are values carried in a weaker form: the `Date` header holds whole seconds (`DateSubSecondDropped`), and the HTML came from the RTF body (`HtmlFromRtf`).
- **Internal** are store bookkeeping such as entry IDs and search keys, listed so nothing is silent.

Nothing goes missing without an entry. The project sent 70 Outlook items through MIME and back and accounted for all 11,142 properties: 0 silent losses.

## Limits

- **A preview.** 0.3.0-preview.2: the API can change before 1.0.
- **Exchange addresses.** In the project’s words: “EX addresses with no SMTP form in the item are left out and reported”. Each one is an `AddressUnresolved` entry in the report.
- **Bcc** isn’t written by default. Set `new OxcMailExportOptions { IncludeBcc = true }` to keep it.
- **One way for files.** EML to MSG would need an MSG writer, which isn’t in this preview. `MimeToMapiConverter` converts to the MAPI model in memory.

For a whole mailbox, see [Convert a PST file to EML](https://recursiveai.org/onemail/cookbook/convert-pst-to-eml/). Every rule: [MAPI and MIME conversion](https://recursiveai.org/onemail/docs/conversion/).
