Введение
Мне была поставлена задача разработать некий ActiveX-control. Так как основным языком программирования для разработки у нас используется C++, то C# не рассматривался. Я решил выбрать Qt, так как он мне интересен.
Создание ActiveX объектов на Qt достаточно простой процесс, в примерах к QtCreator есть несколько вариантов, показывающих как можно использовать ActiveQt (например этот).
При написании компонента пришлось много времени потратить на поиск ответов на казалось бы простые вопросы, по крупицам их собирать. В результате я получил, что требовалось и решил написать простой пример, чтобы ускорить процесс старта разработки ActiveX-control другим.
Сразу обращу внимание, что не описываю всю технологию ActiveQt, подробную информацию можно получить в документации Qt Assistant и в интернете (например здесь), это пример и пара интересных на мой взгляд моментов.
Начало
Итак, для начала устанавливаем QtSDK (я выбрал коммерческую версию в связке с MS VisualStudio 2010).
Создаем пустой проект. В качестве примера создадим ActiveX-control с цифровыми часами (возьмем пример отсюда).
Добавляем в наш проект класс цифровых часов.
Добавляем файл main.cpp, в нем создадим класс, унаследованный от QAxFactory. Этот класс реализовывает фабрику, предоставляющую информацию об элементах управления и создает их по запросу.
#include <QAxFactory>
#include "clock.h" // заголовочный файл класса цифровых часов
class ActiveQtFactory : public QAxFactory
{
public:
ActiveQtFactory( const QUuid &lib, const QUuid &app )
: QAxFactory( lib, app )
{}
// список импортируемых классов
QStringList featureList() const
{
QStringList list;
// если классов будет несколько, то нужно будет добавить в список имя каждого
// в данном пример только один "Clock"
list << "Clock";
return list;
}
// создание объекта экспортируемого класса
QObject *createObject(const QString &key)
{
if ( key == "Clock" )
return new Clock();
// аналогично для каждого класса
return 0;
}
// получение мета-информации о классе
const QMetaObject *metaObject( const QString &key ) const
{
if ( key == "Clock" )
return &Clock::staticMetaObject;
// аналогично для каждого класса
return 0;
}
};
// экспорт фабрики
QAXFACTORY_EXPORT( ActiveQtFactory, "{c1de5776-a143-4884-89fc-81a06d04e87d}", "{11403913-dc94-484a-af5a-521f0e93d2ee}" )
Если захотим в библиотеку добавить другие классы, то нужно подключить их заголовочные файлы и добавить описание в класс ActiveQtFactory
Теперь доработаем класс Clock для экспортирования его метаданных:
В заголовок добавим макрос для динамической библиотеки
// ...........
#include <qglobal.h>
#if defined(Clock_LIBRARY)
# define Clock_LIBRARY Q_DECL_EXPORT
#else
# define Clock_LIBRARY Q_DECL_IMPORT
#endif
class Clock_LIBRARY Clock : public QLCDNumber
{
// ...........
Добавим информацию о классе
// ..............
Q_OBJECT
Q_CLASSINFO("ClassID", "{1edd41d0-e01f-445d-9b4e-78c99ab93acf}")
Q_CLASSINFO("InterfaceID", "{8adccb5c-567e-42f6-8b81-f6634409fb1a}")
Q_CLASSINFO("EventsID", "{f0a4474f-8c0c-4cdf-985d-8379b26bdd19}")
// ..............
Для каждого класса необходимо указывать свои ClassID, InterfaceID, EventsID.
Заключительный момент, скорректируем файл проекта
TEMPLATE = lib
CONFIG += qt qaxserver dll
contains(CONFIG, static):DEFINES += QT_NODLL
SOURCES = main.cpp
clock.cpp
HEADERS +=
clock.h
DEF_FILE = qaxserver.def
DEFINES += clock_LIBRARY
VERSION = 0.0.0.1
# Подключаем заголовочные файлы библиотеки
INCLUDEPATH += clock
TARGET = clock
Компилируем, получаем библиотеку.
Это все описано в документации, теперь несколько дополнений, ради которых собственно я и решил написать статью.
Добавить свойство
Необходимо добавить экспортное свойство класса. Для примера возьмем некое name;
Доработаем класс Clock
// .................
Q_OBJECT
Q_CLASSINFO("ClassID", "{1edd41d0-e01f-445d-9b4e-78c99ab93acf}")
Q_CLASSINFO("InterfaceID", "{8adccb5c-567e-42f6-8b81-f6634409fb1a}")
Q_CLASSINFO("EventsID", "{f0a4474f-8c0c-4cdf-985d-8379b26bdd19}")
// добавим свойство name
Q_PROPERTY(QString name READ getName WRITE setName)
public:
// функция получения свойства
QString getName()const
{
if(name.isEmpty())
return "Clock";
else
return name;
}
// функция установки свойства
void setName(const QString &inName){name = inName;}
private:
QString name;
// .......
Добавить метод
Методы добавляются через публичные слоты в классе Clock:
// .......
public slots:
void function(int a);
QString functionb(const QString &b);
// .......
Добавить событие
Событие, реакцию на которое вы хотите реализовать в алгоритме использующем AXControl, добавляется через сигналы, ниже пример, правда из другого класса:
// в классе создаем сигнал
signals:
void mouseDbClick(int x, int y);
// переопределяем событие
protected:
void mouseDoubleClickEvent(QMouseEvent *event)
{
QPoint pos = event->pos();
emit mouseDbClick(pos.rx(), pos.ry());
}
Заключение
Библиотека Qt отличная вещь, может очень многое, нужно учится ее готовить.
Отдельная благодарность автору статьи о создании динамических библиотек Qt.
Полезные ресурсы, которые помогли в решении задачи doc.crossplatform.ru/qt и qt-project.org
Автор: theshadowco