Как передать интерфейсный объект в вызов функции 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