В 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 (Из-за этого возникают вопросы.).
Как переходить
Перевод на пакет у меня занял около полтора-двух часов, с перекурами.
- На первом этапе можно почти без особого гемороя сделать из DLL'ки BPL пакет при этом сохранив возможность компилировать и DLL.
- Берём исходники DLL. Все функции которые не экспортируются и глобальные переменные либо переносим в классы либо в отдельные модули которые не будут вставлятся в программу.
- По возможности при переносе функции используемые только одном классом делаем по человечески приватными. При этом если они на самом деле не используются компилятор выдаст предупреждение. При переносе
admin.dllя так смог спокойно похезать с десяток невнятных и ненужных функций. Мусор есть мусор, и ООП позволяет с ним разобраться лучше всего. - Теперь у всех экспортируемые функции убираем нафиг директиву
stdcall. - Тоже самое делаем для файла заголовков.
- У всех юнитов в названии добавляем имя dll, например был файл
PlanSh.pasсталAdmin.PlanSh.pas. Таким макаром мы избежим конфликтов имён юнитов в программах (например почти в каждой программе есть юнитGlobal_Variablesи внутри DLL'ки тоже есть такой). - Полученные исходники оформляем в пакет с BPL в конце названия (например
AdminBPL.dpk). При этом мы сохранили DLL версиюAdmin.dprкоторый может компилироваться в DLL. - Для удобства оба проекта объединить в группу проектов и компилить одним кликом.
- Теперь можно поставить в делфи этот пакет, и прописать в
SearchPathисходники. - Отрываем программу которую будем переносить на BPL
- Проходимся по всем юнитам и тупо заменяем заголовочный файл DLL'ки на список всех модулей внутри неё (т.е. все модули которые входят в состав BPL)
- Поскольку мы хорошенько засрём пространство имён неиспользуемыми модулями делаем фокус: в CnPacke нажимаем кнопочку «Очистить неиспользуемые модули» он пройдётся и всё что не нужно просто молча удалит.
- Делаем билд программы и запускаем. В окошке EventLog убеждаемся что у нас загружается DLL (
admin.dll). Грохаем DLL'ку - …
- PROFIT!!!
Второй этап
Второй этап можно начинать после того как все нужные DLL'ки и программы перенесены на BPL. Проект DLLки можно удалять за ненадобностью. Можно удалять функции типаимя_формы_show а вместо них использовать нормальное создание форм через ИмяФормы := КлассФормы.Create(Application). А потом делать полноценный рефакторинг BPL по мере надобности.
Вывод
На BPL переходить нужно и несложно. Я ГАРАНТИРУЮ ЭТО! Они сильно, очень сильно облегчат жизнь повышая производительность, надёжность, удобство и уменьшая размер.
Комментариев нет:
Отправить комментарий