---
title: "Read a PST file in C#"
url: https://recursiveai.org/onemail/cookbook/read-pst-file/
description: "Read an Outlook PST or OST file in C# without Outlook: walk every folder and message, with sender, date, subject and attachments. Runnable code with OneMail 0.3.0-preview.2."
version: "0.3.0-preview.2"
---

# Read a PST file in C#

`Mail.Open` opens a PST (or OST) store and `Enumerate()` walks every message in every folder. Messages are read one at a time as you reach them, so a large store doesn’t have to fit in memory. The program below lists Apache Tika’s public test store.

```shell
dotnet new console -o ReadPst && cd ReadPst
dotnet add package RecursiveAI.OneMail --version 0.3.0-preview.2
curl -LO https://raw.githubusercontent.com/apache/tika/4.2.0/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/test/resources/test-documents/testPST.pst
```

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

```csharp
using RecursiveAI.OneMail;

// Opening reads only the store header; each message is read as you reach it
using MailContainer pst = Mail.Open("testPST.pst");

int count = 0;
foreach (IMailItem msg in pst.Enumerate())
{
    count++;
    MailAddress from = msg.From.FirstOrDefault();
    Console.WriteLine($"{msg.Sent?.Value:yyyy-MM-dd}  {from.Name}: {msg.Subject}");
    Console.WriteLine($"            folder: {msg.Provenance.Folder}, " +
                      $"attachments: {msg.Attachments.Count}");
}
Console.WriteLine($"{count} messages");
```

```text
2014-02-26  Jörn Kottmann: Re: Feature Generators
            folder: Début du fichier de données Outlook, attachments: 0
2014-02-25  Barry Olddog: Re: init tokenizer fails: "Bad type in putfield/putstatic"
            folder: Début du fichier de données Outlook, attachments: 0
2014-02-25  Kenig Ma: Feature Generators
            folder: Début du fichier de données Outlook, attachments: 0
2014-02-26  Nick Burch (JIRA): [jira] [Resolved] (TIKA-1249) Vcard files detection
            folder: Début du fichier de données Outlook, attachments: 0
2014-02-26  Gary Murphy (JIRA): [jira] [Commented] (TIKA-1250) Process loops infintely processing a CHM file
            folder: Début du fichier de données Outlook, attachments: 0
2014-02-24  Couchbase: [WEBINAR] - "Introducing Couchbase Server 2.5"
            folder: Début du fichier de données Outlook, attachments: 0
2020-11-26  Luis Filipe da Cruz Nassif: FW: First email
            folder: Début du fichier de données Outlook, attachments: 1
7 messages
```

Each item is an `IMailItem`, the same view as an MSG or EML file: `TextBody`, `HtmlBody`, `Attachments` and the rest work as in [Read an Outlook MSG file](https://recursiveai.org/onemail/cookbook/read-msg-file/). `Provenance.Folder` is the folder path inside the store. The last message’s sender has only an Exchange address, so its name prints and its SMTP address is empty.

## List the folders

For the folder tree itself, open the store with `PstStore`:

```csharp
using RecursiveAI.OneMail.Pst.Messaging;

if (!PstStore.TryOpen("testPST.pst", out PstStore? store, out var defects))
{
    Console.WriteLine($"Not a PST: {string.Join(", ", defects)}");
    return;
}
using (store)
{
    foreach (PstFolder folder in store!.EnumerateFolders())
    {
        if (folder.Depth == 0) continue;   // the unnamed root
        string indent = new(' ', 2 * (folder.Depth - 1));
        int messages = folder.EnumerateMessageIds().Count();
        string search = folder.IsSearchFolder ? " (search folder)" : "";
        Console.WriteLine($"{indent}{folder.Name}: {messages}{search}");
    }
}
```

```text
Début du fichier de données Outlook: 7
  Éléments supprimés: 0
Racine (pour la recherche): 0
SPAM Search Folder 2: 0 (search folder)
IPM_COMMON_VIEWS: 0
ItemProcSearch: 0 (search folder)
```

`Mail.Open` returns the ordinary messages of ordinary folders. Search folders only link to messages stored elsewhere, so their contents aren’t listed again. Hidden items (rules, views) come from `folder.EnumerateMessages(PstMessageSet.Associated)`.

## Limits

- **A preview.** 0.3.0-preview.2: the API can change before 1.0.
- **Reading only.** OneMail can’t write PST files yet. To export messages, [convert a PST to EML files](https://recursiveai.org/onemail/cookbook/convert-pst-to-eml/).
- **Calendar and task items** come through the read view with `Kind` set to `Appointment` or `Task`, a subject and a body. For their times, attendees and recurrence, [convert them to ICS](https://recursiveai.org/onemail/cookbook/convert-msg-to-ics/).
- **Layouts checked on generated files only.** In the project’s words: “no public ANSI or 512-byte-page Unicode PST from Outlook exists; those layouts and 4 KiB multi-map stores are checked only on files we generate.”
- **Exchange addresses.** There is no directory lookup: a sender with no SMTP address in the store keeps only its name and X.500 address.

OST files open the same way; the project checked them on the NIST CFReDS OST. Everything checked, and every known gap: [Fidelity and known gaps](https://recursiveai.org/onemail/docs/compatibility/). The store layers: [PST and OST messages](https://recursiveai.org/onemail/docs/pst-messaging/).
