Управление памятью для платформы плагинов Delphi на основе TInterfacedClass - PullRequest
6 голосов
/ 27 марта 2011

Для серверной инфраструктуры плагинов я хотел бы реализовать библиотеки DLL, которые предоставляют метод RegisterPlugin, который возвращает ссылку на класс (TInterfacedClass).

Затем приложение хоста создает экземпляры этого классаи экземпляры будут выполняться в контексте потока (ов) хоста.(Это отличается, например, от среды плагинов VCL Jedi, которая создает плагин в DLL или BPL и возвращает экземпляр на хост.)

Первые тесты пока не выявили проблем.Однако существуют ли скрытые проблемы с управлением памятью, о которых мне следует знать?Поскольку я использую Delphi 2009 для этого проекта, FastMM4 является диспетчером памяти по умолчанию.

Вот эскиз проекта DLL плагина:

library ExamplePlugin;
uses
  ...
type
  TPluginOne = class(TInterfacedObject, ...)
  ...
  end;

function RegisterPlugin: TInterfacedClass; stdcall;
begin
  Result := TPluginOne;
end;

exports
  RegisterPlugin;

{ TPluginOne }
// ... plugin class implementation

begin  
end.

Ответы [ 2 ]

7 голосов
/ 27 марта 2011

Нет проблем с диспетчером памяти, потому что FastMM работает как менеджер совместно используемой памяти между EXE и DLL. Но мне действительно не нравится концепция передачи чистых объектов или (наихудших) метаклассов между DLL и EXE. Проблема в том, что TInterfacedObject из EXE не совпадает с TInterfacedObject из DLL! Конечно, они могут выглядеть точно так же, но это не так! И если вы когда-нибудь обновите версию Delphi для EXE или для какой-либо из DLL, вам нужно будет перестроить все (тем самым потеряв все преимущества, которые вы получили от реализации инфраструктуры плагинов).

Гораздо более переносимым решением было бы вернуть "Factory Interface", что-то вроде:

IFactoryInterface = interface
[GUId-goes-here]
  function MakeWhateverInterfaceYouNeed: IUnknownDerivate
end;

затем экспортируйте функцию с такой подписью:

function RegisterPlugin: IFactoryInterface;
2 голосов
/ 27 марта 2011

Ваш код неполон, но из того, что вы включили, есть один очевидный недостаток.

Вы, похоже, экспортируете класс (TInterfacedClass) из DLL.Это может вызвать проблемы, когда клиенты пытаются использовать ваш класс с другой версией Delphi.Более того, это сделает их беспомощными, если они захотят писать плагины на другом языке.

Лично я бы выбрал интерфейс на основе COM, который позволит авторам плагинов создавать плагины влюбой язык.На самом деле это та самая проблема, которую COM придумал для решения.

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

ОБНОВЛЕНИЕ

Космин указывает в комментарии еще один недостаток с экспортом классов через границы модуля.Это в основном то, что вы не должны делать.COM был разработан именно для этой цели, и он по-прежнему должен быть для вас первым выбором.Интерфейсы Delphi, совместимые с COM, поэтому вы можете получить те же преимущества бинарного взаимодействия без необходимости создавать серверы, регистрировать CLSID и т. Д.

Я думаю, что ваш плагин должен выглядеть следующим образом:

library ExamplePlugin;

type
  TPluginOne = class(TInterfacedObject, IPlugin)
  [GUID]
  public
    constructor Create(const Host: THostApp);
  end;

function RegisterPlugin(const Host: IHostApp): IPlugin; stdcall;
begin
  Result := TPluginOne.Create(Host);
end;

exports
  RegisterPlugin;

begin  
end.
...