如何在FreePascal/Lazarus中实现在子进程中使用CEF3渲染进程处理程序

3

我正在尝试将fpcef3渲染处理程序实现为子进程:

通过参考fpcef3的github仓库提供的示例,我已经成功创建了一个渲染处理程序子进程:

Program subprocess;

{$mode objfpc}{$H+}

Uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  cef3lib, cef3types, cef3api, Handler;

Var
  Args : TCefMainArgs;

begin
  CefLoadLibrary;
  CefRenderProcessHandler := TCustomRenderProcessHandler.Create;

  {$IFDEF WINDOWS}
  Args.instance := HINSTANCE();

  Halt(cef_execute_process(@Args, nil, nil));
  {$ELSE}
  Args.argc := argc;
  Args.argv := argv;

  Halt(cef_execute_process(@Args, nil, nil));
  {$ENDIF}
end. 

TCustomRenderProcessHandler与examples子目录中JavaScript示例提供的处理程序完全相同:

Unit Handler;

{$MODE objfpc}{$H+}

(*
 * Everything in here is called from a render process, so there is no access to GUI and all the
 * data of the main process.
 *)

Interface

Uses
  Classes, SysUtils,
  cef3types, cef3intf, cef3ref, cef3own, cef3lib;

Type
  { Custom handler for the render process }
  TCustomRenderProcessHandler = class(TCefRenderProcessHandlerOwn)
  protected
    // Test Window Bindings
    procedure OnContextCreated(const browser: ICefBrowser; const frame: ICefFrame; const context: ICefv8Context); override;
    // Test Extension
    procedure OnWebKitInitialized; override;
  end;

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

Implementation

Var
  mystr : String;

{ TMyHandler }

function TMyHandler.Execute(const name : ustring; const obj : ICefv8Value;
  const arguments : ICefv8ValueArray; var retval : ICefv8Value;
  var exception : ustring) : Boolean;
begin
  // return a value
  //retval := TCefv8ValueRef.NewString('TMyHandler');
  retval := TCefv8ValueRef.NewDate(Now);

  Result := True;
end;

{ TCustomRenderProcessHandler }

procedure TCustomRenderProcessHandler.OnContextCreated(const browser : ICefBrowser;
  const frame : ICefFrame; const context : ICefv8Context);
Var
  myWin : ICefv8Value;
  args  : ICefv8ValueArray;
begin
  myWin := context.GetGlobal;
  mystr := 'a test string';
  SetLength(args, 1);
  args[0] := TCefv8ValueRef.NewString(mystr);
  myWin.SetValueByKey('myval', args[0], []);
end;

procedure TCustomRenderProcessHandler.OnWebKitInitialized;
Var
  Code: ustring;
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;

end.

最后,在我的主进程的主表单中,我提供了子进程的路径:

procedure TForm1.FormCreate(Sender: TObject);
begin
  CefSingleProcess := False;

  //CefRenderProcessHandler := TCustomRenderProcessHandler.Create;
  CefBrowserSubprocessPath := 'C:\Users\aludin\fpCEF3-master\Examples\SubProcess\subprocess64.exe'
end;     

当我启动主应用程序时,Chromium浏览器正确显示,但是处理程序未被调用(但我可以看到子进程已启动)。 我在初始化处理程序时做错了什么?
谢谢您的帮助!

@MartynA,我检查了两个项目,它们都编译为64位Windows可执行文件。我还使用了特定的64位CEF3。我没有检查过这是否适用于32位,但构建64位可执行文件真的不应该是一个问题(因为主进程成功加载CEF3并显示浏览器视图)。 - BigONotation
@MartynA,谢谢你的建议!我打算尝试附加到子进程...但是我有一种感觉,渲染进程处理程序根本没有被初始化... - BigONotation
尝试连接到子进程,预期的是呈现处理程序的执行方法从未被调用。我怀疑主进程没有“看到”子进程的渲染处理程序……因此在子进程中存在某种初始化问题。 - BigONotation
顺便说一句,我假设你已经追踪到了cef代码的那个点,应该加载并执行你的子进程。你做到了吗? - MartynA
@MartynAN 是的,我已经在CEF3初始化期间调用的CefInitialize函数内设置了一个断点,该函数又调用cef_execute_process函数以启动子进程。两个函数都没有返回错误。 - BigONotation
@MartynA 最终找到了答案。请查看下面的回答。 - BigONotation
1个回答

2

好的,最后我终于搞懂了,花了这么长时间做完之后,我感觉自己想要加一些脏话。

在深入研究了fpcef3源代码并单步调试主进程之后,我意识到需要创建一个ICefApp应用程序实例,并将自定义的渲染处理程序分配给该应用程序。为了简化我的生活并避免实现ICefApp接口,我“劫持”了主进程中使用的类。更新后的子进程实现现在由以下代码给出:

Program subprocess;

{$mode objfpc}{$H+}

Uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  cef3lib, cef3types, cef3api, cef3own, cef3intf, Handler;

Var
  Args : TCefMainArgs;
  app : ICefApp;

begin
  CefLoadLibrary;
  CefRenderProcessHandler := TCustomRenderProcessHandler.Create;
  app := TInternalApp.Create;
  {$IFDEF WINDOWS}
  Args.instance := HINSTANCE();

  Halt(cef_execute_process(@Args, CefGetData(app), nil));
  {$ELSE}
  Args.argc := argc;
  Args.argv := argv;

  Halt(cef_execute_process(@Args, CefGetData(app), nil));
  {$ENDIF}
end.

现在,ICefApp实例将使用分配给CefRenderProcessHandler的渲染进程处理程序。最后请注意,cef_execute_process()已被修改为接受应用程序作为附加参数。

很高兴你解决了问题,加一。 - MartynA
嗨,我自己遇到了这个问题。你的解决方案似乎很好用。但是你能否解释一下我们应该如何反过来做呢?创建一个ICefApp,分配自定义处理程序等等...? - Vassilis
@VassilisGr 抱歉,我不明白你的问题?你所说的“反过来”是什么意思? - BigONotation
我的意思是“创建一个ICefApp应用程序实例,并将自定义渲染进程处理程序分配给此应用程序”,而不使用“劫持”方法。谢谢。 - Vassilis
@VassilisGr 好的,我现在理解得更清楚了。在我最初发布帖子后不久,fpcef3的作者通过电子邮件向我指出了我的方法是正确的,我没有劫持任何东西。换句话说,你可以直接在自己的代码中使用ICefApp。如果您愿意,我可以再次检查。 - BigONotation
好的!非常感谢。我只是担心会出现故障。 - Vassilis

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