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.hmsoutl.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 小工具将显示 Outlook 联系人文件夹中的内容,这些内容由model 提供。

#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 报告的条目数。columnCountheaderData 实现在树形视图中显示四列。

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 项目使用 Outlook API 访问,并使用属性设置器修改。最后,项目被保存到 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

© 2025 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.