После того, как мы столь подробно разжевали наши плагины, и вы действительно осознали фразу "пакеты = DLL", то на вопрос "а как их загружать?" у вас уже должен быть ответ: "как DLL!" - т.е. через LoadLibrary.
Позвольте, воскликнет кто-то, но ведь для загрузки пакетов есть функция LoadPackage!
Давайте посмотрим её код и выясним отличия от LoadLibrary. Можно ещё посмотреть справку.
Итак, LoadPackage делает следующее:
- загружает бинарный модуль (module) пакета (привет, LoadLibrary).
- проверяет наличие повторов модулей Delphi (unit) в пакете (*).
- вызывает секции initialization модулей Delphi (unit) в пакете (вызов той самой экспортируемой функции 'Initialize').
Окей, тогда наш план выглядит вот так:
- вызываем LoadLibrary для загрузки модуля пакета (**).
- вызываем '_GetPluginInitInterface' для получения интерфейса (в этот момент эта функция сама вызовет 'Initialize' пакета - этим в ней занимается функция _Init).
- вызываем метод Init интерфейса.
Каждый пакет может зависеть от некоторого числа других пакетов (то самое "requires"). Каждый пакет также содержит один или несколько модулей Delphi. Проблема в том, что может случится множество Плохих Вещей (случайные AV и т.п.), если вы загрузите пакеты, содержащие один и тот же модуль (unit) в двух разных пакетах.
Чтобы этого не произошло, RTL Delphi везде использует функцию LoadPackage для загрузки пакетов, в которой и осуществляется проверка, что загружаемый модуль (module) не содержит повторов. Если обнаруживается конфликт, LoadPackage возбуждает исключение типа EPackageError с сообщением "Cannot load package 'XXX'. It contains unit 'YYY' which is also contained in package 'ZZZ'." (пакет при этом, разумеется, не загружается).
К нашему счастью, мы делаем полностью автономные пакеты, которые не зависят от других пакетов (поскольку мы удалили секцию requires целиком). Таким образом, каждый пакет имеет свой набор модулей (unit), которые никоим образом не зависят от модулей в других пакетах. Следовательно, эта проверка нам попросту не нужна (***).
В следующей части мы поговорим об интерфейсах и обработке ошибок.
(*) Да, я знаю, что две различные сущности - module (двоичный исполняемый модуль, т.е. EXE/DLL) и unit (модуль Delphi, т.е. pas-файл) на русский переводятся одинаково: "модуль". Поэтому на всякий случай, чтобы не возникло недоразумений, я подписываю в скобках, что имеется ввиду. Хотя обычно из контекста это и так понятно.
(**) На самом деле, будет использоваться SafeLoadLibrary - это обёртка вокруг LoadLibrary, которая гарантирует, что загружаемый плагин не попортит некоторые наши глобальные состояния (например, настройки сопроцессора). Вообще говоря, использовать просто LoadLibrary большого смысла нет. Везде, где есть вызовы LoadLibrary, их лучше заменить на SafeLoadLibrary.
(***) Конечно, кто-то может собрать плагин в виде обычного пакета - с requires других пакетов. Что будет при использовании таких плагинов? Будет ли это работать? Но об этом потом.
Большое спасибо за набор статей, жду с нетерпением продолжения.Одно интересно, я смогу использовать классы которые хранятся моих bpl?
ОтветитьУдалить>>> я смогу использовать классы которые хранятся моих bpl?
ОтветитьУдалитьПонятия не имею :D
Скорее всего без проблем, но надо смотреть...
В любом случае этот цикл ещё далеко не закончен.
с нетерпением жду продолжения, вот если б к новому году завершить... :)
ОтветитьУдалить