2016-06-22 30 views
2

Ich folge dem How to call Delphi code from scripts running in a TWebBrowser DelphiDabbler-Tutorial (von Peter Johnson), damit Delphi JavaScript-Ereignisse TWebBrowser hören kann.Erweitern des externen TWebBrowser-Objekts zum Ausführen von Delphi-Code: Wie kann ich auf meine Formularkomponenten zugreifen?

Das funktioniert bis zu dem Punkt, wo ich sehe, meine Delphi-Prozeduren aufgerufen werden. Von dort aus muss ich jedoch einige Formularbeschriftungen aktualisieren, und ich sehe keine Möglichkeit, über diese Prozeduren auf mein Formular zuzugreifen.
Der Code DelphiDabbler Beispiel umgeht schön ‚direkten Zugang Form‘ von creatingTHintAction.Create(nil); der Sache es tun:

Diese lassen haben wir ganz schön unsere externe Objektimplementierung entkoppeln von der Form des Programms

Aber ich möchte auf mein Formular zugreifen! Zu übergebende Daten sind ganze Zahlen und Zeichenfolgen.
Ich könnte PostMessage() und WM_COPYDATA Nachrichten verwenden, aber diese würden immer noch das Formular behandeln. Und gibt es keinen "direkten" Weg zum Formular?

entsprechenden Code:

type 
    TWebBrowserExternal = class(TAutoIntfObject, IWebBrowserExternal, IDispatch) 
    protected 
     procedure SetVanLabel(const ACaption: WideString); safecall; // My 3 procedures that are called... 
     procedure SetNaarLabel(const AValue: WideString); safecall; // ... declared in the type library. 
     procedure SetDistanceLabel(AValue: Integer); safecall; 
    public 
     constructor Create; 
     destructor Destroy; override; 
    end; 

type 
    TExternalContainer = class(TNulWBContainer, IDocHostUIHandler, IOleClientSite) 
    private 
     fExternalObj: IDispatch; // external object implementation 
    protected 
     { Re-implemented IDocHostUIHandler method } 
     function GetExternal(out ppDispatch: IDispatch): HResult; stdcall; 
    public 
     constructor Create(const HostedBrowser: TWebBrowser); 
    end; 

constructor TExternalContainer.Create(const HostedBrowser: TWebBrowser); 
begin 
    inherited Create(HostedBrowser); 
    fExternalObj := TWebBrowserExternal.Create; 
end; 

der Form a property FContainer: TExternalContainer; hat, in der Formcreate ich fContainer := TExternalContainer.Create(WebBrowser); (Parameter der Entwurfszeit TWebBrowser ist), so dass der TExternalContainer.fExternalObj derjenigen zugeordnet ist.

Frage:

procedure TWebBrowserExternal.SetDistanceLabel(AValue: Integer); 
    begin 
    // **From here, how do I send AValue to a label caption on my form?** 
    end; 

Ich muss gestehen, dass Schnittstellen nicht meine Stärke sind ;-)

[Added:] Hinweis: Meine Formulare dynamisch alle erstellt werden, gibt es keine TForm Instanz in die aktuelle Einheit.

+0

wenn Sie das Formular Griff in TWebBrowserExternal, wie Funktion GetformHandle() zurück: Wort? – Passella

+0

Es ist möglich, einen [viel einfacheren Ansatz zum Implementieren externer Methoden in Delphi] (http://stackoverflow.com/a/22184159/859646) zu verwenden, wobei die von ObjComAuto.TObjectDispatch bereitgestellte spät gebundene Funktionalität verwendet wird. Auf diese Weise müssen Sie weder Schnittstellen noch eine Typbibliothek definieren. – JRL

Antwort

1

Unter den Rat Sie sagen, Sie Ihr Formular zugreifen möchten, aber Sie nicht wirklich - zumindest nicht direkt von Dsm in his/her answer, ich habe PostMessage/SendMessage verwenden entschieden (wie ich in meiner Frage angedeutet).

Zuerst gebe ich den Fenstergriff in der Konstrukteurs von TWebBrowserExternal und TExternalContainer und speichern Sie es als Privateigentum:

type 
    TWebBrowserExternal = class(TAutoIntfObject, IWebBrowserExternal, IDispatch) 
    private 
     fHandle: HWND; 
     procedure SendLocationUpdate(AWhere: Integer; ALocation: String); // Helper for SetVanLabel/SetNaarLabel 
    protected 
     procedure SetVanLabel(const AValue: WideString); safecall; 
     procedure SetNaarLabel(const AValue: WideString); safecall; 
     procedure SetDistanceLabel(AValue: Integer); safecall; 
    public 
     constructor Create(AHandle: HWND); 
     destructor Destroy; override; 
    end; 

type 
    TExternalContainer = class(TNulWBContainer, IDocHostUIHandler, IOleClientSite) 
    private 
     fExternalObj: IDispatch; // external object implementation 
    protected 
     { Re-implemented IDocHostUIHandler method } 
     function GetExternal(out ppDispatch: IDispatch): HResult; stdcall; 
    public 
     constructor Create(const HostedBrowser: TWebBrowser; AHandle: HWND); 
    end; 

Im Formcreate die TExternalContainer jetzt als

fContainer := TExternalContainer.Create(WebBrowser, Self.Handle); 

Die Set... erstellt wird Methoden werden wie folgt implementiert:

procedure TWebBrowserExternal.SetDistanceLabel(AValue: Integer); 
begin 
    PostMessage(fHandle,UM_UPDATEDIST,AValue,0); // const UM_UPDATEDIST = WM_USER + 101; 
end; 

procedure TWebBrowserExternal.SetNaarLabel(const AValue: WideString); 
begin 
    SendLocationUpdate(1,AValue); 
end; 

procedure TWebBrowserExternal.SetVanLabel(const AValue: WideString); 
begin 
    SendLocationUpdate(0,AValue); 
end; 

mit Helferfunktion:

procedure TWebBrowserExternal.SendLocationUpdate(AWhere: Integer; ALocation: String); 
var lCopyDataStruct: TCopyDataStruct; 
begin 
    lCopyDataStruct.dwData := AWhere; 
    lCopyDataStruct.cbData := 2 * 2 * Length(ALocation); 
    lCopyDataStruct.lpData := PChar(ALocation); 
    SendMessage(fHandle, WM_COPYDATA, wParam(fHandle), lParam(@lCopyDataStruct)); 
end; 

Meine Form enthält zwei Nachrichtenbehandlungsroutinen, die tatsächlich die Etiketten aktualisieren:

procedure UpdateDistMsgHandler(var Msg: TMessage); message UM_UPDATEDIST; 
procedure WMCopyData(var Msg : TWMCopyData) ; message WM_COPYDATA; 

procedure TFrmGoogleMapsLiveUpdate.UpdateDistMsgHandler(var Msg: TMessage); 
begin 
    LabelDistance.Caption := IntToStr(Msg.WParam); 
end; 

procedure TFrmGoogleMapsLiveUpdate.WMCopyData(var Msg: TWMCopyData); 
var 
    lWhere : integer; 
    lLocation : string; 
begin 
    lWhere := Msg.CopyDataStruct.dwData; 
    lLocation := String(PChar(Msg.CopyDataStruct.lpData)); 
    if lWhere = 0 then 
     LabelVan.Caption := lLocation 
    else 
     LabelNaar.Caption := lLocation; 
end; 
1

Sie sagen, Sie möchten auf Ihr Formular zugreifen, aber Sie nicht wirklich - zumindest nicht direkt. Sie möchten unsere externe Objektimplementierung ziemlich gut von der Programmform entkoppeln. Alles, was Sie wirklich tun müssen, ist eine Funktion oder eine Prozedur zu schreiben, um das zu tun, was Sie in Ihrem Programm wollen, und dann diese Funktion oder Prozedur über Ihren Webbrowser aufzurufen. Genau darum geht es bei Entkopplung und Schnittstellen. Sie nie behandeln Daten aus einer Anwendung direkt von einem anderen. Stattdessen verwenden Sie Funktionen und Prozeduren als Ihre Schnittstelle. Übrigens enthalten Schnittstellen deshalb nur Funktionen und Prozedurprototypen (und Eigenschaften - die aber nur intern als Funktionen und Prozeduren übersetzt werden) - niemals Daten.

Jetzt zu Ihrer spezifischen Frage. Natürlich können Sie auf Ihr Formular zugreifen - es ist eine globale Variable. Angenommen, Ihr Hauptformular ist vom Typ TMainForm in einer Einheit namens Main.pas, wird es eine globale Variable namens Main

var 
    MainForm : TMainForm; 

so in Ihrer Web-Browser-Einheit, in dem Implementierungsabschnitt Du

implementation 

uses Main; 

... 

procedure TWebBrowserExternal.SetDistanceLabel(AValue: Integer); 
begin 
    // **From here, how do I send AValue to a label caption on my form?** 
    FormMain.MyLabel.Caption := StrToInt(AValue); 
end; 
setzen würdest

Im Kontext dessen, was ich gesagt habe, ist SetDistanceLabel die Interface-Funktion und auf das Formular wird nur direkt von Ihrer Delphi-Anwendung aus zugegriffen.

+0

Ah, tut mir leid, ich hätte sagen sollen, dass ich nur Formulare habe, die dynamisch erstellt werden (Aktualisierung meiner Frage). Das bedeutet nicht, dass ich nicht auf sie zugreifen kann, aber ich müsste sie nachschlagen. Aber ich nehme deinen Rat bezüglich der Entkoppelung. –

+0

Das gleiche Prinzip gilt. Der Punkt über die 'uses'-Klausel in der Implementierung war, dass ich nicht erwartet habe, dass das Formular in derselben Einheit ist. Dies ist der Standardweg, um die Rekursion mit Einheiten zu umgehen. Verwenden Sie ggf. die globale Variable Application (in VCL.Forms oder FMX.Forms), um das von Ihnen benötigte Formular zu finden. – Dsm