Delphi: Используйте BPL пакеты вместо DLL библиотек!


Эта тема хорошо отхоливарена на SQL.RU. Вот основные цитаты оттуда передающие общий смысл этой статьи:

В DLL можно экспортировать только набор функций, если классы, наследование и прочие прелести ООП тогда BPL. Процедурное API в DLL устаревшее и скорее вредное.

Перестань парится фигней, форма в DLL это хак и будет это падать в самый не подходящий момент. Самое правильное — использовать Run-Time packages (для этого их и придумали).

Реальный пример

Была программа WorkTime, она использовала dll’ку admin.dll. Внутри это dll были всякие функции для соединения с БД и всякие формы настроек. Все функции которые она экспортирует были описаны в отдельном файле Admin_h.pas (типа Admin headers, «хашник»). Он был метров 8, потому что в него полностью скомпилировалась вся VCL библиотека и ещё куча бонусов.

Я перевёл эту dll в bpl пакет и поэкспериментировал с опциями компилировать пакет в программу или отдельно. Вышло вот что:

worktime без пакетов 353
worktime полностью со всеми пакетами 7630
admin.bpl 2639
admin.dll 8171
admin.dll без половины стандартных bpl 4132
worktime через admin.dll 6018 + admin.dll 8171 = 14189
worktime через admin.bpl 779.5 + admin.bpl 2639 = 3418

Итого при переходе на bpl мы экономим: 14189 — 3418 = 10771 Kb = 10.5 Mb. Нехило.

Итак преимущества пакетов

  • Пакет в отличии от DLL не чёрный ящик. Кликнув по функции ты попадёшь в тело функции а не в её объявление в хашнике.
  • Пакет можно встроить в IDE. Классы внутри пакета можно оформить в виде компонентов и показать панели. Всё установка нужной нам фичи займёт три секунды на поиск компонента на палитре и тупо поставить компонент на форму. Текст программы становится меньше (и меньше ошибок).
  • Можно использовать все типы и по полной.
  • Перейти к человеческому ООП.
  • Лучшая модульность.
  • Экономия размера.
  • Пакет можно скомпилировать прямо в программу, при этом неиспользуемые классы вырезаются компилятором и итоговое приложение занимает намного меньше места. И навсегда, навсегда избавится от DLL HELL!
  • Можно сразу посмотреть любую функцию из пакета просто кликнув по ней и сразу попадёшь в исходники.
  • Можно входить в тело функции из пакета в отладчике!
  • Не нужно вести отдельный файл с заголовками (который может устареть, несоответсовать действительности).
  • Не нужно описывать каждый тип в заголовочном файле.
  • Не нужно парится и указывать тип передачи параметров (stdcall). Кстати и в обычной DLL над этим можно не парится если уверены заранее что не будут эти функции вызываться из программ на других языках (С++).
  • Не нужно парится и экспортировать функции.
  • Не нужно парится и передавать в DLL переменную Application и ей подобные.
  • Можно использовать переменные из BPL’ки не передавая их через функции (например можно сходу использовать переменную MainDatabase а не извлекать её через функцию GetMainDatabase. Можно будет сразу статически использовать её в формах и задавать её как свойство во всяких там Query.
  • Из BPL’ки можно с чистой совестью использовать тип string. Настоящие DLL функции должны использовать только PСhar, аналог Сишного сhar *. Хотя добрый Borland сделал костыль в виде dll’ки borlndmm.dll и нужно обязательно в файле проекта программы подключить ПЕРВЫМ ShareMem.

Неудобства

  • В программе ты подключаешь не заголовочный файл а конкретный модуль (это нормально). Проблема на первых порах решается очень просто — там где раньше был модуль Admin_h вставляются все модули входящие в BPL а потом в CnPack’е вызывается команда убрать лишние модули.
  • Поскольку модули пакета находятся в том же адресном пространстве, нужно отслеживать глобальные переменные а точнее ИСКОРЕНИТЬ их вообще — это большое зло независимо от того dll это или пакет.
  • BPL нельзя использовать другим программ написанным на других языках (на самом деле можно но…). В любом случае так как они у нас написаны их тоже нельзя использовтаь потому что функции должны использовать только базовые типы Си (int, double, char *).
  • BPL зависимы от версии делфи. Нас это не парит.
  • BPL не могут ссылаться друг на друга (как и юниты), возможно придётся менять модульность.
  • Пакеты нужно ставить в IDE. Это делается один раз и ничего сложного в этом нет. Вот только при изменении BPL пакета иногда приходится его переставлять в IDE чтобы изменёные компоненты появились на палитре.
  • Геморок начинается если остальные DLL’ки настроены именно на DLL версию. Т.е. все зависимые DLL’ки тоже нужно переписать на BPL.
  • Если компилируешь программу только с runtime пакетом admin, то пакеты которые у него в зависимостях тоже компилируются в runtime (Из-за этого возникают вопросы.).

Как переходить

Перевод на пакет у меня занял около полтора-двух часов, с перекурами.

  1. На первом этапе можно почти без особого гемороя сделать из DLL’ки BPL пакет при этом сохранив возможность компилировать и DLL.
  2. Берём исходники DLL. Все функции которые не экспортируются и глобальные переменные либо переносим в классы либо в отдельные модули которые не будут вставлятся в программу.
  3. По возможности при переносе функции используемые только одном классом делаем по человечески приватными. При этом если они на самом деле не используются компилятор выдаст предупреждение. При переносе admin.dll я так смог спокойно похезать с десяток невнятных и ненужных функций. Мусор есть мусор, и ООП позволяет с ним разобраться лучше всего.
  4. Теперь у всех экспортируемые функции убираем нафиг директиву stdcall.
  5. Тоже самое делаем для файла заголовков.
  6. У всех юнитов в названии добавляем имя dll, например был файл PlanSh.pas стал Admin.PlanSh.pas. Таким макаром мы избежим конфликтов имён юнитов в программах (например почти в каждой программе есть юнит Global_Variables и внутри DLL’ки тоже есть такой).
  7. Полученные исходники оформляем в пакет с BPL в конце названия (например AdminBPL.dpk). При этом мы сохранили DLL версию Admin.dpr который может компилироваться в DLL.
  8. Для удобства оба проекта объединить в группу проектов и компилить одним кликом.
  9. Теперь можно поставить в делфи этот пакет, и прописать в SearchPath исходники.
  10. Отрываем программу которую будем переносить на BPL
  11. Проходимся по всем юнитам и тупо заменяем заголовочный файл DLL’ки на список всех модулей внутри неё (т.е. все модули которые входят в состав BPL)
  12. Поскольку мы хорошенько засрём пространство имён неиспользуемыми модулями делаем фокус: в CnPacke нажимаем кнопочку «Очистить неиспользуемые модули» он пройдётся и всё что не нужно просто молча удалит.
  13. Делаем билд программы и запускаем. В окошке EventLog убеждаемся что у нас загружается DLL (admin.dll). Грохаем DLL’ку
  14. PROFIT!!!

Второй этап

Второй этап можно начинать после того как все нужные DLL’ки и программы перенесены на BPL. Проект DLLки можно удалять за ненадобностью. Можно удалять функции типа имя_формы_show а вместо них использовать нормальное создание форм через ИмяФормы := КлассФормы.Create(Application). А потом делать полноценный рефакторинг BPL по мере надобности.

Вывод

На BPL переходить нужно и несложно. Я ГАРАНТИРУЮ ЭТО! Они сильно, очень сильно облегчат жизнь повышая производительность, надёжность, удобство и уменьшая размер.

Матчасть

Advertisements

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s