2012-07-12 23 views
7

Delphi parte:Come passare l'oggetto interfacciato alla chiamata della funzione Script Pascal?

Ho una classe con l'evento e da quell'evento ho bisogno di chiamare una procedura che passa l'oggetto interfacciato ad esso. Funziona bene con Delphi ma ho problemi a dichiararlo in Pascal Script.

Per lo sfondo - l'interfaccia IGPGraphics è una parte della Delphi GDI+ library e senza metodi è definito in questo modo:

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

Quello che segue è solo un pseudocodice Delphi semplificata di quello che ho bisogno di fare in 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 parte la compilazione:

Il mio obiettivo è di avere la classe con evento disponibile in Pascal Script e di passare quell'oggetto interfacciato a quella procedura proprio come sopra, quindi il primo che ho provato a dichiarare per la compilazione è il tempo (ma non lo sono nemmeno sicuro se questo è il modo giusto per farlo):

// 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 runtime script parte:

dove sto sicuramente perso è la parte runtime. Io non riesco a capire come ottenere l'oggetto interfacciato dallo stack delle chiamate e passarlo al mio procedimento 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; 

E la domanda è:

Qualcuno mi può suggerire come definire correttamente la compilazione e la parte runtime per questo caso o correggermi in qualche modo passando l'oggetto interfacciato?

P.S. mi dispiace per quel tag Inno-Setup ma forse qualcuno da lì ha provato a personalizzare InnoSetup in questo modo.

Grazie mille!

risposta

1

Se ho capito cosa stai chiedendo, vuoi passare un'interfaccia come parametro a un metodo. Sfortunatamente, non ho una risposta esatta a questo, ma so come assegnare un'interfaccia a una variabile globale per PascalScript. Ecco come lo faccio in Castalia:

Nell'evento PSScript OnCompile, aggiungere l'interfaccia con PS.Comp.AddInterface e quindi aggiungere ciascuno dei metodi necessari. Successivamente, aggiungi una variabile del tipo di interfaccia.Ecco come si presenta, ad esempio:

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'); 

Poi, in caso OnExectute, legare la variabile precedentemente creato per l'istanza:

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

Fatto questo, lo script ha accesso all'oggetto interfacciato attraverso la variabile, quindi in questo caso, lo script potrebbe contenere una chiamata a Clipboard.GetText e funziona come ci si aspetterebbe.

Che è testato e funziona.

Ora, vorrei ipotizzare che potresti essere in grado di utilizzare TPSScript.ExecuteFunction, passando il PIFVariant dall'alto, per avvicinarti a ciò che desideri. Non è qualcosa che ho provato, comunque.

Buona fortuna!

1

E 'difficile da credere, ma ho trovato il modo di farlo

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; 

Questo è fondamentalmente ciò che dovrebbe essere fatto.

  • utente l'istanza TPSExec, diciamo x
  • Poi ottenere il numero di procedura con il metodo x.GetProc
  • quindi creare la lista degli argomenti di tipo TPSList
  • Creare TPSVariantClass var, assegnare l'istanza della classe (che dovrebbe essere passato) per il suo campo di dati
  • assegnare anche x.FindType2 (btClass) per il suo campo VI.FType (non hanno assolutamente idea del perché questo funziona)
  • aggiungere un puntatore alla variabile TPSVariantClass alla lista TPSList
  • E ....... Chiamare la procedura x.RunProc (argList, proc); dove proc è il numero della procedura ottenuto in precedenza

Questo funziona per le classi, ma dovrebbe essere non molto diverso per le interfacce, basta usare tipo TPSVariantInterface per la variabile argomento anziché TPSVariantClass; tutto il resto dovrebbe essere lo stesso.

Spero che questo possa aiutare qualcuno forse.