Qutlook 示例(ActiveQt)

Qutlook 示例演示了如何使用 ActiveQt 实现 Outlook 的自动化。该示例利用dumpcpp工具,为描述 Outlook 对象模型的类型库生成一个 C++ 命名空间。

该示例的项目文件如下所示:

TEMPLATE = app
TARGET   = qutlook
QT += widgets axcontainer

TYPELIBS = $$system(dumpcpp -getfile {00062FFF-0000-0000-C000-000000000046})

isEmpty(TYPELIBS) {
    message("Microsoft Outlook type library not found!")
    REQUIRES += Outlook
} else {
    HEADERS  = addressview.h
    SOURCES  = addressview.cpp main.cpp
}

该项目文件使用dumpcpp 工具将MS Outlook类型库添加到项目中。 如果此操作失败,生成的 makefile 只会输出一条错误信息;否则,构建步骤将对类型库运行dumpcpp工具,并生成一个头文件和一个 cpp 文件(在本例中分别为msoutl.h 和msoutl.cpp ),用于声明并实现一个便于使用的 Outlook 对象 API。

class AddressView : public QWidget
{
    Q_OBJECT

public:
    explicit AddressView(QWidget *parent = nullptr);

protected slots:
    void addEntry();
    void changeEntry();
    void itemSelected(const QModelIndex &index);

    void updateOutlook();

protected:
    AddressBookModel *model;

    QTreeView *m_treeView;
    QPushButton *m_addButton;
    QPushButton *m_changeButton;
    QLineEdit *m_firstName;
    QLineEdit *m_lastName;
    QLineEdit *m_address;
    QLineEdit *m_email;
};

AddressView 类是用于用户界面的QWidget 子类。QTreeView 控件将显示由model 提供的 Outlook“联系人”文件夹的内容。

#include "addressview.h"
#include "msoutl.h"
#include <QtWidgets>

class AddressBookModel : public QAbstractListModel
{
public:
    explicit AddressBookModel(AddressView *parent);
    virtual ~AddressBookModel();

    int rowCount(const QModelIndex &parent = QModelIndex()) const;
    int columnCount(const QModelIndex &parent) const;
    QVariant headerData(int section, Qt::Orientation orientation, int role) const;
    QVariant data(const QModelIndex &index, int role) const;

    void changeItem(const QModelIndex &index, const QString &firstName, const QString &lastName, const QString &address, const QString &email);
    void addItem(const QString &firstName, const QString &lastName, const QString &address, const QString &email);
    void update();

private:
    Outlook::Application outlook;
    Outlook::Items *folderItems = nullptr;

    mutable QHash<QModelIndex, QStringList> cache;
};

AddressBookModel 类是QAbstractListModel 的子类,它直接与 Outlook 通信,并使用QHash 进行缓存。

AddressBookModel::AddressBookModel(AddressView *parent)
: QAbstractListModel(parent)
{
    if (!outlook.isNull()) {
        Outlook::NameSpace session(outlook.Session());
        session.Logon();
        Outlook::MAPIFolder *folder = session.GetDefaultFolder(Outlook::olFolderContacts);
        folderItems = new Outlook::Items(folder->Items());
        connect(folderItems, SIGNAL(ItemAdd(IDispatch*)), parent, SLOT(updateOutlook()));
        connect(folderItems, SIGNAL(ItemChange(IDispatch*)), parent, SLOT(updateOutlook()));
        connect(folderItems, SIGNAL(ItemRemove()), parent, SLOT(updateOutlook()));

        delete folder;
    }
}

构造函数负责初始化 Outlook。Outlook 提供的用于通知内容变化的各种信号均连接到updateOutlook() 的插槽上。

AddressBookModel::~AddressBookModel()
{
    delete folderItems;

    if (!outlook.isNull())
        Outlook::NameSpace(outlook.Session()).Logoff();
}

析构函数会从会话中注销。

int AddressBookModel::rowCount(const QModelIndex &) const
{
    return folderItems ? folderItems->Count() : 0;
}

int AddressBookModel::columnCount(const QModelIndex & /*parent*/) const
{
    return 4;
}

rowCount() 的实现返回 Outlook 报告的条目数量。columnCount 和headerData 的实现用于在树视图中显示四列。

QVariant AddressBookModel::headerData(int section, Qt::Orientation /*orientation*/, int role) const
{
    if (role != Qt::DisplayRole)
        return QVariant();

    switch (section) {
    case 0:
        return tr("First Name");
    case 1:
        return tr("Last Name");
    case 2:
        return tr("Address");
    case 3:
        return tr("Email");
    default:
        break;
    }

    return QVariant();
}

headerData() 的实现返回硬编码的字符串。

QVariant AddressBookModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid() || role != Qt::DisplayRole)
        return QVariant();

    QStringList data;
    if (cache.contains(index)) {
        data = cache.value(index);
    } else {
        Outlook::ContactItem contact(folderItems->Item(index.row() + 1));

        if (contact.Class() == Outlook::OlObjectClass::olContact)
            data << contact.FirstName() << contact.LastName() << contact.HomeAddress() << contact.Email1Address();

        cache.insert(index, data);
    }

    if (index.column() < data.size())
        return data.at(index.column());

    return QVariant();
}

data() 的实现是该模型的核心。如果请求的数据在缓存中,则使用缓存值;否则,数据将从 Outlook 中获取。

void AddressBookModel::changeItem(const QModelIndex &index, const QString &firstName, const QString &lastName, const QString &address, const QString &email)
{
    Outlook::ContactItem item(folderItems->Item(index.row() + 1));

    if (item.Class() != Outlook::OlObjectClass::olContact)
        return; // Not a contact

    item.SetFirstName(firstName);
    item.SetLastName(lastName);
    item.SetHomeAddress(address);
    item.SetEmail1Address(email);

    item.Save();

    cache.take(index);
}

当用户通过用户界面更改当前条目时,会调用changeItem() 插槽。通过Outlook API访问Outlook项目,并使用属性设置器对其进行修改。最后,将该项目保存到Outlook中,并从缓存中移除。请注意,模型不会向视图发出数据更改信号,因为Outlook会自行发出信号。

void AddressBookModel::addItem(const QString &firstName, const QString &lastName, const QString &address, const QString &email)
{
    Outlook::ContactItem item(outlook.CreateItem(Outlook::olContactItem));
    if (!item.isNull()) {
        item.SetFirstName(firstName);
        item.SetLastName(lastName);
        item.SetHomeAddress(address);
        item.SetEmail1Address(email);

        item.Save();
    }
}

addItem() 插槽会调用 Outlook 的 CreateItem 方法来创建一个新的联系人项目,将新项目的属性设置为用户输入的值,并保存该项目。

void AddressBookModel::update()
{
    beginResetModel();
    cache.clear();
    endResetModel();
}

update() 插槽会清空缓存,并发出 reset() 信号,以通知视图数据已发生变化,需要重新绘制内容。

AddressView::AddressView(QWidget *parent)
: QWidget(parent)
{
    QGridLayout *mainGrid = new QGridLayout(this);

    QLabel *firstNameLabel = new QLabel(tr("First &Name"), this);
    firstNameLabel->resize(firstNameLabel->sizeHint());
    mainGrid->addWidget(firstNameLabel, 0, 0);

    QLabel *lastNameLabel = new QLabel(tr("&Last Name"), this);
    lastNameLabel->resize(lastNameLabel->sizeHint());
    mainGrid->addWidget(lastNameLabel, 0, 1);

    QLabel *addressLabel = new QLabel(tr("Add&ress"), this);
    addressLabel->resize(addressLabel->sizeHint());
    mainGrid->addWidget(addressLabel, 0, 2);

    QLabel *emailLabel = new QLabel(tr("&E-Mail"), this);
    emailLabel->resize(emailLabel->sizeHint());
    mainGrid->addWidget(emailLabel, 0, 3);

    m_addButton = new QPushButton(tr("A&dd"), this);
    m_addButton->resize(m_addButton->sizeHint());
    mainGrid->addWidget(m_addButton, 0, 4);
    connect(m_addButton, &QPushButton::clicked, this, &AddressView::addEntry);

    m_firstName = new QLineEdit(this);
    m_firstName->resize(m_firstName->sizeHint());
    mainGrid->addWidget(m_firstName, 1, 0);
    firstNameLabel->setBuddy(m_firstName);

    m_lastName = new QLineEdit(this);
    m_lastName->resize(m_lastName->sizeHint());
    mainGrid->addWidget(m_lastName, 1, 1);
    lastNameLabel->setBuddy(m_lastName);

    m_address = new QLineEdit(this);
    m_address->resize(m_address->sizeHint());
    mainGrid->addWidget(m_address, 1, 2);
    addressLabel->setBuddy(m_address);

    m_email = new QLineEdit(this);
    m_email->resize(m_email->sizeHint());
    mainGrid->addWidget(m_email, 1, 3);
    emailLabel->setBuddy(m_email);

    m_changeButton = new QPushButton(tr("&Change"), this);
    m_changeButton->resize(m_changeButton->sizeHint());
    mainGrid->addWidget(m_changeButton, 1, 4);
    connect(m_changeButton, &QPushButton::clicked, this, &AddressView::changeEntry);

    m_treeView = new QTreeView(this);
    m_treeView->setSelectionMode(QTreeView::SingleSelection);
    m_treeView->setRootIsDecorated(false);

    model = new AddressBookModel(this);
    m_treeView->setModel(model);

    connect(m_treeView->selectionModel(), &QItemSelectionModel::currentChanged, this, &AddressView::itemSelected);

    mainGrid->addWidget(m_treeView, 2, 0, 1, 5);
}

void AddressView::updateOutlook()
{
    model->update();
}

void AddressView::addEntry()
{
    if (!m_firstName->text().isEmpty() || !m_lastName->text().isEmpty() ||
         !m_address->text().isEmpty() || !m_email->text().isEmpty()) {
        model->addItem(m_firstName->text(), m_lastName->text(), m_address->text(), m_email->text());
    }

    m_firstName->clear();
    m_lastName->clear();
    m_address->clear();
    m_email->clear();
}

void AddressView::changeEntry()
{
    QModelIndex current = m_treeView->currentIndex();

    if (current.isValid())
        model->changeItem(current, m_firstName->text(), m_lastName->text(), m_address->text(), m_email->text());
}

void AddressView::itemSelected(const QModelIndex &index)
{
    if (!index.isValid())
        return;

    QAbstractItemModel *model = m_treeView->model();
    m_firstName->setText(model->data(model->index(index.row(), 0)).toString());
    m_lastName->setText(model->data(model->index(index.row(), 1)).toString());
    m_address->setText(model->data(model->index(index.row(), 2)).toString());
    m_email->setText(model->data(model->index(index.row(), 3)).toString());
}

该文件的其余部分仅使用 Qt API 来实现用户界面,即不与 Outlook 直接通信。

#include "addressview.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    AddressView view;
    view.setWindowTitle(QObject::tr("Qt Example - Looking at Outlook"));
    view.show();

    return a.exec();
}

main() 入口点函数最终会实例化用户界面并进入事件循环。

要构建该示例,您必须先构建QAxContainer 库。然后在examples/activeqt/qutlook 目录下运行您的 make 工具,并运行生成的qutlook.exe 程序。

示例项目 @ code.qt.io

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.