Расширение внешнего объекта TWebBrowser для выполнения кода Delphi: как получить доступ к компонентам моей формы?

Я следую Как вызвать код Delphi из скриптов, запущенных в TWebBrowser DelphiDabbler учебник (автор Peter Johnson), позволяющий Delphi прослушивать TWebBrowser события JavaScript.

Это работает до того момента, когда я вижу, что мои процедуры Delphi вызываются. Однако оттуда мне нужно обновить некоторые метки форм, и я не вижу способа получить доступ к моей форме из этих процедур.
Код примера DelphiDabbler изящно обходит «прямой доступ к форме» с помощью создание THintAction.Create(nil);, которое будет делать свое дело:

Это позволяет нам довольно хорошо отделить нашу реализацию внешнего объекта от формы программы.

Но я хочу получить доступ к своей форме! Передаваемые данные представляют собой целые числа и строки.
Я мог бы использовать сообщения PostMessage() и WM_COPYDATA, но для них все равно потребуется дескриптор формы. И нет ли «прямого» пути к форме?

Соответствующий код:

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;

У формы есть property FContainer: TExternalContainer;, в FormCreate я делаю fContainer := TExternalContainer.Create(WebBrowser); (параметр - время разработки TWebBrowser), поэтому этому присваивается TExternalContainer.fExternalObj.

Вопрос:

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

Должен признаться, что интерфейсы не моя сильная сторона ;-)

[Добавлено:] Примечание. Все мои формы создаются динамически, в текущем модуле нет экземпляра TForm.


person Jan Doggen    schedule 22.06.2016    source источник
comment
если вы вернете дескриптор формы в TWebBrowserExternal, например функцию GetformHandle(): Word ?   -  person Passella    schedule 22.06.2016
comment
Можно использовать гораздо более простой подход для реализации внешних методов в Delphi, используя функциональность позднего связывания, предоставляемую ObjComAuto. TObjectDispatch. Таким образом, вам не нужно определять ни интерфейсы, ни библиотеку типов.   -  person JRL    schedule 10.12.2016


Ответы (2)


Вы говорите, что хотите получить доступ к своей форме, но на самом деле это не так — по крайней мере, напрямую. Вы хотите «хорошо отделить нашу реализацию внешнего объекта от формы программы». Все, что вам действительно нужно сделать, это написать функцию или процедуру, которая будет делать то, что вы хотите внутри вашей программы, а затем вызвать эту функцию или процедуру из вашего веб-браузера. Это и есть развязка и интерфейсы. Вы никогда не обрабатываете данные, принадлежащие одному приложению, непосредственно из другого. Вместо этого вы используете функции и процедуры в качестве своего интерфейса. Между прочим, именно поэтому интерфейсы содержат только прототипы функций и процедур (и свойства, но внутри они просто транслируются как функции и процедуры), а не данные.

Теперь о вашем конкретном вопросе. Конечно, вы можете получить доступ к своей форме — это глобальная переменная. Предположим, что ваша основная форма имеет тип TMainForm в модуле с именем Main.pas, там будет глобальная переменная с именем MainForm.

var
  MainForm : TMainForm;

поэтому в вашем модуле веб-браузера в разделе реализации вы бы поместили

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;

В контексте того, что я сказал, SetDistanceLabel — это функция интерфейса, а доступ к форме возможен только из вашего приложения Delphi.

person Dsm    schedule 22.06.2016
comment
Ах, извините, я должен был сказать, что у меня есть только формы, которые создаются динамически (обновление моего вопроса). Это не значит, что я не могу получить к ним доступ, но мне придется их поискать. Но я следую твоему совету по поводу развязки. - person Jan Doggen; 22.06.2016
comment
Применяется тот же принцип. Дело в предложении «uses» в реализации заключалось в том, что я не ожидал, что форма будет в одном и том же модуле. Это стандартный способ обойти рекурсию с единицами измерения. При необходимости используйте глобальную переменную Application (в VCL.Forms или FMX.Forms соответственно), чтобы найти нужную форму. - person Dsm; 22.06.2016

Следуя совету Вы говорите, что хотите получить доступ к своей форме, но на самом деле это не так — по крайней мере, напрямую из Dsm в его/ее ответ, я решил использовать PostMessage/SendMessage (как я намекнул в своем вопросе).

Сначала я передаю дескриптор окна в конструкторы TWebBrowserExternal и TExternalContainer и сохраняю его как частное свойство:

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;

В FormCreate TExternalContainer теперь создается как

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

Методы Set... реализованы как:

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;

со вспомогательной функцией:

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;

Моя форма содержит два обработчика сообщений, которые фактически обновляют метки:

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;
person Jan Doggen    schedule 23.06.2016