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::binary to std::ifstream when loading MSG files.
  • Check html_body() before body() — HTML-only messages store their content in the HTML body property and leave body() empty.
  • from_file() loads all attachment data into memory. For large MSG files, consider opening the underlying container with msg_reader to inspect metadata without fully materialising attachment bytes.
  • The strict parameter of from_file(path, strict) and from_stream(stream, strict) defaults to false, tolerating minor structural deviations in real-world MSG files.
  • Recipient objects expose recipient_type to distinguish To, Cc, and Bcc entries.

Common Issues

IssueCauseFix
msg_exception on a valid-looking fileFile is not a genuine OLE CFB containerVerify with cfb_reader::from_file() — if it also throws, the file is not a valid MSG
body() returns empty stringMessage uses HTML body onlyUse html_body() instead
recipients() is emptyOlder MSG without a recipient tableAccess display_to via get_property_value() with common_message_property_id::display_to
Stream corruption on WindowsStream opened without std::ios::binaryAdd std::ios::binary flag to std::ifstream
High memory usage for large filesAll attachment data is loaded upfrontUse 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 / MethodDescription
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