WLM2PST converts a recursive folder tree of Windows Live Mail .eml files
into one Unicode .pst file that opens locally in classic Microsoft
Outlook. That is the entire product.
wlm2pst.exe --source "C:\Mail\Windows Live Mail" --output "D:\Migration\Sample-Mail.pst"- Recursively scans a source folder for
.emlfiles. - Recreates the source folder structure as ordinary PST folders under one
root folder (default name
Windows Live Mail, or--root-name). - Imports every EML into the PST through classic Outlook's own MIME-to-MAPI
converter (
IConverterSession::MIMEToMAPI), so the parsing behavior is Outlook's, not a reimplementation. - Writes a single Unicode PST via Outlook's
MSUPST MSprovider. - Keeps a crash-safe SQLite resume database beside the PST so an interrupted
job can continue with
--resumeinstead of restarting. - Validates the finished PST through a second, independent MAPI session before reporting success.
WLM2PST has exactly one destination format and no cloud path. It does not implement, and will not add:
- Microsoft 365, Exchange, or Exchange Online migration.
- Microsoft Graph API access.
- IMAP upload.
- Cloud storage of any kind.
- OST creation.
- MSG or MBOX export.
- Contact, calendar, task, account-settings, signature, or rule migration from Windows Live Mail.
- Automatic duplicate deletion.
- Automatic splitting into multiple PST files.
- Merging into an arbitrary pre-existing PST.
- A GUI or a web service.
- Telemetry or any other network dependency at runtime.
- HTML rendering, content sanitization, or antivirus scanning of imported mail — the tool preserves content, it does not vet it.
- Modification of any source
.emlfile, ever.
- Windows 10 or Windows 11.
- Classic Outlook with Extended MAPI (see the next section; New Outlook is not supported).
- Microsoft Visual C++ Redistributable (x86 and x64 — the launcher is
32-bit, the worker may be 64-bit). The binaries link the dynamic CRT
(
/MD); most machines with Office already have the redistributable. A missing-DLL error at startup (VCRUNTIME140.dllor similar) means it needs installing from Microsoft. - No administrator privileges, no network access, and no configured mail account are required.
WLM2PST drives classic Outlook's installed Extended MAPI implementation and Unicode PST provider. It has no PST-writing code of its own.
New Outlook (the Microsoft 365 "one Outlook" app) does not support Extended
MAPI and cannot be used. If only New Outlook is installed, the launcher
exits with OUTLOOK_UNAVAILABLE (exit code 14) and says so explicitly. A
classic Outlook installation (desktop MSI or Click-to-Run "Outlook" that
still exposes MAPI) is required on the machine that runs the conversion. No
mail account needs to be configured in it — WLM2PST creates and uses its own
temporary MAPI profile.
The machine also needs the Microsoft Visual C++ Redistributable (x86 and
x64) matching the build's toolset — the release binaries link the dynamic
CRT (/MD). Most machines with Office already have it; if the tool fails to
start with a missing-DLL error (VCRUNTIME140.dll or similar), install the
redistributable from Microsoft.
Extended MAPI requires the calling process's bitness to match the installed classic Outlook's bitness. The package therefore ships three executables:
wlm2pst.exe 32-bit launcher
wlm2pst-worker-x86.exe Win32 worker
wlm2pst-worker-x64.exe x64 worker
wlm2pst.exe is deliberately built 32-bit so it can run under WOW64 on any
64-bit Windows and inspect both the 32-bit and 64-bit registry views. At
startup it layers several detection signals (Click-to-Run configuration,
Office/MSI bitness keys, App Paths, and finally the OUTLOOK.EXE PE header
itself) to determine which Outlook is installed and how wide it is, then
launches the matching worker with the original command line, forwarding
Ctrl+C and the worker's exit code unchanged. If the matching worker binary is
missing from the install directory, the launcher exits with
BITNESS_MISMATCH (exit code 15) rather than guessing.
Both workers are built from identical source and behave identically; only the target architecture differs.
Basic conversion:
wlm2pst.exe --source "C:\OldMail" --output "D:\Migration\OldMail.pst"Custom root folder name (Hebrew example):
wlm2pst.exe --source "C:\OldMail" --output "D:\Migration\OldMail.pst" --root-name "דואר ישן"Resuming an interrupted job (only valid when both the PST and its matching state database already exist):
wlm2pst.exe --source "C:\OldMail" --output "D:\Migration\OldMail.pst" --resume--source <directory> Required. Recursive source folder containing EML files.
--output <file.pst> Required. Path to a new PST file.
--root-name <name> Optional. Root folder name inside the PST.
Default: Windows Live Mail
--resume Resume a compatible interrupted job.
--overwrite Delete a previous tool-owned output and start again.
--quiet Show only errors and final summary.
--verbose Show additional technical progress without exposing message content.
--help Show help.
--version Show application, worker, architecture, and build versions.
Rules worth knowing:
--sourceand--outputare mandatory for a conversion run.--outputmust end in.pst(case-insensitive), must be a local filesystem path (UNC and network drives are rejected), and must not be inside the source tree (or vice versa).- WLM2PST never overwrites an existing PST unless
--overwriteis given, and--overwriteonly ever deletes files it can prove it created itself (see Output files, below). --resumeand--overwriteare mutually exclusive.
Every run writes its output beside the requested PST path, using the PST's full path as a prefix:
| File | Created | Purpose |
|---|---|---|
<output>.pst |
Always | The Unicode PST itself — the only mail output. |
<output>.wlm2pst-state.sqlite |
Always | Crash-safe resume database: job identity, per-file import status, folder map. Required for --resume and for proving tool-ownership before --overwrite. |
<output>.wlm2pst-report.json |
Always, at end of run | Machine-readable summary: counts, validation status, renamed folders, warnings. |
<output>.wlm2pst.log |
Always | Human-readable run log (privacy-safe fields only; see docs/privacy.md). |
<output>.wlm2pst-errors.csv |
Only if any error or fallback occurred | One row per file that failed to read, failed to convert, or was preserved as a fallback attachment. UTF-8 with BOM so Hebrew paths open correctly in Excel. |
Exit codes are centralized in src/common/exit_codes.h — this table matches
it exactly and is the single source of truth used by the tool.
| Code | Name | Meaning |
|---|---|---|
| 0 | SUCCESS |
Conversion and validation completed with no warnings. |
| 2 | SUCCESS_WITH_FALLBACKS |
Completed and validated; one or more EML files were preserved as fallback attachments instead of being converted. |
| 3 | SUCCESS_WITH_READ_FAILURES |
Completed and validated; one or more source files could not be read at all. |
| 10 | INVALID_ARGUMENTS |
Invalid command-line arguments. |
| 11 | INVALID_SOURCE |
Invalid source directory, or no EML files found. |
| 12 | OUTPUT_EXISTS |
Output already exists and cannot be safely overwritten. |
| 13 | INSUFFICIENT_DISK_SPACE |
Insufficient free disk space for the estimated output. |
| 14 | OUTLOOK_UNAVAILABLE |
Classic Outlook or Extended MAPI is unavailable. |
| 15 | BITNESS_MISMATCH |
Worker and Outlook bitness mismatch (or the matching worker binary is missing). |
| 16 | PST_PROVIDER_FAILURE |
The Unicode PST provider could not be created or configured. |
| 17 | PST_SIZE_CEILING_EXCEEDED |
Estimated output exceeds the safe single-PST ceiling (45 GiB). |
| 18 | OUTPUT_WRITE_FAILURE |
Disk full or output write failure during conversion. |
| 19 | VALIDATION_FAILED |
Post-conversion PST validation failed. |
| 20 | STATE_DB_INCOMPATIBLE |
State database is incompatible with the PST or the current source manifest. |
| 21 | SOURCE_CHANGED |
Source tree changed after the manifest was created. |
| 22 | RESUME_INTEGRITY_ERROR |
Integrity error during resume crash-window recovery (e.g. ambiguous duplicate tool-owned messages). |
| 23 | PROFILE_CLEANUP_FAILURE |
Temporary MAPI profile creation or cleanup failed critically. |
| 130 | CANCELLED |
User cancelled with Ctrl+C. |
WLM2PST processes potentially sensitive personal and business mail. Logs,
the JSON report, and the errors CSV never contain message subjects, bodies,
HTML, sender/recipient addresses, raw MIME headers, or attachment content —
not even at --verbose. They contain only relative paths, target folder
paths, sizes, SHA-256 hashes, statuses, attempt counts, HRESULT/Win32 codes,
timings, counts, and bitness. See docs/privacy.md for the full contract,
including the one documented exception (the fallback message subject
intentionally contains the original filename, by design — see spec section
16 and docs/privacy.md).
- Exactly one PST per run. WLM2PST does not split output across multiple files and will refuse a job whose estimated output exceeds an ~45 GiB ceiling (exit code 17) rather than silently produce an oversized file.
- Duplicates are preserved intentionally. Two distinct source files always become two PST items, even with identical content; the resume system only ever suppresses re-import of the same source file within the same job.
- Every imported message is marked read. EML does not reliably carry Windows Live Mail's per-user read/unread state, so WLM2PST does not guess from timestamps or folder location — it marks everything read on import.
- The filesystem folder tree is the source of truth; WLM2PST does not read
Mail.MSMessageStoreor any other Windows Live Mail database. - Source paths longer than 260 characters are supported without any
machine-wide policy change: the scanner and every per-file open use
extended-length (
\\?\) paths internally, so deeply nested Windows Live Mail trees convert even whenLongPathsEnabledis off. - Windows Live Mail's Inbox/Sent/Drafts are not special-cased into Outlook's built-in folders — they are recreated as ordinary folders under the tool root, matched only by name for sent/draft flag behavior.
Once conversion finishes, open the PST directly — no import wizard is needed:
File -> Open & Export -> Open Outlook Data File
Then browse to the .pst file WLM2PST produced. Outlook attaches it as a
regular data file; the migrated folders appear under the root folder name
you chose (or Windows Live Mail by default).
See BUILDING.md.
See ARCHITECTURE.md.
WLM2PST was not written as a demo. It was built for, and proven on, a real migration of a 26-year law-office mail archive:
- 301,118 unique messages / 67 GB converted into six PSTs in a single run.
- Reconciled against a manifest end to end: every source message is accounted for in a PST — 298,202 converted normally, 2,916 preserved as fallback attachments (genuinely malformed sources), 0 lost.
- Hebrew subjects, bodies, folder names and attachment filenames verified in
Outlook, including
windows-1255and RFC 2047 encoded headers. - Exercised against both a 32-bit and a 64-bit classic Outlook (Microsoft 365 Click-to-Run), on Windows 10 and Windows 11.
- Source files were never modified.
The verification record — including the Click-to-Run converter defect, how it
was diagnosed, and why the tool drives Windows' own MimeOle engine — is in
docs/verification/.
Developed by KALIT — the development arm of Natanor IT Services.
Licensed under the MIT License. Uses the SQLite amalgamation (public domain) and Microsoft's Extended MAPI headers from the MAPI Stub Library (MIT); see THIRD-PARTY-NOTICES.txt.