Жасмин Бланшет - QT 4: программирование GUI на С++
Устанавливающие свойства функции setSpeed() и setRadius() следуют этому же образцу, и так же работают слоты start() и stop(), поскольку они изменяют значение свойства running (приложение выполняется).
Осталось рассмотреть еще одну интересную функцию—член класса AxBouncer:
QAxAggregated *AxBouncer::createAggregate()
{
return new ObjectSafetyImpl;
}
Функция createAggregate() класса QAxBindable переопределяется. Она позволяет нам реализовать интерфейсы СОМ, которые модуль QAxServer еще не реализовал, или обойти определенные по умолчанию в QAxServer интерфейсы СОМ. Ниже мы делаем это для обеспечения интерфейса IObjectSafety, который используется в Internet Explorer для доступа к свойствам безопасности компонента. Это является стандартным способом устранения непопулярного сообщения об ошибке «Object not safe for scripting» (объект небезопасен при использовании в сценарии) в Internet Explorer.
Ниже приводится определение класса, которое реализует интерфейс IObjectSafety:
01 class ObjectSafetyImpl : public QAxAggregated, public IObjectSafety
02 {
03 public:
04 long queryInterface(const QUuid &iid, void **iface);
05 QAXAGG_IUNKNOWN
06 HRESULT WINAPI GetInterfaceSafetyOptions(REFIID riid,
07 DWORD *pdwSupportedOptions, DWORD *pdwEnabledOptions);
08 HRESULT WINAPI SetInterfaceSafetyOptions(REFIID riid,
09 DWORD pdwSupportedOptions, DWORD pdwEnabledOptions);
10 };
Класс ObjectSafetyImpl наследует как QAxAggregated, так и IObjectSafety. Класс QAxAggregated является абстрактным базовым классом, предназначенным для реализации дополнительных интерфейсов СОМ. Объект СОМ, который расширяет QAxAggregated, доступен при помощи функции controllingUnknown(). Этот объект СОМ создается незаметно для пользователя модулем QAxServer.
Макрос QAXAGG_IUNKNOWN обеспечивает стандартную реализацию функций QueryInterface(), AddRef() и Release(). В этих реализациях просто делается вызов одноименных функций для управляющего объекта СОМ.
01 long ObjectSafetyImpl::queryInterface(const QUuid &iid, void **iface)
02 {
03 *iface = 0;
04 if (iid == IID_IObjectSafety) {
05 *iface = static_cast<IObjectSafety *>(this);
06 } else {
07 return E_NOINTERFACE;
08 }
09 AddRef();
10 return S_OK;
11 }
Функция queryInterface() — чистая виртуальная функция класса QAxAggregated. Она вызывается управляющим объектом СОМ для предоставления доступа к интерфейсу, который обеспечивается подклассом QAxAggregated. Мы должны возвращать E_NOINTERFACE для интерфейсов, которые мы не определили, и также для IUnknown.
01 HRESULT WINAPI ObjectSafetyImpl::GetInterfaceSafetyOptions(
02 REFIID /* riid */, DWORD *pdwSupportedOptions,
03 DWORD *pdwEnabledOptions)
04 {
05 *pdwSupportedOptions =
06 INTERFACESAFE_FOR_UNTRUSTED_DATA
07 | INTERFACESAFE_FOR_UNTRUSTED_CALLER;
08 *pdwEnabledOptions = *pdwSupportedOptions;
09 return S_OK;
10 }
11 HRESULT WINAPI ObjectSafetyImpl::SetInterfaceSafetyOptions(
12 REFIID /* riid */, DWORD /* pdwSupportedOptions */,
13 DWORD /* pdwEnabledOptions */)
14 {
15 return S_OK;
16 }
Функции GetInterfaceSafetyOptions() и SetInterfaceSafetyOptions() объявляются в IObjectSafety. Мы реализуем их, чтобы уведомить всех о том, что наш объект безопасен для использования в сценариях.
Давайте теперь рассмотрим main.cpp:
01 #include <QAxFactory>
02 #include "axbouncer.h"
03 QAXFACTORY_DEFAULT(AxBouncer,
04 "{5e2461aa-a3e8-4f7a-8b04-307459a4c08c}",
05 "{533af11f-4899-43de-8b7f-2ddf588d1015}",
06 "{772c14a5-a840-4023-b79d-19549ece0cd9}",
07 "{dbce1e56-70dd-4f74-85e0-95c65d86254d}",
08 "{3f3db5e0-78ff-4e35-8a5d-3d3b96c83e09}")
Макрос QAXFACTORY_DEFAULT() экспортирует элемент управления ActiveX. Мы можем использовать его для серверов ActiveX, которые экспортируют только один элемент управления. В следующем примере данного раздела будет показано, как можно экспортировать много элементов управления ActiveX.
Первым аргументом макроса QAXFACTORY_DEFAULT() является имя экспортируемого класса Qt. Такое же имя используется для экспорта элемента управления. Остальные пять аргументов следующие: идентификатор класса, идентификатор интерфейса, идентификатор интерфейса событий, идентификатор библиотеки типов и идентификатор приложения. Мы можем использовать стандартные инструментальные средства, например guidgen или uuidgen, для получения этих идентификаторов. Поскольку сервер реализован в виде библиотеки, нам не требуется иметь функцию main().
Ниже приводится файл .pro для внутрипроцессного сервера ActiveX:
TEMPLATE = lib
CONFIG += dll qaxserver
HEADERS = axbouncer.h
objectsafetyimpl.h
SOURCES = axbouncer.cpp
main.cpp
objectsafetyimpl.cpp
RC_FILE = qaxserver.rc
DEF_FILE = qaxserver.def
Файлы qaxserver.rc и qaxserver.def, на которые имеются ссылки в файле .pro, —стандартные файлы, которые можно скопировать из каталога Qt srcactiveqtcontrol.
Файл makefile или сгенерированный утилитой qmake файл проекта Visual С++ содержат правила для регистрации сервера в реестре Windows. Для регистрации сервера на машине пользователя мы можем использовать утилиту regsvr32, которая имеется во всех системах Windows.
Мы можем затем включить компонент Bouncer в страницу HTML, используя тег <object>:
<object id="AxBouncer"
classid="clsid:5e2461aa-a3e8-4f7a-8b04-307459a4c08c">
<b>The ActiveX control is not available. Make sure you
have built and registered the component server.</b>
</object>
Мы можем создать кнопку для вызова слотов:
<input type="button" value="Start" onClick="AxBouncer.start()">
<input type="button" value="Stop" onClick="AxBouncer.stop()">
Мы можем манипулировать виджетом при помощи языков JavaScript или VBScript точно так же, как и любым другим элементом управления ActiveX (см. расположенный на компакт-диске файл demo.html, содержащий очень простую страницу, в которой используется сервер ActiveX.
Наш последний пример — приложение Address Book (адресная книга), применяющее сценарий. Это приложение может рассматриваться в качестве стандартного приложения Qt для Windows или внепроцессного сервера ActiveX. В последнем случае мы можем создавать сценарий работы приложения, используя, например, Visual Basic.
01 class AddressBook : public QMainWindow
02 {
03 Q_OBJECT
04 Q_PROPERTY(int count READ count)
05 Q_CLASSINFO("ClassID",
06 "{588141ef-110d-4beb-95ab-ee6a478b576d}")
07 Q_CLASSINFO("InterfaceID",
08 "{718780ec-b30c-4d88-83b3-79b3d9e78502}")
09 Q_CLASSINFO("ToSuperClass", "AddressBook")
10 public:
11 AddressBook(QWidget *parent = 0);
12 ~AddressBook();
13 int count() const;
14 public slots:
15 ABItem *createEntry(const QString &contact);
16 ABItem *findEntry(const QString &contact) const;
17 ABItem *entryAt(int index) const;
18 private slots:
19 void addEntry();
20 void editEntry();
21 void deleteEntry();
22 private:
23 void createActions();
24 void createMenus();
25 QTreeWidget *treeWidget;
26 QMenu *fileMenu;
27 QMenu *editMenu;
28 QAction *exitAction;
29 QAction *addEntryAction;
30 QAction *editEntryAction;
31 QAction *deleteEntryAction;
32 };
Виджет AddressBook является главным окном приложения. Предоставляемые им свойства и слоты можно применять при создании сценария. Макрос Q_CLASSINFO() используется для определения идентификаторов класса и интерфейсов, связанных с классом. Они генерируются с помощью таких утилит, как guid или uuid.
В предыдущем примере мы определяли идентификаторы класса и интерфейса при экспорте класса QAxBouncer, используя макрос QAXFACTORY_DEFAULT(). В этом примере мы хотим экспортировать несколько классов, поэтому нельзя использовать макрос QAXFACTORY_DEFAULT(). Мы можем поступать двумя способами:
• можно создать подкласс QAxFactory, переопределить его виртуальные функции для представления информации об экспортируемых нами типах и использовать макрос QAXFACTORY_EXPORT() для регистрации фабрики классов;
• можно использовать макросы QAXFACTORY_BEGIN(), QAXFACTORY_END(), QAXCLASS() и QAXTYPE() для объявления и регистрации фабрики классов. В этом случае потребуется использовать макрос Q_CLASSINFO() для определения идентификаторов класса и интерфейса.
Вернемся к определению класса AddressBook. Третий вызов макроса Q_CLASSINFO() может показаться немного странным. По умолчанию элементы управления ActiveX предоставляют в распоряжение клиентов не только свои собственные свойства, сигналы и слоты, но и свои суперклассы вплоть до QWidget. Атрибут ToSuperClass позволяет определить суперкласс самого высокого уровня (в дереве наследования), который мы собираемся предоставить клиенту. Здесь мы указываем имя класса компонента («AddressBook») в качестве имени экспортируемого класса самого высокого уровня — это значит, что не будут экспортироваться свойства, сигналы и слоты, определенные в суперклассах AddressBook.