Оглавление
Создание серверов ActiveX в QtМодуль QAxServer является частью каркаса ActiveQt. Оно состоит из трех классов:
Предоставляются несколько примеров реализации элементов управления ActiveX и COM-объектов. Темы: Использование библиотекиЧтобы превратить стандартное приложение Qt в COM-сервер используя библиотеку QAxServer вы должны добавить qaxserver как установку CONFIG в вашем .pro файле. Внепроцессный исполняемый файл сервера генерируется из .pro файла примерно так: TEMPLATE = app CONFIG += qaxserver RC_FILE = qaxserver.rc ... Чтобы собрать внутрипроцессный сервер используйте .pro файл, например так: TEMPLATE = lib CONFIG += qaxserver dll DEF_FILE = qaxserver.def RC_FILE = qaxserver.rc ... Файлы qaxserver.rc и qaxserver.def являются частью каркаса и могут быть использованы из своих обычных мест расположения (путь указывается в .pro файле) или скопированы в каталог проекта. Вы можете изменять эти файлы до тех пор, пока они включают в себя любой файл как запись библиотеки типов, т.е. вы можете добавить информацию о версии или указать другую пиктограмму панели инструментов. Конфигурация qaxserver вынудит инструмент qmake добавить требуемые шаги сборки к системе сборки:
Чтобы пропустить этап постобработки установите также конфигурацию qaxserver_no_postlink. Дополнительно вы можете указать номер версии используя переменную VERSION, например TEMPLATE = lib VERSION = 2.5 ... Указанный номер версии будет использовать в качестве версии библиотеки типов и сервера при регистрации. Вне процесса или внутри процессаБудет ли ваш COM-сервер запущен как автономный исполняемый файл или как разделяемая библиотека в процессе клиента зависит главным образом от типа COM-объектов которые вы хотите предоставить в сервере. Исполняемый файл сервера имеет преимущество being able to запущен как автономное приложение, но добавляет значительные накладные расходы на связь между клиентом COM и COM-объектом. Если в элементе управления происходит программная ошибка, то только запущенный элементом управления серверный процесс аварийно завершит работу, а клиентское приложение скорее всего продолжит работу. Не все клиенты COM поддерживают исполняемые файлы серверов. Внутрипроцессный сервер обычно меньше по размеру и быстрее запускается. Связь между клиентом и сервером выполняется непосредственно через вызовы виртуальных функций и не привносит накладные расходы требуемые для удаленных вызовов процедур. Однако, если сервер аварийно завершит работу клиентское приложение вероятно также аварийно заверится, и не вся функциональность доступна для внутрипроцессных серверов (т.е., регистрация в таблице исполняющихся объектов COM). Оба типа серверов могут использовать Qt либо как разделяемую библиотеку, либо статически слинкованную в серверную библиотеку. Типовые ошибки во время шагов после сборкиДля связанных с ActiveQt шагов постобработки для работы сервера нужно удовлетворить некоторые требования:
Если эти требования не выполнены, то возможно произойдет одна или более из следующих ошибок: Аварийное завершение работы исполняемого файла сервераЧтобы сгенерировать IDL виджетов, открытых как элементы управления ActiveX, необходимо создать экземпляр класса (вызывается конструктор). На этой стадии не существует ничего кроме объекта QApplication. Конструктор вашего виджета не должен полагаться на какие-либо другие созданные объекты, например, он будет проверять на ненулевые указатели. Чтобы отладить ваш сервер, запустите его с ключом -dumpidl выходной файл и проверьте, где он аварийно завершается. Обратите внимание на то, что функции элемента управления не вызываются. Исполняемый файл сервера не является допустимым приложением Win32Прикрепление библиотеки типов повреждает бинарный файл сервера. Это ошибка в Windows и случается только в релизных сборках. Первый шаг линковки заключается в линковке пустой библиотеки типов в исполняемый файл, который idc позднее может заменить. Добавим файл ресурса с библиотекой типов к вашему проекту как продемонстрировано в примерах. "Unable to locate DLL"Системе сборки нужно запустить исполняемый файл сервера для генерирования определения интерфейса и зарегистрировать сервер. Если динамически слинкованная библиотека, с которой слинкован сервер, не находится в пути это может вызвать аварийное завершение работы (например, вызовы Visual Studio сервера используя установки окружения, указанные в опции "Directories"). Убедитесь, что все DLLки, требуемые вашему серверу, расположены в каталоге, который перечислен в путях как напечатано в окне сообщения ошибки. "Cannot open file ..."Сервер ActiveX не может выключиться когда последний клиенты перестал его использовать. Обычно для завершения приложения требуется около двух секунд, но вы можете использовать диспетчер задач, чтобы убить процесс (например, когда клиент не освобождает элементы управления должным образом). Реализация элементов управленияЧтобы реализовать COM-объект с помощью Qt, создайте подкласс QObject или любой существующий подкласс QObject. Если класс является подклассом QWidget, COM-объект будет элементов управления ActiveX. #include <QWidget> class MyActiveX : public QWidget { Q_OBJECT Макрос Q_OBJECT требуется для предоставления каркасу ActiveQt метаобъектной информации о виджете. Q_CLASSINFO("ClassID", "{1D9928BD-4453-4bdd-903D-E525ED17FDE5}") Q_CLASSINFO("InterfaceID", "{99F6860E-2C5A-42ec-87F2-43396F4BE389}") Q_CLASSINFO("EventsID", "{0A3E9F27-E4F1-45bb-9E47-63099BCCD0E3}") Используйте макрос Q_CLASSINFO() для указания идентификаторов COM для COM-объекта. ClassID и InterfaceID требуются пока EventsID необходим только когда ваш объект имеет сигналы. Чтобы сгенерировать эти идентификаторы используйте системные инструменты, аналогичные uuidgen или guidgen. Вы можете указать дополнительные атрибуты для каждого из ваших классов; за подробностями обращайтесь к Информация о классе и настройка. Q_PROPERTY(int value READ value WRITE setValue)
Используйте макрос Q_PROPERTY() для объявления свойств элемента управления ActiveX. Объявите стандартный конструктор, получающий родительский объект, и функции, сигналы и слоты аналогичные для любого подкласса QObject. public: MyActiveX(QWidget *parent = 0) ... int value() const; public slots: void setValue(int v); ... signals: void valueChange(int v); ... }; Каркас ActiveQt открывает свойства и открытые слоты в качестве свойств ActiveX и методы, а сигналы как события ActiveX, а также конвертировать между типами данных Qt и эквивалентными типами данных COM. Типы данныхТипы данных Qt, поддерживаемых для свойств:
Типы данных Qt, которые поддерживаются для параметров сигналов и слотов:
Также поддерживаются экспортируемые перечисления и флаги (смотрите Q_ENUMS() и Q_FLAGS()). Внутрипараметрические (in-parameter) типы также поддерживаются как возвращаемые значения. Свойства и сигналы/слоты, которые имеют параметры, использующие любые другие типы данных, игнорируются каркасом ActiveQt. ПодобъектыCOM-объекты может иметь множество подобъектов, которые могут представлять субэлемент COM-объекта. COM-объект, отображающий приложение многодокументной электронной таблицы, может предоставить например один подобъект для каждого листа таблицы. Любой подкласс QObject можно использовать в качестве типа для подобъекта в ActiveX, если он известен QAxFactory. Затем тип можно использовать в свойствах, или в качестве возвращаемого типа или параметра слота. Уведомление о свойствеЧтобы сделать свойства связываемыми для клиента ActiveX, используйте множественное наследование от класса QAxBindable: #include <QAxBindable> #include <QWidget> class MyActiveX : public QWidget, public QAxBindable { Q_OBJECT При реализации функций записи свойства используйте функции класса QAxBindable - requestPropertyChange() и propertyChanged() - чтобы позволить клиентам ActiveX привязать свойства к элементу управления. Использование элементов управленияЧтобы сделать COM-сервер доступны для системы COM нужно его зарегистрировать в системном реестре используя пять уникальных идентификаторов. Эти идентификаторы предоставляются инструментами тип guidgen или uuidgen. Информация о регистрации позволяет COM для локализации бинарного файла, предоставляемого запрашиваемым элементом управления ActiveX, передавать (marshall) вызовы удаленных процедур для элемента управления и прочитать информацию типов о методах и свойствах отрытых элементом управления. Чтобы создать COM-объект при запросе клиента своего сервера должны экспортировать реализацию QAxFactory. Самым лёгким способом сделать это - использовать набор макросов: QAXFACTORY_BEGIN("{ad90301a-849e-4e8b-9a91-0a6dc5f6461f}", "{a8f21901-7ff7-4f6a-b939-789620c03d83}") QAXCLASS(MyWidget) QAXCLASS(MyWidget2) QAXTYPE(MySubType) QAXFACTORY_END() Это экспортирует MyWidget и MyWidget2 как COM-объекты, которые могут быть созданы клиентами COM и будет зарегистрирован MySubType как тип, который может быть использован в свойствах и параметрах MyWidget и MyWidget2. Документация класса QAxFactory объясняет как использовать этот макрос и как реализовать и использовать пользовательские фабрики. Для внепроцессных исполняемых файлов серверов вы можете реализовать функцию main() чтобы создать экземпляр объекта QApplication и войти в цикл обработки событий так же как любое обычное приложение Qt. По умолчанию приложение будет запущено как стандартное приложение Qt, но если вы передадите -activex в командной строке то оно будет запущено как сервер ActiveX. Используйте QAxFactory::isServer() для создания и запуска стандартного интерфейса приложения, или для предотвращения автономного выполнения: #include <QApplication> #include <QAxFactory> int main(int argc, char *argv[]) { QApplication app(argc, argv); if (!QAxFactory::isServer()) { // создаём и показываем главное окно } return app.exec(); } Этого однако не требуется так как ActiveQt предоставляет реализацию по умолчанию функции main. Реализация по умолчанию вызывает QAxFactory::startServer(), создаёт экземпляр класса QApplication и вызывает exec(). Чтобы собрать исполняемый файл сервера ActiveX запустите qmake для генерации make-файла, и используйте make-инструмент вашего компилятора как любого другого приложения Qt. Процесс создания будет также регистрировать элементы управления в системном реестре вызывая получившийся исполняемый файл с опцией -regserver командной строки. Если сервер ActiveX является исполняемым файлом, то поддерживаются следующие опции командной строки:
Внутрипроцессные сервера могут быть зарегистрированы используя инструмент regsvr32, доступный на всех системах Windows. Типичные проблемы во время компиляцииПеречисленные ошибки компилятора/линкера основаны на таких проблемах компилятора Microsoft Visual C++ 6.0. "No overloaded function takes 2 parameters"Когда произойдёт ошибка в коде, который использует макрос QAXFACTORY_DEFAULT(), класс виджета не имеет конструктора, который может быть использован фабрикой по умолчанию. Либо добавьте стандартный конструктор виджета, либо реализуйте пользовательскую фабрику, которой он не требуется. Когда произойдёт ошибка в коде, который использует макрос QAXFACTORY_EXPORT(), подкласс QAxFactory не имеет соответствующего конструктора. Предоставьте общий конструктор класса, похожий на MyFactory(const QUuid &, const QUuid &); для вашего класса фабрики. "Syntax error: bad suffix on number"Уникальные идентификаторы не могут быть переданы как строки в макрос QAXFACTORY_EXPORT() или QAXFACTORY_DEFAULT(). "Unresolved external symbol _ucm_instantiate"Сервер не экспортирует реализацию QAxFactory. Используйте макрос QAXFACTORY_EXPORT() в одном из файлов реализации для создания экземпляра класса и экспорта фабрики, или используйте макрос QAXFACTORY_DEFAULT() для использования фабрики по умолчанию. "_ucm_initialize already defined in ..."Сервер экспортирует более, чем одну реализацию QAxFactory или экспортирует одну и туже реализацию дважды. Если вы используете фабрику по умолчанию, то макрос QAXFACTORY_DEFAULT() должен использоваться в проекте только один раз. Используйте пользовательскую реализацию QAxFactory и макрос QAXFACTORY_EXPORT(), если сервер предоставляет множество элементов управления ActiveX. Распространение бинарных файлов QAxServerСервера ActiveX написанные с помощью Qt могут использовать Qt либо как разделяемую библиотеку, либо имеют Qt, слинкованную статически в бинарный файл. В результате обоих способов получаются весьма большие пакеты (либо сам бинарный файл сервера становится больше, либо вы поставляет Qt DLL). Установка автономных серверовЕсли ваш сервер ActiveX может быть запущен также как автономное приложение, запустите исполняемый файл сервера с параметром командной строки -regserver после установки исполняемого файла на целевой системе. После этого элементы управления, предоставляемые сервером, станут доступными для клиентов ActiveX. Установка внутрипроцессных серверовЕсли ваш сервер ActiveX является частью установочного пакета, то используйте инструмент regsvr32, предоставляемый Microsoft, чтобы зарегистрировать элементы управления в целевой системе. Если этого инструмента нет, загрузите DLL в ваш процесс установки, разрешите символ DllRegisterServer и вызовите функцию: HMODULE dll = LoadLibrary("myserver.dll"); typedef HRESULT(__stdcall *DllRegisterServerProc)(); DllRegisterServerProc DllRegisterServer = (DllRegisterServerProc)GetProcAddress(dll, "DllRegisterServer"); HRESULT res = E_FAIL; if (DllRegisterServer) res = DllRegisterServer(); if (res != S_OK) // обработка ошибки Распространение серверов через ИнтернетЕсли вы хотите использовать элементы управления вашего сервера в веб-страницах, вам нужно сделать доступным сервер для браузера, используемого для просмотра вашей страницы, и вам нужно указать расположение пакета сервера в вашей странице. Чтобы указать расположение сервера используйте атрибут CODEBASE тега OBJECT вашего веб-сайта. Значение может указывать на сам файл сервера, на INF-файл, перечисляющий другие файлы требуемые серверу (например, DLL Qt), или сжатый архив CAB. Файлы INF и CAB документированы почти в каждой доступной книге о программировании ActiveX и COM также, как и в библиотеке MSDN и различных онлайн ресурсах. Примеры включают INF-файлы, которые могут быть использованы для сборки архивов CAB: [version] signature="$CHICAGO$" AdvancedINF=2.0 [Add.Code] simpleax.exe=simpleax.exe [simpleax.exe] file-win32-x86=thiscab clsid={DF16845C-92CD-4AAB-A982-EB9840E74669} RegisterServer=yes Инструмент CABARC от Microsoft легко может генерировать архивы CAB: cabarc N simpleax.cab simpleax.exe simple.inf INF-файлы допускают статическую сборку Qt, поэтому зависимости от других DLL не перечисляются в INF-файлах. Чтобы распространить DLLки, от которых зависит сервер ActiveX, вы должны добавить зависимости и предоставить библиотечные файлы с архивом. Использование элементов управленияЧтобы использовать элементы управления ActiveX, например, для встраивания их в веб-страницу, используйте HTML-тег <object>. <object ID="MyActiveX1" CLASSID="CLSID:ad90301a-849e-4e8b-9a91-0a6dc5f6461f"> ... <\object> To initialize the control's properties, use <object ID=...> <param name="name" value="value"> <\object> Если веб-браузер поддерживает выполнение скриптов используйте JavaScript, VBScript и формы для сценария элемента управления. Примеры ActiveQt включают в себя демонстрацию HTML-страниц для элементов управления из примеров. Поддерживаемые и неподдерживаемые клиенты ActiveXНижеследующее в значительной мере базируется на наших экспериментах с элементами управления ActiveX и клиентскими приложениями, и никоим образом не являются исчерпывающими. Поддерживаемые клиентыЭти стандартные приложения работают с элементами управления ActiveX, разработанными с помощью ActiveQt. Обратите внимание на то, что некоторые клиенты поддерживают только внутрипроцессные элементы управления.
Приложения Microsoft Office поддерживаются, но вам нужно зарегистрировать элементы управления как "Insertable" объекты. Переопределите QAxFactory::registerClass чтобы добавить этот атрибут к классу COM, или установите "Insertable" информацию класса для вашего класса равной "yes" используя макрос Q_CLASSINFO. Неподдерживаемые клиентыМы не сумели заставить работать COM-объекты, основанные на ActiveQt, со следующими клиентскими приложениями.
Типичные ошибки времени выполненияThe Server Does Not RespondЕсли система не может запустить сервер (проверьте с помощью менеджера задач запущен ли процесс сервера), убедитесь что DLL, от которых зависит сервер, присутствуют в системных путях (например, Qt DLL!). Используйте dependency walker чтобы увидеть все зависимости бинарного файла сервера. Если сервер запущен (например, есть в списке менеджера задач), информацию по отладке вашего сервера смотрите в следующем разделе. The Object Cannot Be CreatedЕсли сервер может быть собран и правильно зарегистрирован во время процесса сборки, но объект не может быть инициализирован, например, приложение OLE/COM Object Viewer, убедитесь, что DLL, от которых зависит сервер, присутствуют в системных путях (например, Qt DLL). Используйте dependency walker чтобы увидеть все зависимости бинарного файла сервера. Если сервер запущен, информацию по отладке вашего сервера смотрите в следующем разделе. Отладка ошибок времени выполненияЧтобы отладить внутрипроцессный сервер в Visual Studio установите проект сервера в качестве активного проекта и укажите клиент "executable for debug session" в настройках проекта (например, используйте Тестовый контейнер ActiveX). Вы можете установить точки останова в вашем коде, а также пройти по шагам код ActiveQt и Qt если вы установили отладочную версию. Для отладки исполняемого файла сервера запустите приложение в отладчике и начните с параметра командной строки -activex. Затем запустите своего клиента и создайте экземпляр вашего элемента управления ActiveX. COM будет использовать существующий процесс для следующего клиента пытающегося создать элемент управления ActiveX. Информация о классе и настройкаЧтобы предоставить атрибуты для каждого класса COM используйте макрос Q_CLASSINFO, являющийся частью мета-объектной системы Qt.
Обратите внимание на то, что и ключи, и значения являются чувствительными к регистру. Нижеследующее объявляет версию 2.0 класса, который делает видимым только свой собственный API, и доступен в диалоге "Insert Objects" приложений Microsoft Office. class MyActiveX : public QWidget { Q_OBJECT Q_CLASSINFO("Version", "2.0") Q_CLASSINFO("ClassID", "{7a4cffd8-cbcd-4ae9-ae7e-343e1e5710df}") Q_CLASSINFO("InterfaceID", "{6fb035bf-8019-48d8-be51-ef05427d8994}") Q_CLASSINFO("EventsID", "{c42fffdf-6557-47c9-817a-2da2228bc29c}") Q_CLASSINFO("Insertable", "yes") Q_CLASSINFO("ToSuperClass", "MyActiveX") Q_PROPERTY(...) public: MyActiveX(QWidget *parent = 0); ... }; Разработка лицензированных компонентовЕсли вы разрабатываете компоненты, вы может захотеть контролировать кто может создавать экземпляры этих компонентов. Поскольку бинарный файл сервера может распространяться и регистрироваться на любой клиентской машине, любой может использовать эти компоненты в своём программного обеспечении. Лицензирование компонентов может быть произведено используя разнообразные приёмы, например, код создания элемента управления может предоставлять лицензионный ключ или механизм, в котором предполагается запускать элемент управления, требует лицензирования. Чтобы пометить класс Qt как лицензированный укажите "LicenseKey" используя макрос Q_CLASSINFO(). class MyLicensedControl : public QWidget { Q_OBJECT Q_CLASSINFO("LicenseKey", "<key string>") ... }; Ключ требуется чтобы иметь возможность создать экземпляр класса MyLicensedControl на машине, которая не лицензирует сам сеяб. Лицензированный разработчик теперь может повторно распространять бинарный файл сервера со своим приложением, которое создаёт элемент управления используя значение "LicenseKey" до тех пор, пока пользователи приложения не смогут создать элемент управления мез лицензионного ключа. Если единственного лицензионного ключа для элемента управления недостаточно (т.е. вы хотите различать разработчиков чтобы запрашивать разные лицензионные ключи), вы можете указать пустой ключ чтобы покачать, что элементу управления требуется лицензия, и переопределить QAxFactory::validateLicenseKey() для проверки того, что лицензия существует в системе (т.е. через файл лицензии). Больше интерфейсовЭлементы управления ActiveX, предоставленные серверами ActiveQt, поддерживают минимальный набор интерфейсов COM для реализации спецификаций OLE. Если класс ActiveX унаследован от класса QAxBindable, он также может реализовать дополнительные интерфейсы COM. Создайте новый подкласс QAxAggregated и используйте множественное наследование для создание подкласса дополнительного интерфейса классов COM. class AxImpl : public QAxAggregated, public ISomeCOMInterface { public: AxImpl() {} long queryInterface(const QUuid &iid, void **iface); // IUnknown QAXAGG_IUNKNOWN // ISomeCOMInterface ... } Переопределите функцию QAxAggregated::queryInterface() для поддержки дополнительных интерфейсов COM. long AxImpl::queryInterface(const QUuid &iid, void **iface) { *iface = 0; if (iid == IID_ISomeCOMInterface) *iface = (ISomeCOMInterface *)this; else return E_NOINTERFACE; AddRef(); return S_OK; } Поскольку ISomeCOMInterface - подкласс IUnknown, вам нужно реализовать функции QueryInterface(), AddRef() и Release(). Чтобы сделать это используйте макрос QAXAGG_IUNKNOWN в определении вашего класса. Если вы реализовали функции IUnknown вручную, делегируйте вызовы указателю на интерфейс, возвращаемому функцией QAxAggregated::controllingUnknown(), например HRESULT AxImpl::QueryInterface(REFIID iid, void **iface) { return controllingUnknown()->QueryInterface(iid, iface); } Не поддерживайте интерфейс IUnknown самостоятельно в вашей реализации queryInterface(). Реализуйте методы интерфейсов COM и используйте QAxAggregated::object(), если вам нужно сделать вызовы подкласса QObject, реализуя элемент управления. В вашем подклассе QAxBindable, реализуйте QAxBindable::createAggregate() чтобы вернуть новый объект подкласса QAxAggregated. class MyActiveX : public QWidget, public QAxBindable { Q_OBJECT public: MyActiveX(QWidget *parent); QAxAggregated *createAggregate() { return new AxImpl(); } }; Смотрите также Каркас ActiveQt. |
Попытка перевода Qt документации. Если есть желание присоединиться, или если есть замечания или пожелания, то заходите на форум: Перевод Qt документации на русский язык... Люди внесшие вклад в перевод: Команда переводчиков |