2 декабря 2008 г.

Создаём систему плагинов, часть 3

Прежде чем переходить к реализации нашей функции _Init, пощупаем сначала эти "пакеты в виде DLL".

Откройте Delphi, закройте все свои проекты и выберите File/New/Delphi projects/Package.

Delphi создаст обычный пустой пакет. Откройте менеджер проекта (View/Project Manager) или исходник (Project/View Source) - вы увидите, что этот пакет зависит от стандартного пакета Delphi rtlXXX.bpl (вы увидите слова "requires" и "rtl"), где XXX - это номер версии Delphi (например 120 для версии 12.0, т.е. Delphi 2009).

Это пакет в стиле Delphi. Если вы скомпилируете его, то увидите маленький bpl-файл размером около 10 Кб. Фактически этот файл представляет собой обычную DLL. Вы можете открыть его в любой утилите просмотра структуре PE-файлов.

Этот модуль экспортирует две процедуры инициализации Initialize и Finalize, которые выполняют в пакете всю ту работу, которая в обычных DLL находится внутри DLLMain. А также он экспортирует несколько служебных функций, предназначенных для получения информации о пакете.

Сам пакет практически не содержит кода - поэтому он такой небольшой, а весь рабочий код расположен в стандартном пакете Delphi rtl120.bpl (или какие там цифирьки у вашей версии Delphi). Собственно и зависит он только от двух модулей: kernel32.dll и rtl120.bpl.

Пакеты такого рода используется в Delphi повсеместно. Вы получаете удобство (общий менеджер памяти, повторное использование кода и т.п.), но платите за это привязкой к конкретной версии компилятора.

Уберём зависимость от компилятора Delphi. Для этого удалите пакет rtl из списка requires (например, удалив его из менеджера проектов или вручную отредактировав исходник). Перекомпилируем проект.

Теперь наш bpl-файл занимает около 100 Кб. Если вы посмотрите на его секцию экспорта, то станет ясно, почему он подрос: в него вошёл стандартный код, ранее располагавшийся в rtl120.bpl (а именно - модуль System). Теперь секция экспорта просто пестрит функциями, связанными с модулем System. Разумеется, Initialize и Finalize остались как были. Кроме того, в секции импорта исчезла зависимость от модуля rtl120.bpl, зато появились зависимости от advapi32.dll, user32.dll и oleaut32.dll.

Вот ровно такого рода пакеты мы и будем использовать как плагины. Если не считать тонну экспортируемых функций, этот файл представляет собой обычную DLL, у которой работа, выполняемая в DLLMain, сидит в Initialize и Finalize. По сигнатуре вызова, обе эти функции - это обычные процедуры без параметров с типом вызова register.

Поэтому, функцию _Init (а заодно и потребующуюся нам в скором времени функцию _Done) можно реализовать очень просто вот так:
function GetProcAddress(hModule: HMODULE; lpProcName: PAnsiChar): Pointer; stdcall; external 'kernel32.dll' name 'GetProcAddress';

procedure _Init;
type
  TPackageLoad = procedure;
var
  PackageLoad: TPackageLoad;
begin
  @PackageLoad := GetProcAddress(HInstance, 'Initialize'); //Do not localize
  PackageLoad;
end;

procedure _Done;
type
  TPackageUnload = procedure;
var
  PackageUnload: TPackageUnload;
begin
  @PackageUnload := GetProcAddress(HInstance, 'Finalize'); //Do not localize
  PackageUnload;
end;

Как нужно загружать такие пакеты - это в следующий раз.

6 комментариев :

  1. Супер жду продолжения. потом можно будет это сверстать в общий документик и с семплом?

    ОтветитьУдалить
  2. Да, продолжение будет, но... не сразу :(
    У меня тут "небольшие" проблемы с домашним ноутом (никогда не берите Acer :) ), поэтому такая задержка.

    >>> потом можно будет это сверстать в общий документик и с семплом
    Примеры, разумеется, будут. По поводу цельного текста одной страницей - навряд ли. Между частями я вставляю ссылки и, может быть, потом ещё добавлю тэгов.

    ОтветитьУдалить
  3. ну и на том респект и сенкс!

    ОтветитьУдалить
  4. Есть еще один недостаток у bpl по сравнению с dll.
    В случае с dll у нас есть линкер, который выкинет из скомпилированного модуля код который не используется в экспортируемых процедурах.
    В случае с bpl в PE модуле будут все функции и методы из всех модулей. Если использовать сторонние бибилотеки, то размер таких bpl может стать очень большим. ;(

    Что касается раздутой секции экспорта, то есть утилиты (PE Corrector)которые могут удалить лишние функции из секции экспорта. Это позволит уменьшить размер модуля.

    ОтветитьУдалить
  5. Да, хорошее замечание, спасибо.

    ОтветитьУдалить
  6. Читаю всё, что пишешь - очень доволен...

    ОтветитьУдалить

Можно использовать некоторые HTML-теги, например:

<b>Жирный</b>
<i>Курсив</i>
<a href="http://www.example.com/">Ссылка</a>

Вам необязательно регистрироваться для комментирования - для этого просто выберите из списка "Анонимный" (для анонимного комментария) или "Имя/URL" (для указания вашего имени и (опционально) ссылки на сайт). Все прочие варианты потребуют от вас входа в вашу учётку (поддерживается OpenID).

Пожалуйста, по возможности используйте "Имя/URL" вместо "Анонимный". URL можно просто не указывать.

Ваше сообщение может быть помечено как спам спам-фильтром - не волнуйтесь, оно появится после проверки администратором.