Динамически загружаемый код совместного использования / передача объектов BPL

Я раздумывал над идеей использования динамической загрузки BPL и передачи экземпляров объектов из основного приложения в метод в BPL. Это создает проблему между модулями, используемыми приложением и BPL.

Я написал небольшой прототип, который сделал это, и мне было любопытно, как Delphi внутренне управляет различиями между классами, определенными в приложении, и BPL.

Например, скажем, базовый класс виджетов, например:

TmyWidget = class
private
  fId:Integer;
  fDescription:String;
public
  procedure DoSomething1();
end;

Теперь приложение и BPL построены с использованием модуля, содержащего класс TmyWidget. Позже что-то меняется в TMyWidget, и приложение перестраивается, но BPL нет (или наоборот). Я добавил еще один метод DoSomething2 () и создал экземпляр TmyWidget в приложении и передал его в BPL для обработки и в базовый пример, это сработало. Но это явно чревато потенциальными проблемами.

Если другой динамически загружаемый BPL также использует TmyWidget, все становится еще интереснее. Кажется, это работает, но определенно не кажется идеальным.

Главный вопрос - как обычно передавать объекты в главное приложение и из них, а также из библиотек DLL или BPL? Я никогда не пробовал это раньше и, вероятно, по уважительной причине, но у меня есть идея, которая поддается этому подходу ...

Я предполагаю, что лучший подход - сериализовать объект, передать эти байты и десериализовать его в DLL / BPL, учитывая при этом потенциальные различия версий между хостом и динамически загружаемым модулем, но я надеялся, что новый SimpleSharedMem опция может принести эту новую функциональность без накладных расходов на сериализацию, но она кажется не очень полезной, если только вы не строго соблюдаете перестройку приложения и dll при любых изменениях общего кода ... но в этом прототипе приложение будет оставаться довольно постоянным и динамически загружаемые модули будут часто меняться с добавлением функций в TmyWidget. (Серверное приложение служит фабрикой для создания TmyWidget на основе клиентских запросов, и приложение будет передавать экземпляры в различные модули для обработки.)


person Darian Miller    schedule 09.03.2011    source источник
comment
Конечно, правильное решение - иметь только один экземпляр каждого устройства. Почему у вас один и тот же блок дважды? Разве вы не можете организовать код, чтобы этого избежать, как задумано природой?   -  person David Heffernan    schedule 09.03.2011
comment
Да, его можно (нужно) реорганизовать - он начался с того, что поигрался с идеей, которая превратилась в загрузку BPL, и эти BPL, которые начинались как DLL с некоторым общим кодом, не были сегментированы и по какой-то причине это сработало, и я было любопытно, почему.   -  person Darian Miller    schedule 09.03.2011


Ответы (2)


... было любопытно, как Delphi внутренне управляет различиями между классами, определенными в приложении, и BPL

Delphi справляется с этим, не разрешая этого. Вы не можете иметь модуль с тем же именем в нескольких пакетах одновременно: если вы это сделаете, вы получите сообщение об ошибке, в котором говорится что-то похожее на Package XYZ already contains ABC (давно не видел этого ...). Поскольку имя типа включает в себя имя модуля, у вас не может быть одного и того же типа в двух разных пакетах. Если только это не интерфейс, определяемый его GUID, но это другая история.

... как обычно передавать объекты в основное приложение и из них, а также из библиотек DLL или BPL?

Вы не передаете объекты в DLL, это плохая идея. Когда вам нужно передать объекты в BPL, убедитесь, что базовый класс для этого BPL определен в 3-м BPL.

Пример. Полиморфное поведение вашего TmyWidget, вероятно, определяется с помощью каких-то виртуальных методов. Убедитесь, что у вас есть класс TmyWidgetBase, который определяет все эти виртуальные методы, унаследуйте все ваши TmyWidget от этого базового класса и передайте объекты с типом TmyWidgetBase. Убедитесь, что класс TmyWidgetBase находится в собственном пакете.

Когда я попытался сделать это, я получил крошечный "bootstrap" exe и множество BPL. По сути, вся логика была в BPL, чтобы облегчить передачу объектов.

person Cosmin Prund    schedule 09.03.2011
comment
Примечание. Я не получил, что пакет уже содержит ошибку xx с двумя динамически загружаемыми BPL, которые включают в себя один и тот же модуль TmyWidget в каждом (динамически загружаются с помощью LoadPackage во время выполнения). не может случиться. - person Darian Miller; 09.03.2011
comment
@Darian, к сожалению, я не могу сейчас это проверить. Возможно, Delphi не выполняет тесты на повторяющиеся имена модулей, если между пакетами нет условия require. - person Cosmin Prund; 09.03.2011
comment
Я получаю сообщение об ошибке, если создаю приложение с пакетами времени выполнения, в противном случае ошибки нет. Тогда он просто действует как обычная DLL с вызываемыми инициализациями / финализациями. - person Darian Miller; 09.03.2011
comment
Когда вы создаете Exe с пакетами времени выполнения, вы создаете отношение требования, о котором я упоминал. Пакеты действительно имеют ключевое слово requires в файле DPK, а exe - нет, но это то же самое. Если вы сделаете это, и Delphi сразу же не будет жаловаться, вы столкнетесь, по крайней мере, с несколькими проблемами: операторы is и as не будут работать должным образом, потому что каждый пакет содержит свою собственную копию структуры информации о классе. Я отправлю ненужный большой exe-файл, потому что он также содержит свою собственную копию RTL и VCL (и вам нужно будет отправить их версии BPL для нужд пакета). - person Cosmin Prund; 09.03.2011
comment
@Darian, вы пробовали загрузить два пакета, содержащих один и тот же модуль, в исполняемый файл во время выполнения? Это то, что я хотел протестировать, но не могу этого сделать прямо сейчас, потому что у меня есть некоторые проблемы с отсутствующими пакетами времени выполнения, и у меня нет времени (и желания) исправить это. - person Cosmin Prund; 09.03.2011
comment
Да, это уже давно прошло ... немного взбесилась ночной идеей. Если все в основном находится в пакетах времени выполнения, тогда это становится очень забавным, когда базовые пакеты нуждаются в обновлении, и есть несколько загруженных зависимых BPL, которые необходимо заменить, пока сервер в реальном времени обслуживает клиентские запросы. Вернемся к DLL черного ящика и отключимся от этого динамического пакета времени выполнения, загружающего круговую логику .. Спасибо за спасательный круг! - person Darian Miller; 09.03.2011
comment
И да, я загрузил два пакета, каждый из которых содержал один и тот же модуль, во время выполнения с помощью LoadPackage. Даже основное приложение включало модуль, чтобы сделать его счастливыми 3 копиями определения класса, загруженными потенциальным кошмаром, когда все 3 имеют разные реализации класса. Delphi разрешил это, и это «работало», даже когда я расширил класс в одном из BPL, а затем снова в основном приложении, но это, безусловно, кошмар, который ждет, чтобы испытать его в любом другом приложении, кроме тестового. - person Darian Miller; 09.03.2011
comment
Никогда не следует использовать LoadPackage с приложением, которое не было скомпилировано по крайней мере с пакетом rtl в качестве пакета времени выполнения. - person Thorsten Engler; 10.03.2011
comment
@Thorsten - можешь добавить это в вики? :) Я никогда раньше не использовал LoadPackage ... просто гнался за дикой идеей. - person Darian Miller; 10.03.2011
comment
@Darian, пакеты работают очень хорошо, когда вы хотите иметь возможность передавать объекты между модулями (exe / dll). Но важно, чтобы вы передавали через границы модуля только те типы, которые были определены в пакете, который требуется обоими модулями (для пакетов, для которых требуется условие, для exe и dll это пакеты времени выполнения, которые вы указали в параметрах компилятора). - person Thorsten Engler; 10.03.2011
comment
Проверки повторяющихся единиц пропускаются, если функция AValidatePackage возвращает True. Имейте в виду, что это может вызвать странное и непредсказуемое поведение, если загружены два пакета, содержащие одинаковые единицы и типы. - из SysUtils.pas. Не видел этого нигде в документации. - person Kenneth Cochran; 10.03.2011
comment
@CodelElegance ... спасибо, я видел этот комментарий в источнике постфактум. По сути, Delphi позволяет этому случиться, но они не гарантируют, что произойдет, когда вы это сделаете. Я избегаю этого из-за одновременного обновления более чем одной BPL на лету. - person Darian Miller; 10.03.2011
comment
Если вы считаете ограничения стандартной dll слишком громоздкими, вам следует изучить программирование на Com. Delphi имеет отличную поддержку для этого и дает вам некоторые из тех же преимуществ, что и использование bpls. - person Kenneth Cochran; 10.03.2011

В одном из проектов, над которыми я работал, уже более десяти лет успешно используется большое количество пакетов среды выполнения, поэтому я поделюсь некоторыми из моих опытов работы с пакетами.

Как отметил Космин, разные пакеты не могут содержать одни и те же блоки. Если вы используете неявное связывание, добавив пакет в предложение требует другого пакета или добавив пакет в список Runtime packages в параметрах проекта, компилятор выполнит работать на вас и сообщить об одном из следующих сообщений об ошибке:

E2199: Packages '%s' and '%s' both contain unit '%s' (если ваш компилируемый проект зависит от двух пакетов, содержащих один и тот же модуль)

E2200: Package '%s' already contains unit '%s' (если вы компилируете пакет, содержащий модуль, он уже содержится в одном из пакетов, от которого он зависит)

Если вы используете явное связывание с помощью LoadPackage, обычно выполняется попытка проверки во время выполнения (хотя ее можно обойти) и сгенерировать:

EPackageError: невозможно загрузить пакет "% s". Он содержит модуль "% s", который также содержится в пакете "% s".

Исправить эти ошибки на самом деле не так уж и сложно.

Если у вас есть два пакета, которые оба должны использовать модуль, просто позвольте одному из них содержать модуль, а другому - первый. график зависимостей пакетов

Если у вас есть два пакета, которым необходимо использовать отдельный модуль друг друга, вам придется переместить эти модули в новый пакет, от которого оба могут зависеть. график зависимостей

Неявно связанные пакеты имеют то преимущество, что вы можете напрямую обращаться к определениям классов, как если бы они были статически связаны. Просто добавьте модуль в раздел uses модуля, в котором его нужно использовать. Компилятор и среда выполнения позаботятся обо всем.

Явно связанные пакеты должны будут полагаться на регистрацию класса в разделе инициализации.

person Kenneth Cochran    schedule 10.03.2011