Как передать интерфейсный объект в вызов функции Pascal Script?

Delphi часть:

У меня есть класс с событием, и из этого события мне нужно вызвать процедуру, передающую ему интерфейсный объект. Это прекрасно работает в Delphi, но у меня есть проблемы с объявлением его в Pascal Script.

На заднем плане - IGPGraphics Интерфейс является частью Delphi GDI+ library и без методов определяется так:

type
  IGdiplusBase = interface
  ['{24A5D3F5-4A9B-42A2-9F60-20825E2740F5}']
  IGPGraphics = interface(IGdiPlusBase)
  ['{57F85BA4-CB01-4466-8441-948D03588F54}']

Ниже приведен лишь упрощенный псевдокод Delphi того, что мне нужно сделать в Pascal Script:

type
  TRenderEvent = procedure(Sender: TObject; const GPGraphics: IGPGraphics) of object;
  TRenderClass = class(TGraphicControl)
  private
    FOnRender: TRenderEvent;
  public
    property OnRender: TRenderEvent read FOnRender write FOnRender;
  end;

// when the TRenderClass object instance fires its OnRender event I want to call 
// the RenderObject procedure passing the IGPGraphics interfaced object to it; I
// hope I'm doing it right, I'm just a newbie to this stuff - but it works so far
// in Delphi (since I didn't get it to work in Pascal Script)

procedure TForm1.RenderClass1Render(Sender: TObject; const GPGraphics: IGPGraphics);
begin
  RenderObject(GPGraphics, 10, 10);
end;

// what I need in Pascal Script is between these two lines; just pass the interface
// object from the event fired by component to the procedure called from inside it

procedure RenderObject(const GPGraphics: IGPGraphics; X, Y);
begin
  // and here to work with the interfaced object somehow
end;

Часть компиляции Pascal Script:

Моя цель - сделать класс с событием доступным в Pascal Script и передать этот интерфейсный объект в эту процедуру, как описано выше, поэтому сначала я попытался объявить это для времени компиляции (но я даже не уверен, что это правильный способ сделать это)

// the interface
PS.AddInterface(Cl.FindInterface('IUnknown'), StringToGuid('{57F85BA4-CB01-4466-8441-948D03588F54}'), 'IGPGraphics');
// the type for the event
PS.AddTypeS('TRenderEvent', 'procedure(Sender: TObject; const GPGraphics: IGPGraphics)');
// and the class with the event itself
with PS.AddClassN(PS.FindClass('TGraphicControl'), 'TRenderClass') do
begin
  RegisterProperty('OnRender', 'TRenderEvent', iptrw);
end;

Pascal Script runtime часть:

Где я определенно потерялся, это часть времени выполнения. Я не могу понять, как получить интерфейсный объект из стека вызовов и передать его в мою процедуру RenderObject:

function RenderClassProc(Caller: TPSExec; Proc: TPSExternalProcRec; Global, 
  Stack: TPSStack): Boolean;
var
  PStart: Cardinal;
begin
  PStart := Stack.Count-1;
  Result := True;
  if Proc.Name = 'RENDEROBJECT' then
  begin
    // how do I get the interfaced object from Stack (or whatever else) and pass 
    // it to the RenderObject proc here ? I can't find anything related about it
    // except functions that has no parameter index
    RenderObject(Stack.Get ?, Stack.GetInt(PStart-2), Stack.GetInt(PStart-3));
  end;
end;

И вопрос такой:

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

PS извините за этот тег Inno-Setup, но, возможно, кто-то оттуда пытался настроить InnoSetup таким образом.

Большое спасибо!

3 ответа

Решение

Если я понимаю, о чем вы спрашиваете, вы хотите передать интерфейс в качестве параметра методу. К сожалению, у меня нет точного ответа на это, но я знаю, как назначить интерфейс для глобальной переменной для PascalScript. Вот как я делаю это в Castalia:

В событии PSScript OnCompile добавьте интерфейс с PS.Comp.AddInterface, а затем добавьте каждый из необходимых методов. После этого добавьте переменную типа интерфейса. Это выглядит так, например:

with PS.Comp.AddInterface(ARunner.Comp.FindInterface('IUnknown'),
  StringToGUID('{0346F7DF-CA7B-4B15-AEC9-2BDD680EE7AD}'),
  'ICastaliaMacroClipboardAccess') do
begin
  RegisterMethod('function GetText: string', cdRegister);
  RegisterMethod('procedure SetText(AText: string)', cdRegister);
end;
PS.AddRegisteredVariable('Clipboard', 'ICastaliaMacroClipboardAccess');

Затем в событии OnExectute привяжите ранее созданную переменную к экземпляру:

P := PS.GetVariable('Clipboard'); //P is type PIFVariant
SetVariantToInterface(P, Implementing object as ICastaliaMacroClipboardAccess);    

Сделав это, сценарий получает доступ к связанному объекту через переменную, поэтому в этом случае сценарий может содержать вызов Clipboard.GetText, и он работает так, как вы ожидаете.

Это проверено и работает.

Теперь я хотел бы предположить, что вы можете использовать TPSScript.ExecuteFunction, передавая PIFVariant сверху, чтобы приблизиться к тому, что вы хотите. Это не то, что я проверял, однако.

Удачи!

Трудно поверить, но я узнал, как это сделать

procedure TApp.CallProcedureWithClassArg(x: TPSExec);
var
  o: TSampleObject;
  argumentList: TPSList;
  arg1: TPSVariantClass;
  proc: Integer;
begin
  o := TSampleObject.Create;
  o.X := 1; // do something with the object maybe
  proc := x.GetProc('TakeThis');
  argumentList := TPSList.Create;
  arg1.VI.FType := x.FindType2(btClass);
  arg1.Data := o;
  argumentList.Add(@arg1);
  x.RunProc(argumentList, proc);
  argumentList.Free;
end;

Это в основном то, что должно быть сделано.

  • Пользователь ваш экземпляр TPSExec, скажем, х
  • Затем получите номер процедуры, используя метод x.GetProc.
  • затем создайте список аргументов типа TPSList
  • Создайте TPSVariantClass var, назначьте свой экземпляр класса (который нужно передать) его полю данных
  • также присвойте x.FindType2(btClass) его полю VI.FType (понятия не имею, почему это работает)
  • добавить указатель на переменную TPSVariantClass в список TPSList
  • И....... вызвать процедуру x.RunProc(argList, proc); где proc - номер полученной ранее процедуры

Это работает для классов, но оно не должно сильно отличаться для интерфейсов, просто используйте тип TPSVariantInterface для переменной-аргумента вместо TPSVariantClass; все остальное должно быть таким же.

Я надеюсь, что это может кому-то помочь.

Интерфейс в PS требует объявления только во время компиляции (без RIRegister):

      procedure SIRegister_IHttpConnection2(CL: TPSPascalCompiler);
begin
  with CL.AddInterface(Cl.FindInterface('IUnknown'), StringToGuid('{B9611100-5243-4874-A777-D91448517116}'),
                                               'IHttpConnection2') do
 //or with CL.AddInterface(CL.FindInterface('IUNKNOWN'),IHttpConnection2,'IHttpConnection2') do begin
Другие вопросы по тегам