如何在Pascal脚本函数调用中传递接口对象?

7

Delphi部分:

我有一个带有事件的类,需要从该事件中调用一个过程,并将接口对象传递给它。在Delphi中可以正常工作,但在Pascal Script中声明时遇到了问题。

背景说明 - IGPGraphics接口是Delphi GDI+库的一部分,没有定义方法,如下所示:

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

以下是我需要在Pascal Script中完成的简化Delphi伪代码:
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脚本编译部分:

我的目标是在Pascal脚本中使用具有事件的类,并将该接口对象传递给该过程,就像上面那样。因此,我首先尝试在编译时声明以下内容(但我甚至不确定这是否是正确的方法):

// 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脚本运行时部分:

我确实迷失在运行时部分。我无法弄清楚如何从调用堆栈中获取接口对象并将其传递给我的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;

问题是:

有没有人能建议我如何正确定义这种情况下的编译和运行时部分,或者通过某种方式传递接口对象进行修正?

P.S. 对于使用Inno-Setup标签的情况表示抱歉,但也许有来自那里的人尝试过以这种方式自定义InnoSetup。

非常感谢!

3个回答

1
在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

1

如果我理解你的问题,你想将一个接口作为参数传递给一个方法。不幸的是,我没有确切的答案,但我知道如何将一个接口分配给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');

然后,在OnExecute事件中,将先前创建的变量绑定到实例:
P := PS.GetVariable('Clipboard'); //P is type PIFVariant
SetVariantToInterface(P, Implementing object as ICastaliaMacroClipboardAccess);    

做到这一点后,脚本通过变量可以访问接口对象,因此在这种情况下,脚本可以包含对Clipboard.GetText的调用,并且它按照您的期望工作。

已经测试并且有效。

现在,我推测您可能能够使用TPSScript.ExecuteFunction,传入上面的PIFVariant,以更接近您想要的结果。不过,我没有测试过。

祝你好运!


1

很难相信,但我找到了如何做到这一点

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
  • 然后使用x.GetProc方法获取过程编号
  • 创建TPSList类型的参数列表
  • 创建TPSVariantClass var,将类实例(应传递)分配给其Data字段
  • 还将x.FindType2(btClass)分配给其VI.FType字段(绝对不知道为什么这样可以)
  • 将指向TPSVariantClass变量的指针添加到TPSList列表中
  • 然后.......调用x.RunProc(argList,proc)过程;其中proc是之前获取的过程编号

这适用于类,但对于接口应该没有太大区别,只需使用TPSVariantInterface类型替换参数变量中的TPSVariantClass即可;其他所有内容应该相同

希望这可能会帮助某些人。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接