如何使用Chromium和Delphi 6在网页中将“本地函数”暴露给JavaScript?

15

我已经使用Delphi Chromium Embedded成功将Chromium嵌入到我的Delphi 6项目中。现在,我想能够执行Javascript代码并将结果返回给我的主机Delphi应用程序。我的当前方法是调用ExecuteJavascript()并使用一个DOM元素,让Javascript调用将其结果写入该元素,并从Delphi的TTimer方法中轮询该元素以检索结果。然而,我读到了关于使用本地函数和V8扩展来使Javascript调用“回调”到我的Delphi代码作为一种接收结果的方法:

http://magpcss.org/ceforum/viewtopic.php?f=7&t=180

我想尝试这个,也想知道如何将基于Delphi的事件监听器附加到网页中的DOM元素(例如onblur,onmousedown等)。如果有人知道在哪里找到这些示例,希望能展示给我看。

2
哇,Delphi-6需要手动处理所有Unicode,而Chromium则具有完全的Unicode支持。对我来说,这听起来像是阻抗不匹配 :) - Jeroen Wiert Pluimers
"阻抗不匹配"。房间里有一位电子工程师。 :) 你说得对,但Delphi 6仍然是我最喜欢的IDE。至少目前是这样。 - Robert Oschler
我希望自己是一名电子工程师;我从未有机会学习它,但仍然觉得了解电子工程并对可能出现的问题有感觉在IT领域非常有用。我知道你喜欢Delphi 6,但我敦促你看看更当前的版本。最近我不得不回去使用旧版,发现错过了很多东西... - Jeroen Wiert Pluimers
1个回答

14

添加监听器非常容易(仅适用于旧版本的CEF):

procedure MouseDownCallback(const Event: ICefDomEvent);
begin
  ShowMessage('Mouse down on '+Event.Target.Name);
end;

procedure AttachMouseDownListenerProc(const Doc: ICefDomDocument);
begin
  Doc.Body.AddEventListenerProc('mousedown', True, MouseDownCallback);
end;

procedure TMainForm.Button1Click(Sender: TObject);
begin
  ChromiumComponent.Browser.MainFrame.VisitDomProc(AttachMouseDownListenerProc);
end;

关于直接获取JavaScript结果的扩展功能:主干代码库尚未包含它们(还?)。似乎正在进行中。
编辑:
通过扩展摆脱轮询:
确实,您的JavaScript代码可以使用扩展回调到Delphi代码。此外,您可以从JavaScript发送值到Delphi——这可以用于在不需要轮询的情况下传输结果。
首先,在您的初始化部分注册扩展,该扩展将创建一个JavaScript对象以便在回调时使用:
procedure RegisterExtension;
var
  Code:string;
begin

  Code :=
   'var cef;'+
   'if (!cef)'+
   '  cef = {};'+
   'if (!cef.test)'+
   '  cef.test = {};'+
   '(function() {'+
   '  cef.test.__defineGetter__(''test_param'', function() {'+
   '    native function GetTestParam();'+
   '    return GetTestParam();'+
   '  });'+
   '  cef.test.__defineSetter__(''test_param'', function(b) {'+
   '    native function SetTestParam();'+
   '    if(b) SetTestParam(b);'+
   '  });'+
   '  cef.test.test_object = function() {'+
   '    native function GetTestObject();'+
   '    return GetTestObject();'+
   '  };'+
   '})();';

  CefRegisterExtension('example/v8', Code, TMyHandler.Create as ICefv8Handler);
end;

initialization
  RegisterExtension;

TMyHandlerExecute方法将会在之后被调用。 TMyHandler的定义如下:

TMyHandler = class(TCefv8HandlerOwn)
protected
  function Execute(const name: ustring; const obj: ICefv8Value;
    const arguments: TCefv8ValueArray; var retval: ICefv8Value;
    var exception: ustring): Boolean; override;
end;

目前为了演示目的,实现简单:

function TMyHandler.Execute(const name: ustring; const obj: ICefv8Value; const arguments: TCefv8ValueArray; var retval: ICefv8Value; var exception: ustring): Boolean;
begin
  ShowMessage('Execute!');
end;

现在,为了测试从JavaScript调用Delphi的方法,只需执行以下操作:
ChromiumComponent.Browser.MainFrame.ExecuteJavaScript('cef.test.test_object().GetMessage();', 'about:blank', 0);

这应该显示一个消息框,上面写着"Execute!"。

我从一个名为cefclient的示例中提取了演示脚本,您可以在组件根目录的\demos\cefclient文件夹中找到它。扩展示例代码有点隐蔽,并与其他演示代码混杂在一起。但对我们特别感兴趣的是TExtension.Execute的实现(相当于我的TMyHandler.Execute)。在那里,您可以找到如何确定调用哪个函数以及如何传递参数的方法。(链接到代码。


谢谢 Heinrich。这正是我需要的。“关于获取JavaScript结果的扩展功能...”。我读过的一个线程表明,有些人担心这会涉及阻塞调用线程,直到返回结果。我理解这种担忧,但个人认为应该加上通常关于阻塞线程的警告。但这只是我的意见。 - Robert Oschler
很高兴能够帮忙。我完全同意扩展功能可能会很有用,特别是executeScriptAndReturnValue听起来很方便。 - Heinrich Ulbricht

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