Название: Урок: Создание динамических библиотек Отправлено: Eugene Efremov от Декабрь 20, 2008, 19:23 Создание динамических библиотек
Вступление Тема динамических библиотек, казалось бы, раскрыта 42 главе фундаментального труда Шлее. Однако, при ближайшем рассмотрении выясняется, что раскрыта она, прямо скажем, недостаточно. В приведенном примере Шлее ограничивается рассмотрением создания и использования DLL, содержащей только функции, но не классы. После чего сразу переходит к рассмотрению плагинов. Между тем, при создании динамических библиотек, содержащих классы, возникают некоторые тонкости, которые необходимо учитывать. Об этом и пойдет речь в данном howto. При описании я буду предполагать, что читатель в общих чертах знаком с основами Qt, и у него не возникнет вопросов типа «а что такое QVBoxLayout?». Так что я не буду описывать текст исходников примера (он сам по себе весьма прост), а сосредоточусь непосредственно на особенностях, характерных для DLL. Если с пониманием примера все же возникнут трудности — рекомендую сперва прочитать того же Шлее, а уже потом обратится к этому тексту. И еще — здесь рассматривается только явная компоновка приложения и библиотеки. Если необходимо подгрузить класс динамически — лучше оформить его как плагин. В противном случае вас ждут большие проблемы... 1.Библиотека Лежит в директории src/dll. Библиотека содержит простейший виджет, производный от QLabel. Им можно пользоваться точно также, как любым другим виджетом Qt: добавлять в свои виджеты, использовать слоты/сигналы, создавать производные классы и т.д. Отличие от обычного Qt приложения сосредоточены, в основном, в двух файлах: ddll.h и dll.pro. ddll.h: Код
Вообще говоря, без этого файла можно обойтись. Если пользоваться нормальным компилятором. Но, помимо нормальных компиляторов, существуют еще компиляторы от microsoft. И в них, по слухам, требуется подобное извращение. Что здесь написано. Q_DECL_EXPORT и Q_DECL_IMPORT — это два недокументированных (пока?) макроса, которые введены разработчиками Qt специально, чтобы обойти эти грабли в MSVS. Первый из них требуется писать перед объявлением класса в DLL, второй — в приложениях, которые этот DLL используют. Во всех остальных компиляторах они равны пустой строке. Соответственно, перед всеми классами в нашей библиотеке мы должны проставить наш макрос D_SHARED: Код
На этом отличия исходного кода кончаются. Главные различия касаются файла проекта: dll.pro: Код
Полагаю, TEMPLATE и CONFIG в комментариях не нуждаются. На некоторых других вещах стоит остановится подробнее. DEFINES — определяем макрос D_SHARED_LIB, который используется в ddll.h (см. выше). DESTDIR и DLLDESTDIR — мы помещаем получившуюся библиотеку в lib. Но само приложение у нас будет находится в bin, поэтому мы копируем DLL туда. VERSION — а вот здесь нам необходимо небольшое лирическое отступление. Существует такая вещь, как бинарная совместимость. Если кратко — мы создаем некоторую структуру, помещаем ее в DLL. Потом наши приложения этот DLL юзают. А потом мы ее изменяем. И перезаписываем DLL поверх старого. А приложения обращаются к ней по старым адресам... Результат, думаю, ясен. Так вот, это называется — отсутствие бинарной совместимости. В просторечии — dll hell. И чтобы хоть частично этого избежать, вводят понятие версии DLL. Предполагается, что DLL бинарно совместимы, если старший номер версии у них не отличается. Ответственность за реализацию такого поведения целиком лежит на разработчике библиотеки. Однако, вернемся к нашим баранам. Увидев VERSION, qmake во-первых, принимает необходимые меры, чтобы эта версия прописалась вo внутренних свойствах DLL, а, во-вторых — приписывает ее старший номер к имени создаваемого DLL. Т.е. в нашем примере это будет dlabel1.dll. Соответственно, если мы изменим интерфейс нашей библиотеки до полной несовместимости, для предотвращения использования ее старыми приложениями мы должны будем просто сменить VERSION на 2.0.0. И тогда новые версии будут использовать dlabel2.dll, старые — dlabel1.dll. 2.Приложение Лежит в директории src/main. Как я уже писал выше — мы можем использовать наш виджет точно также, как любой другой. Единственные отличия — в pro-файле. main.pro: Код
Остановимся на этих отличиях подробнее. INCLUDEPATH — указываем путь к хидерам библиотеки. DEPENDPATH — объясняем qmake, что хидеры библиотеки необходимо учитывать при построении зависимостей. LIBS — подключаем библиотеку. При этом путь и сам библиотека указывается отдельно и в самом общем виде (без расширений и т.п.). В такой форме это будет правильно обработано для любого компилятора. VERSION у библиотеки должен быть указан, иначе это не сработает. При наличии нескольких версий будет выбрана последняя. 3. Возможные грабли При использовании динамической линковки следует помнить о некоторых общих вещах, которые необходимо учитывать во избежание ошибок. Впрочем, при нормальной работе Qt, в большинстве случаев, сама заботится о том, чтобы сделать «нарушение правил» достаточно сложным... Итак: 1. Пользуйтесь для сборки библиотеки и приложения одним и тем же компилятором. Разные компиляторы по-разному кодируют в объектном коде имена функций, классов и т.д. Более того, могут отличаться и смещения полей внутри класса. По этому попытка подключить dll, собранный в другом компиляторе, скорее всего, увенчается успехом лишь в том случае, если все ф-ции отмечены как extern "C" и смещения структур выравнены принудительно. Для классов это, разумеется, не подходит. Как ни трудно понять, при самостоятельной разработке библиотеки эта проблема вряд ли возникнет. А вот если мы имеем чужой dll и закрытый код... Тем, кто попал в такую ситуацию, остается только посочувствовать. 2. ...И одними и теми же опциями командной строки — в части, влияющей на свойства генерируемого кода. Имеются в виду, прежде всего, опции, включающие поддержку исключений, RTTI, управляющие отладочной информацией и т. п. Как ни трудно догадаться, если в один модуль скомпилирован с поддержкой RTTI, другой — без, то при использовании этого самого RTTI программу ждут большие неприятности. То же касается и отладки (там разные версии стандартных библиотек — см. ниже). В Qt об опциях командной строки компилятора заботится qmake, так что в обычной ситуации эта проблема возникать не должна. Иными словами — если не делать явных глупостей, вроде смешения debug и release в одну кучу, то этого и не произойдет. 3. Используя низкоуровневые ресурсы, обеспечте инкапсуляцию всей фактической работы с ними в рамках одного модуля, линкуемого динамически. Если CRT (http://ru.wikipedia.org/wiki/%D0%A1%D1%82%D0%B0%D0%BD%D0%B4%D0%B0%D1%80%D1%82%D0%BD%D0%B0%D1%8F_%D0%B1%D0%B8%D0%B1%D0%BB%D0%B8%D0%BE%D1%82%D0%B5%D0%BA%D0%B0_%D1%8F%D0%B7%D1%8B%D0%BA%D0%B0_%D0%A1%D0%B8) скомпонована статически, то ресурс (например — память), выделенный в рамках одного модуля, невозможно корректно освободить в рамках другого: каждый из них будет оперировать со своим адресным пространством и своими копиями внутренних структур, обеспечивающих нормальный доступ к ресурсам. Кроме того, разумеется, нужно следить, чтобы во всех модулях использовалась CRT одной и той же версии. В Qt все низкоуровневые вызовы инкапсулированы в рамках модуля QtCore. Так что помнить это правило нужно лишь при низкоуровневой работе с вещами, для которых Qt не предоставляет удовлетворительной поддержки. Впрочем, такую работу нужно инкапсулировать в любом случае. В штатных же случаях — просто пользуйтесь стандартными средствами библиотеки — и будет вам счастье :). Разумеется, при этом саму Qt необходимо линковать динамически. Вообще, динамическую и статическую линковку в одну кучу мешать не следует — многих проблем удастся избежать. И еще, касательно данного примера. Следует иметь в виду, что он писался в предположении, что библиотека будет линковаться только динамически. Для для случая статической линковки макросы Q_DECL_IMPORT/EXPORT следует убрать. Как средствами qmake отличить статическую линковку от динамической и обеспечить включение/выключение нужных макросов — продемонстрировано ниже по треду (http://www.prog.org.ru/index.php?topic=8259.msg44623#msg44623) в письме от pastor, на модификации моего примера. За что ему отдельное спасибо. Собственно, на этом всё. Enjoy! :) Проверено
Название: Re: HowTo: Создание динамических библиотек Отправлено: pastor от Декабрь 20, 2008, 20:20 Да, действительно, __dllimport (Q_DECL_IMPORT) и __dllexport (Q_DECL_EXPORT) это приблуда MS Visual Studio. Если использовать другой компилятор, отличный от MS Visual Studio, то без этого можно обойтись. Но это ещё не все, что касается этих макросов и MS Visual Studio. Если разрабатываемую вами библиотеку сделать статической, то приложение, использующее её, линковатся не будет. Причина в том, что при статической линковке эти макросы также не нужны.
Я немного модифицировал код Eugene Efremov. Проверено: Windows XP x64, Qt 4.4.3, MSVS 2008 openSuse 11, Qt 4.4.3, gcc version 4.3.1 20080507 Название: Re: HowTo: Создание динамических библиотек Отправлено: Rcus от Декабрь 20, 2008, 20:34 На самом деле MinGW тоже поддерживает эти директивы (другое дело что если их не указывать msvc не будет экспортировать символы вообще, а MinGW экспортирует все указанные в хедерах).
И еще не хватает части про несовместимость name mangling, проблемы с dynamic_cast и crt. А так описываются почти все грабли :) Название: Re: HowTo: Создание динамических библиотек Отправлено: Eugene Efremov от Декабрь 22, 2008, 17:13 И еще не хватает части про несовместимость name mangling, проблемы с dynamic_cast и crt. А так описываются почти все грабли :) Проблемы с dynamic_cast — имеется в виду, что для его нормальной работы оно оба модуля должны быть скомпилированы с поддержкой RTTI? Или там еще какие-то грабли есть, про которые я не знаю? В общем, секцию с граблями я добавил, если про dynamic_cast нужно что-то еще — поправлю... Я немного модифицировал код Eugene Efremov. Спасибо. Я указал в конце, что рассматривался только случай динамической линковки, а если нужно еще и статическую — см. ниже. И дал ссылку на этот новый пример. Название: Re: HowTo: Создание динамических библиотек Отправлено: Rcus от Декабрь 22, 2008, 19:09 Проблемы с dynamic_cast — имеется в виду, что для его нормальной работы оно оба модуля должны быть скомпилированы с поддержкой RTTI? Или там еще какие-то грабли есть, про которые я не знаю? В общем, секцию с граблями я добавил, если про dynamic_cast нужно что-то еще — поправлю... если мы создаем в одном исполняемом модуле некоторый объект и передаем указатель на его в динамическую библиотеку, то dynamic_cast будет выдавать 0. Это связано с особенностью реализации rtti. Отчасти эту проблему решает qobject_cast, но работает только для потомков QObject. Грабли crt заключены в том, что оба модуля должны линковаться с одной версией crt (например релизная и отладочная версии несовместимы) Название: Re: HowTo: Создание динамических библиотек Отправлено: Eugene Efremov от Декабрь 22, 2008, 21:33 если мы создаем в одном исполняемом модуле некоторый объект и передаем указатель на его в динамическую библиотеку, то dynamic_cast будет выдавать 0. Это связано с особенностью реализации rtti. Отчасти эту проблему решает qobject_cast, но работает только для потомков QObject. Ммм... У меня в gcc этот баг не получается воспроизвести. Все dynamic_cast отлично работают и там, и там. Можно пример кода? Или это еще один личный глюк MSVS? Грабли crt заключены в том, что оба модуля должны линковаться с одной версией crt (например релизная и отладочная версии несовместимы) Т.е., фактически попадает под то, что у меня описано в п.2. Добавил там про отладку. Название: Re: HowTo: Создание динамических библиотек Отправлено: Rcus от Декабрь 22, 2008, 22:06 На самом деле это не баг, а особенность механизма линковки. Демо проект прикреплен,
Код: main@rcuhome:~$ g++ --version Название: Re: HowTo: Создание динамических библиотек Отправлено: Eugene Efremov от Декабрь 22, 2008, 22:40 На самом деле это не баг, а особенность механизма линковки. Демо проект прикреплен, Код: main@rcuhome:~$ g++ --version Ну и? У меня (windows 2003, g++ 3.4.5) все работает: Код: Dynamic? Foo::test, upcast ? = 0 Хотя, если учесть, что там мешается в одну кучу статическая и динамическая линковка — меня это очень удивляет. При таком раскладе действительно странно, что оно работает. Если заменить в foobar.pro static на shared — оно под linux'ом заработает? Если да — dynamic_cast тут несколько сбоку... Кстати, а я ведь в явном виде так и не написал, что статическую и динамическую линковку лучше не мешать. Исправляюсь... Название: Re: HowTo: Создание динамических библиотек Отправлено: lex_newton от Август 05, 2009, 10:04 Собственно, хочу рассказать о граблях ;)
Windows XP SP3, g++ (GCC) 3.4.2 (mingw-special), QT 4.4.3 Создаю динамическую библиотеку в которой находится класс унаследованный от QWidget и имеет макрос Q_OBJECT Код: class MyClass : public QWidget Собираю динамическую библиотеку (все собирается без ошибок и т.п.) Код: CONFIG += shared И в главной программе использую MyClass. В чем грабли? Если использовать MyClass то ничего особенного не происходит. все работает прекрассно. НО! Если попытаться в главной программе сделать нечто подобное, то возникает ошибка 0xc0000005 при запуске программы Код: class MyClass2 : public MyClass Причем, если закомментировать Q_OBJECT то программа перестает вылетать. Как оказалось, дело в том, что под windows даже используя gcc необходимо указывать Q_DECL_EXPORT Q_DECL_IMPORT. И VS тут не при чем, это фишка платформы win32. + если мы хотим экспортировать не класс, а функцию, поступаем тем же самым способом. пишем перед прототипом функции соотвествующие макросы. Название: Re: HowTo: Создание динамических библиотек Отправлено: polyakovp84 от Сентябрь 29, 2009, 17:38 Разбираясь с данным примером понял, что библиотека загружается сразу при запуске программы.
А как сделать чтобы при выполнении некого условия библиотека загружалась динамически, затем содавался класс для последующей работы с ним? Если можно простой работающий пример. Заранее благодарен. Название: Re: HowTo: Создание динамических библиотек Отправлено: Rcus от Сентябрь 29, 2009, 17:43 "Plug & Paint Example"
Название: Re: HowTo: Создание динамических библиотек Отправлено: SASA от Октябрь 01, 2009, 15:37 Есть проблема с qobject_cast.
Если класс описан в dll и создан там же, то в app попытка привести указатель к этому классу даёт ноль. Ответ сказали искать здесь http://www.qtcentre.org/forum/f-qt-programming-2/t-casting-result-of-qwidgetstack-currentwidget-5037.html (http://www.qtcentre.org/forum/f-qt-programming-2/t-casting-result-of-qwidgetstack-currentwidget-5037.html) Но я чё-то не понял и проблему решить не смог. Может есть у кого какие мысли. З.Ы. Билбиотека загружается динамически - плагин. Название: Re: HowTo: Создание динамических библиотек Отправлено: UVV от Октябрь 01, 2009, 16:10 Там в принципе описано решение, использовать третью библиотеку и линковаться с ней.
Но у меня прокатило просто dynamic_cast =) Название: Re: HowTo: Создание динамических библиотек Отправлено: Elfet от Апрель 06, 2010, 18:20 Приветствую!
У меня возникла такая проблема с dll, не знаю в чём дело. Пожалуйста, помогите! Есть два проекта: один интерфейс, другой библиотека dll на чистом C++ Всё собираю в QtSDK (Qt Creator + minGW). Одно время было всё хорошо, а вот недавно по непонятной мне причине появились глюкт с dll. Нажимаю пересобрать библиотеку, затем пересобрать интерфейс, запускаю - вроде бы всё хорошо, но выполняется совсем не тот код (видно по тому что отображается на интерфейсе) Однако если я нажимаю F5 (отладка) то всё запускается нормально и выполняется нормально. В чём тут дело? ??? Может быть я что-то неправильно настроил? SmartFlow.pro: Цитировать ... # SmartFlowLib DEPENDPATH += ../SmartFlowLib/Source INCLUDEPATH += ../SmartFlowLib/Source LIBS += ./debug/smartflowlib1.dll ... SmartFlowLib.pro Цитировать QT -= gui TEMPLATE = lib CONFIG += shared TARGET = smartflowlib DESTDIR = ../SmartFlow/debug VERSION = 1.0.0.1 Весь код можно найти тут: http://code.google.com/p/smart-flow/source/browse/#svn/trunk Заранее спасибо! Название: Re: HowTo: Создание динамических библиотек Отправлено: Amigo_sa от Апрель 06, 2010, 18:22 у нас такое было, когда в папке с объектными файлами случайно оказывались одноименные. проверьте пути, скорее всего просто либа собирается из старых объектников
Название: Re: HowTo: Создание динамических библиотек Отправлено: Elfet от Апрель 06, 2010, 18:34 Да я вроде бы всё очистил. Даже папки все удалил. И полностью пересобрал всё.
Ещё я заметил что запуская несколько раз подряд свою программу - ошибки разные появляются :-\ Название: Re: HowTo: Создание динамических библиотек Отправлено: Elfet от Апрель 06, 2010, 19:16 И вот ещё что не понятно. Если запуская не через Qt (Ctrl+R) А захожу в папку и запускаю файл SmartFlow.exe - всё работает нормально (Остальный Qt***.dll лежат в этой же папке)
Название: Re: Урок: Создание динамических библиотек Отправлено: serg_hd от Июнь 23, 2010, 13:03 Непонятны слова
И еще — здесь рассматривается только явная компоновка приложения и библиотеки. Если необходимо подгрузить класс динамически — лучше оформить его как плагин. В противном случае вас ждут большие проблемы... А тут разве классы подгружаются не динамически? dll же.И чем отличается явная компоновка от динамической подгрузки - не описано? Название: Re: Урок: Создание динамических библиотек Отправлено: Igors от Июнь 23, 2010, 13:54 А тут разве классы подгружаются не динамически? dll же. Здесь имеется ввиду 2 подходаИ чем отличается явная компоновка от динамической подгрузки - не описано? 1) dll загружается при старте приложения (в заголовке exe указано какие dll необходимы). Если ОС не сможет их найти/ загрузить - exe не запустится. Ф-ции dll доступны в течение всей жизни exe 2) Приложение стартует без dll, а затем само (используя ф-ции OC) загружает dll (может и выгружать). Ф-ции dll обычно запрашиваются через GetProcAddress (Вындоуз) Название: Re: Урок: Создание динамических библиотек Отправлено: andrey2033 от Июль 05, 2010, 13:26 Доброго времени суток!
У меня возникла проблема с friend-функциями класса в моей библиотеке. При их наличии компилятор выдаёт ошибку undefined reference... к этим функциям... Без них всё работает. И отсюда вопрос: в dll нельзя пользоваться friend-функциями? Название: Re: Урок: Создание динамических библиотек Отправлено: xintrea от Июль 10, 2010, 22:44 Автор этой статьи удалил свой аккаунт на форуме. Так что он, наверно не ответит. Лучше задать этот вопрос в другом разделе, например в "Общих вопросах" http://www.prog.org.ru/board_50_0.html (http://www.prog.org.ru/board_50_0.html).
Название: Re: Урок: Создание динамических библиотек Отправлено: andrey2033 от Июль 14, 2010, 09:40 Автор этой статьи удалил свой аккаунт на форуме. Так что он, наверно не ответит. Лучше задать этот вопрос в другом разделе, например в "Общих вопросах" http://www.prog.org.ru/board_50_0.html (http://www.prog.org.ru/board_50_0.html). Спасибо! Название: Re: Урок: Создание динамических библиотек Отправлено: voral от Март 03, 2011, 12:20 В последнем абзаце слово повторяется....
И еще, касательно данного примера. Следует иметь в виду, что он писался в предположении, что библиотека будет линковаться только динамически. Для для случая статической линковки макросы Q_DECL_IMPORT/EXPORT следует убрать. Как средствами qmake отличить статическую линковку от динамической и обеспечить включение/выключение нужных макросов — продемонстрировано ниже по треду (http://www.prog.org.ru/index.php?topic=8259.msg44623#msg44623) в письме от pastor, на модификации моего примера. За что ему отдельное спасибо. Название: Re: Урок: Создание динамических библиотек Отправлено: raus от Июнь 22, 2012, 12:40 Здравствуйте. Могу ли я dllку Qt использовать в mfc проекте?
Название: Re: Урок: Создание динамических библиотек Отправлено: Bepec от Июнь 22, 2012, 16:43 Если наружу выставить за. ой, интерфейс на С++, то да. Однако кажется, что сигнал-слотовые соединения не будут работать, если не запущен QApplication.
Есть вроде и обход этих ограничений :) Название: Re: Урок: Создание динамических библиотек Отправлено: raus от Июнь 22, 2012, 17:23 Если наружу выставить за. ой, интерфейс на С++, то да. Однако кажется, что сигнал-слотовые соединения не будут работать, если не запущен QApplication. ну вроде выставил я интерфес C++? а в проекти dllки экземпляр QApplication вот так создаю: Код: int argc = 1; dllка вроде создалась, но что-то не могу ее прикрутить к проекту Название: Re: Урок: Создание динамических библиотек Отправлено: mutineer от Июнь 22, 2012, 17:26 ты же в курсе, что app.exec() это цикл бесконечный?
Название: Re: Урок: Создание динамических библиотек Отправлено: raus от Июнь 22, 2012, 18:04 ты же в курсе, что app.exec() это цикл бесконечный? ну так с цикла можно будет выйти, если главный виджет разрушится.. Название: Re: Урок: Создание динамических библиотек Отправлено: mutineer от Июнь 22, 2012, 18:05 Я имел в виду что вызов библиотечного метода с таким содержимым заблокирует вызывающего
Название: Re: Урок: Создание динамических библиотек Отправлено: raus от Июнь 23, 2012, 14:55 Я имел в виду что вызов библиотечного метода с таким содержимым заблокирует вызывающего ну мне этэ, наверно, не страшно..Название: Re: Урок: Создание динамических библиотек Отправлено: raus от Июнь 23, 2012, 15:03 Подскажите пожалуйста.
я создал dll в visual studio, но почему-то не могу вызвать метод из нее...еще создавал libку, метод вроде виден, но при сборке куча ошибок, ну это, вроде как и понятно, тк в ней Qt ...думаю с dllкой должно быть все норм..но вот не находится мой метод ??? Название: Re: Урок: Создание динамических библиотек Отправлено: xintrea от Июнь 24, 2012, 21:53 Подскажите пожалуйста. я создал dll в visual studio, но почему-то не могу вызвать метод из нее...еще создавал libку, метод вроде виден, но при сборке куча ошибок, ну это, вроде как и понятно, тк в ней Qt ...думаю с dllкой должно быть все норм..но вот не находится мой метод ??? Последовательность действий: 1. Выучить русский язык, его правила грамматики и пунктуации. Это требуется сделать чтобы люди могли понять тебя. 2. Начать читать документацию о проектировании dll и вообще о разделяемых библиотеках. 3. Понять что телепаты обитают на других форумах, и какие у тебя ошибки тут никто не знает. Название: Re: Урок: Создание динамических библиотек Отправлено: raus от Июнь 25, 2012, 11:30 2. Начать читать документацию о проектировании dll и вообще о разделяемых библиотеках. вот этот пункт помог. спасибо ;) |