Reading MSG Files
Reading MSG Files
mapi_message is the entry point for loading and parsing Outlook MSG files. A single
call to from_file() or from_stream() reads the full CFB container, extracts all MAPI
properties, and exposes them through typed accessor methods — no Outlook installation
required.
Load from a File Path
Use mapi_message::from_file() with a std::filesystem::path:
#include <filesystem>
#include <iostream>
#include "aspose/email/foss/msg/mapi_message.hpp"
int main()
{
const auto message = aspose::email::foss::msg::mapi_message::from_file(
std::filesystem::path("sample.msg"));
std::cout << "Subject: " << message.subject() << '\n';
std::cout << "From: " << message.sender_name()
<< " <" << message.sender_email_address() << ">\n";
std::cout << "Body: " << message.body() << '\n';
}Load from a Stream
Use mapi_message::from_stream() when MSG data arrives from a network socket, an
archive, or any std::istream:
#include <fstream>
#include <iostream>
#include "aspose/email/foss/msg/mapi_message.hpp"
int main()
{
std::ifstream input("sample.msg", std::ios::binary);
const auto message = aspose::email::foss::msg::mapi_message::from_stream(input);
std::cout << "Subject: " << message.subject() << '\n';
}Always open the stream with std::ios::binary. Text-mode streams translate line
endings on Windows and corrupt the CFB container.
Access Message Properties
mapi_message exposes the most common message properties as named getters:
#include <filesystem>
#include <iostream>
#include "aspose/email/foss/msg/mapi_message.hpp"
int main()
{
const auto message = aspose::email::foss::msg::mapi_message::from_file(
std::filesystem::path("sample.msg"));
// Core properties
std::cout << "Subject: " << message.subject() << '\n';
std::cout << "Sender name: " << message.sender_name() << '\n';
std::cout << "Sender email: " << message.sender_email_address() << '\n';
std::cout << "Internet msg-id: " << message.internet_message_id() << '\n';
std::cout << "Message class: " << message.message_class() << '\n';
// Body — check html_body() first; fall back to plain body()
const auto& body = message.html_body().empty()
? message.body()
: message.html_body();
std::cout << "Body length: " << body.size() << " chars\n";
}Iterate Recipients
message.recipients() returns a std::vector of recipient objects with display_name,
email_address, and recipient_type:
#include <filesystem>
#include <iostream>
#include "aspose/email/foss/msg/mapi_message.hpp"
int main()
{
const auto message = aspose::email::foss::msg::mapi_message::from_file(
std::filesystem::path("sample.msg"));
for (std::size_t i = 0; i < message.recipients().size(); ++i)
{
const auto& r = message.recipients()[i];
std::cout << "[" << (i + 1) << "] "
<< r.display_name << " <" << r.email_address << ">"
<< " type=" << r.recipient_type << '\n';
}
}Iterate Attachments
message.attachments() returns a std::vector of attachment objects. Each exposes
filename, mime_type, data, content_id, and is_embedded_message():
#include <filesystem>
#include <fstream>
#include <iostream>
#include "aspose/email/foss/msg/mapi_message.hpp"
int main()
{
const auto message = aspose::email::foss::msg::mapi_message::from_file(
std::filesystem::path("sample.msg"));
for (const auto& a : message.attachments())
{
std::cout << a.filename
<< " " << a.mime_type
<< " " << a.data.size() << " bytes"
<< " embedded=" << (a.is_embedded_message() ? "yes" : "no")
<< '\n';
if (!a.is_embedded_message())
{
std::ofstream out("extracted_" + a.filename, std::ios::binary);
out.write(reinterpret_cast<const char*>(a.data.data()),
static_cast<std::streamsize>(a.data.size()));
}
}
}Access Embedded Messages
When is_embedded_message() is true, the attachment contains another MSG file.
Access it via attachment.embedded_message:
#include <filesystem>
#include <iostream>
#include "aspose/email/foss/msg/mapi_message.hpp"
int main()
{
const auto message = aspose::email::foss::msg::mapi_message::from_file(
std::filesystem::path("sample.msg"));
for (const auto& a : message.attachments())
{
if (a.is_embedded_message() && a.embedded_message != nullptr)
{
const auto& inner = *a.embedded_message;
std::cout << "Embedded subject: " << inner.subject() << '\n';
inner.save(std::filesystem::path("embedded.msg"));
}
}
}Tips and Best Practices
- Always pass
std::ios::binarytostd::ifstreamwhen loading MSG files. - Check
html_body()beforebody()— HTML-only messages store their content in the HTML body property and leavebody()empty. from_file()loads all attachment data into memory. For large MSG files, consider opening the underlying container withmsg_readerto inspect metadata without fully materialising attachment bytes.- The
strictparameter offrom_file(path, strict)andfrom_stream(stream, strict)defaults tofalse, tolerating minor structural deviations in real-world MSG files. - Recipient objects expose
recipient_typeto distinguish To, Cc, and Bcc entries.
Common Issues
| Issue | Cause | Fix |
|---|---|---|
msg_exception on a valid-looking file | File is not a genuine OLE CFB container | Verify with cfb_reader::from_file() — if it also throws, the file is not a valid MSG |
body() returns empty string | Message uses HTML body only | Use html_body() instead |
recipients() is empty | Older MSG without a recipient table | Access display_to via get_property_value() with common_message_property_id::display_to |
| Stream corruption on Windows | Stream opened without std::ios::binary | Add std::ios::binary flag to std::ifstream |
| High memory usage for large files | All attachment data is loaded upfront | Use msg_reader for low-level access without full materialisation |
FAQ
Does from_file() keep the file handle open?
No. The file is opened, fully parsed, and closed before the method returns. You can move or delete the source file after the call.
How do I tell if an attachment is a file or an embedded MSG?
Call attachment.is_embedded_message(). Returns true for embedded MSG files;
false for regular file attachments. For embedded messages, access the nested object
via attachment.embedded_message.
What is the difference between from_file() and using msg_reader?
mapi_message::from_file() is the high-level API — it parses the full message and
exposes properties as typed fields. msg_reader is the low-level reader that gives
access to the raw CFB container structure and is useful when you need to inspect
message layout without parsing all MAPI properties.
Are Unicode and ANSI MSG files both supported?
Yes. mapi_message handles both Unicode (property type ptyp_string) and ANSI
(ptyp_string8) string properties. unicode_strings() returns true for messages
stored with Unicode properties.
API Reference Summary
| Class / Method | Description |
|---|---|
mapi_message::from_file(path) | Load an MSG file from a std::filesystem::path |
mapi_message::from_file(path, strict) | Load with strict mode option |
mapi_message::from_stream(stream) | Load MSG from an std::istream |
mapi_message::from_stream(stream, strict) | Load from stream with strict mode option |
mapi_message::subject() | Return the message subject string |
mapi_message::body() | Return the plain-text body |
mapi_message::html_body() | Return the HTML body (empty if not present) |
mapi_message::sender_name() | Return the sender display name |
mapi_message::sender_email_address() | Return the sender email address |
mapi_message::internet_message_id() | Return the RFC 2822 Message-ID header |
mapi_message::message_class() | Return the MAPI message class string |
mapi_message::recipients() | Return the recipient list |
mapi_message::attachments() | Return the attachment list |
mapi_attachment::is_embedded_message() | True if attachment is an embedded MSG |
mapi_message::unicode_strings() | True if message uses Unicode string properties |