How to Read MSG Files in C++
Aspose.Email FOSS for C++ lets you open and parse Outlook MSG files with a single
function call. mapi_message::from_file() and mapi_message::from_stream() load the
full message — subject, body, sender, recipients, and attachments — into an in-memory
object with no Outlook or COM dependency.
Step 1 — Set Up the Project
Clone the repository and add it to your CMakeLists.txt:
git clone https://github.com/aspose-email-foss/Aspose.Email-FOSS-for-Cpp.gitadd_subdirectory(Aspose.Email-FOSS-for-Cpp)
target_link_libraries(your_target PRIVATE AsposeEmailFoss::AsposeEmailFoss)Step 2 — Include the Header
#include "aspose/email/foss/msg/mapi_message.hpp"This single header provides mapi_message and all supporting types in the
aspose::email::foss::msg namespace.
Step 3 — Load a Message from a File Path
Use mapi_message::from_file() when you have 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';
}Step 4 — Load a Message from a Stream
Use mapi_message::from_stream() to parse from any std::istream — useful for
processing MSG data received over a network or from an in-memory buffer:
#include <fstream>
#include <iostream>
#include "aspose/email/foss/msg/mapi_message.hpp"
int main()
{
std::ifstream input("sample.msg", std::ios::binary);
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 to prevent Windows line-ending
translation from corrupting the CFB container.
Step 5 — Iterate Recipients
message.recipients() returns a std::vector of recipient objects, each with
display_name, email_address, and recipient_type fields:
#include <fstream>
#include <iostream>
#include "aspose/email/foss/msg/mapi_message.hpp"
int main()
{
std::ifstream input("sample.msg", std::ios::binary);
auto message = aspose::email::foss::msg::mapi_message::from_stream(input);
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';
}
}Step 6 — Iterate Attachments
message.attachments() returns a std::vector of attachment objects. Each attachment
exposes filename, mime_type, data (the raw bytes), content_id, and the
is_embedded_message() predicate:
#include <fstream>
#include <iostream>
#include "aspose/email/foss/msg/mapi_message.hpp"
int main()
{
std::ifstream input("sample.msg", std::ios::binary);
auto message = aspose::email::foss::msg::mapi_message::from_stream(input);
for (std::size_t i = 0; i < message.attachments().size(); ++i)
{
const auto& a = message.attachments()[i];
std::cout << "[" << (i + 1) << "] "
<< a.filename << " mime=" << a.mime_type
<< " bytes=" << a.data.size()
<< " embedded=" << (a.is_embedded_message() ? "yes" : "no")
<< '\n';
}
}Common Issues and Fixes
Empty body() after loading. Some messages store text only in the HTML body
property. Check message.html_body() when message.body() returns an empty string.
Stream opened without std::ios::binary. On Windows, text-mode streams translate
CRLF sequences, silently corrupting binary CFB data. Always open MSG files with
std::ios::binary.
msg_exception thrown for a valid-looking file. Verify the file is a genuine MSG
(OLE Compound File). Files saved by some export tools may be plain text or MIME and
cannot be parsed by mapi_message::from_file().
recipients() returns an empty vector. Older MSG files sometimes omit the recipient
table but store display recipients in the display_to MAPI property. Access it via
message.get_property_value() with
common_message_property_id::display_to and property_type_code::ptyp_string.
Large attachment data in memory. mapi_message::from_file() and from_stream()
load the entire message including all attachment bytes. For large files, open the
underlying container with msg_reader and inspect attachment streams without fully
materialising them.
Frequently Asked Questions
What is the difference between from_file() and from_stream()?
Both produce an identical mapi_message object. from_file() accepts a
std::filesystem::path and opens the file internally. from_stream() reads from
any std::istream you provide — useful when the MSG data is already in memory or
arriving over a socket.
Does from_file() keep the file open after loading?
No. The file is opened, fully parsed into memory, and closed before from_file()
returns. You can move or delete the source file immediately after the call.
Can I read MSG files created by Outlook on macOS or Linux?
Yes. The MSG format (OLE Compound File + MAPI properties) is portable across
operating systems. Any .msg file produced by any version of Outlook can be opened
with mapi_message::from_file().
How do I read the HTML body?
Call message.html_body(). If the message was created with only a plain-text body,
this returns an empty string. message.body() returns the plain-text body.
Is strict mode the default?
mapi_message::from_file(path, strict) and from_stream(stream, strict) accept an
optional strict boolean. The default is false, which tolerates minor structural
deviations in real-world MSG files.