В Delphi возможно связать интерфейс с объектом, который его не реализует - PullRequest
10 голосов
/ 21 сентября 2011

Я знаю, что Delphi XE2 имеет новый интерфейс TVirtualInterface для создания реализаций интерфейса во время выполнения.К сожалению, я не использую XE2, и мне интересно, что за хакерство используется для подобных вещей в старых версиях Delphi.

Допустим, у меня есть следующий интерфейс:

  IMyInterface = interface
  ['{8A827997-0058-4756-B02D-8DCDD32B7607}']
    procedure Go;
  end;

Возможно ли привязать к этому интерфейсу во время выполнения без помощи компилятора?

TMyClass = class(TObject, IInterface)
public
  function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
  function _AddRef: Integer; stdcall;
  function _Release: Integer; stdcall;
  procedure Go; //I want to dynamically bind IMyInterface.Go here
end;

Я пробовал простой hard cast:

var MyInterface: IMyInterface;
begin
  MyInterface := IMyInterface(TMyClass.Create);
end;

, но компилятор мешаетthis.

Затем я попробовал as приведение, и оно хотя бы скомпилировалось:

MyInterface := TMyClass.Create as IMyInterface;

Поэтому я представляю, что ключом является получение QueryInterface для возврата действительного указателя на реализациюзапрашиваемого интерфейса.Как мне построить его во время выполнения?

Я копался в System.pas, так что я, по крайней мере, смутно знаком с тем, как работают GetInterface, GetInterfaceEntry и InvokeImplGetter.(К счастью, Embacadero решил оставить исходные тексты вместе с оптимизированной сборкой).Возможно, я не правильно понимаю, но, похоже, могут быть записи интерфейса со смещением нуля, и в этом случае есть альтернативные способы назначения интерфейса с использованием InvokeImplGetter.

Моя конечная цель - симуляциянекоторые из возможностей динамических прокси и имитаторов, которые доступны в языках с поддержкой отражений.Если бы я смог успешно привязать объект, который имеет те же имена методов и сигнатуры, что и интерфейс, это был бы большой первый шаг.Это вообще возможно или я лаю не на том дереве?

Ответы [ 2 ]

8 голосов
/ 21 сентября 2011

Теоретически можно добавить поддержку интерфейса к существующему классу во время выполнения, но это будет действительно сложно, и для поддержки RTTI потребуется D2010 или более поздняя версия.

Каждый класс имеет VMT, а VMT имеет указатель на интерфейсную таблицу. (См. Реализацию TObject.GetInterfaceTable.) Таблица интерфейса содержит записи интерфейса, которые содержат некоторые метаданные, включая GUID, и указатель на сам интерфейс vtable. Если вы действительно хотите, вы можете создать копию интерфейсной таблицы (НЕ делайте это оригинальной; вы можете повредить память!) Добавить новую запись, содержащую новый интерфейс vtable с указателями указывая на правильные методы (которые вы можете сопоставить, посмотрев их в RTTI), а затем измените указатель на таблицу интерфейса класса, чтобы он указывал на новую таблицу.

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

7 голосов
/ 21 сентября 2011

Я не уверен, что вы хотите выполнить и почему вы хотите динамически привязать этот интерфейс, но вот способ сделать это (не знаю, подходит ли он вам):

А это соответствующий код теста:

var
  intf: IInterface;
  my: IMyInterface;
begin
  intf := TMyClass.Create(false);
  if Supports(intf, IMyInterface, my) then
    ShowMessage('wrong');

  intf := TMyClass.Create(true);
  if Supports(intf, IMyInterface, my) then
    my.Go;
end;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...