如何使用Cef4Delphi从JavaScript调用Delphi函数

如何使用Cef4Delphi从JavaScript调用Delphi函数,javascript,delphi,cef4delphi,Javascript,Delphi,Cef4delphi,我是Delphi的初学者。目前正在使用Delphi柏林版 我试图从JavaScript调用Delphi函数/方法。例如,我想打开一个新的Delphi表单,点击带有附加数据属性的html按钮 HTML代码 <input type="button" name="btn" value="Button" id="edit" data-prop="24"></button> <

我是Delphi的初学者。目前正在使用Delphi柏林版

我试图从JavaScript调用Delphi函数/方法。例如,我想打开一个新的Delphi表单,点击带有附加数据属性的html按钮

HTML代码


<input type="button" name="btn" value="Button" id="edit" data-prop="24"></button>
<input type="button" name="btnAnother" value="Button2" id="edit2" data-prop="1"></button>

日志文件的内容

[1012/202103.335:ERROR:CEF4Delphi(1)] PID: 6708, TID: 2872, PT: Renderer - JavaScript extension registered successfully!
[1012/203832.621:ERROR:CEF4Delphi(1)] PID: 6660, TID: 6748, PT: Renderer - JavaScript extension registered successfully!
[1012/203832.688:ERROR:CEF4Delphi(1)] PID: 5436, TID: 6016, PT: Renderer - JavaScript extension registered successfully!
单击调试事件日志下方的按钮时。Cef4DelphiJsExtension.exe是应用程序名

Thread Start: Thread ID: 1732. Process Cef4DelphiJsExtension.exe (7156)
Thread Exit: Thread ID: 1732. Process Cef4DelphiJsExtension.exe (7156)
Thread Start: Thread ID: 1180. Process Cef4DelphiJsExtension.exe (7156)
Thread Exit: Thread ID: 2076. Process Cef4DelphiJsExtension.exe (7156)
Thread Exit: Thread ID: 6592. Process Cef4DelphiJsExtension.exe (7156)
Thread Start: Thread ID: 7200. Process Cef4DelphiJsExtension.exe (7156)
Thread Start: Thread ID: 7220. Process Cef4DelphiJsExtension.exe (7156)
Thread Start: Thread ID: 7276. Process Cef4DelphiJsExtension.exe (7156)
Thread Exit: Thread ID: 7276. Process Cef4DelphiJsExtension.exe (7156)
Thread Start: Thread ID: 7376. Process Cef4DelphiJsExtension.exe (7156)
Thread Exit: Thread ID: 7376. Process Cef4DelphiJsExtension.exe (7156)
Thread Exit: Thread ID: 7220. Process Cef4DelphiJsExtension.exe (7156)

谢谢。

您可以通过两种方式完成:

  • 在该按钮的“onclick”事件中添加JavaScript函数。该函数只需调用带有“OpenMyNewFormInDelphi”的“console.log()”或任何您想要的文本参数。然后使用tcromium.onConsolleMessage事件并检查“aMessage”参数。如果消息中有“OpenMyNewFormInDelphi”,则向主窗体发送Windows消息,以在主应用程序线程中显示新窗体。这个解决方案是最简单的,它不是很优雅,但它完成了工作。有关更多详细信息,请参阅DOMVisitor演示
  • 您还可以向CEF4Delphi注册一个“JavaScript扩展”,以从JavaScript执行Delphi代码。这是迄今为止最复杂的解决方案,因为它涉及从TCefv8HandlerOwn继承的自定义类的创建和注册。该类将接收来自JS代码的调用,如果应用程序需要响应JS调用,则可以向主浏览器进程发送IPC消息。有关更多信息,请参见JSExtensionJSRTTIExtension演示
  • JavaScript扩展的完整解释有点长,但您可以在这里阅读:


    请记住,所有TChromium和GlobalCEFApp事件都是在与主应用程序线程不同的CEF线程中执行的。VCL不是线程安全的,如果在这些事件中创建、销毁或修改Windows控件,您可能会遇到问题。CEF4Delphi演示过于简单,您应该始终将VCL代码移到这些事件之外。第一种解决方案为此向主窗体发送Windows消息。

    移动Chromium1.LoadURL('file:///jsExtensionClickEvent.html“)代码行到TChromium.OnLoadEnd之外的另一个过程。TChromium.LoadURL是异步的,它将在完成加载文档之前返回。我尝试了你的代码将tcromium.LoadURL行移动到BrowserCreatedMsg过程,它工作得非常好。如果答案对你有用,请接受。谢谢谢谢你@SalvadorDíazFau先生。
    unit uMainForm;
    
    interface
    
    uses
    {$IFDEF DELPHI16_UP}
      Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
      System.Classes, Vcl.Graphics,
      Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls,
      Vcl.ComCtrls, System.IOUtils,
    {$ELSE}
      Windows, Messages, SysUtils, Variants, Classes, Graphics,
      Controls, Forms, Dialogs, StdCtrls, ExtCtrls, ComCtrls, IOUtils,
    {$ENDIF}
      uCEFChromium, uCEFWindowParent, uCEFInterfaces, uCEFApplication, uCEFTypes,
      uCEFConstants,
      uCEFWinControl, uCEFSentinel, uCEFChromiumCore;
    
    const
      MINIBROWSER_SHOWSECONDFORM = WM_APP + $100;
    
    type
      TForm1 = class(TForm)
        CEFWindowParent1: TCEFWindowParent;
        Chromium1: TChromium;
        Timer1: TTimer;
        procedure FormShow(Sender: TObject);
        procedure FormCreate(Sender: TObject);
        procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
        procedure Timer1Timer(Sender: TObject);
        procedure Chromium1ProcessMessageReceived(Sender: TObject;
          const browser: ICefBrowser; const frame: ICefFrame;
          sourceProcess: TCefProcessId; const message: ICefProcessMessage;
          out Result: Boolean);
        procedure Chromium1AfterCreated(Sender: TObject;
          const browser: ICefBrowser);
        procedure Chromium1BeforePopup(Sender: TObject; const browser: ICefBrowser;
          const frame: ICefFrame; const targetUrl, targetFrameName: ustring;
          targetDisposition: TCefWindowOpenDisposition; userGesture: Boolean;
          const popupFeatures: TCefPopupFeatures; var windowInfo: TCefWindowInfo;
          var client: ICefClient; var settings: TCefBrowserSettings;
          var extra_info: ICefDictionaryValue;
          var noJavascriptAccess, Result: Boolean);
        procedure Chromium1Close(Sender: TObject; const browser: ICefBrowser;
          var aAction: TCefCloseBrowserAction);
        procedure Chromium1BeforeClose(Sender: TObject; const browser: ICefBrowser);
        procedure Chromium1LoadEnd(Sender: TObject; const browser: ICefBrowser;
          const frame: ICefFrame; httpStatusCode: Integer);
      private
        { Private declarations }
      public
        { Public declarations }
      protected
        Fid: string;
        // Variables to control when can we destroy the form safely
        FCanClose: Boolean; // Set to True in TChromium.OnBeforeClose
        FClosing: Boolean; // Set to True in the CloseQuery event.
    
        procedure BrowserCreatedMsg(var aMessage: TMessage);
          message CEF_AFTERCREATED;
        procedure BrowserDestroyMsg(var aMessage: TMessage); message CEF_DESTROY;
        procedure ShowSecondForm(var aMessage: TMessage);
          message MINIBROWSER_SHOWSECONDFORM;
        procedure WMMove(var aMessage: TWMMove); message WM_MOVE;
        procedure WMMoving(var aMessage: TMessage); message WM_MOVING;
        procedure WMEnterMenuLoop(var aMessage: TMessage); message WM_ENTERMENULOOP;
        procedure WMExitMenuLoop(var aMessage: TMessage); message WM_EXITMENULOOP;
      end;
    
    var
      Form1: TForm1;
    
    procedure CreateGlobalCEFApp;
    
    implementation
    
    uses
      uSecondForm, uCEFMiscFunctions, uCEFDictionaryValue, uExtensionHandler;
    
    procedure GlobalCEFApp_OnWebKitInitialized;
    var
      TempExtensionCode: string;
      TempHandler: ICefv8Handler;
    begin
      try
        TempExtensionCode := 'var myextension;' + 'if (!myextension)' +
          '  myextension = {};' + '(function() {' +
          '  myextension.mouseclick = function(b,c) {' +
          '    native function mouseclick();' + '    mouseclick(b,c);' + '  };'
          + '})();';
    
        TempHandler := TExtensionHelper.Create;
    
        if CefRegisterExtension('myextension', TempExtensionCode, TempHandler) then
    {$IFDEF DEBUG}CefDebugLog('JavaScript extension registered successfully!'){$ENDIF}
        else
    {$IFDEF DEBUG}CefDebugLog('There was an error registering the JavaScript extension!'){$ENDIF};
      finally
        TempHandler := nil;
      end;
    end;
    
    procedure CreateGlobalCEFApp;
    begin
      GlobalCEFApp := TCefApplication.Create;
      GlobalCEFApp.OnWebKitInitialized := GlobalCEFApp_OnWebKitInitialized;
    {$IFDEF DEBUG}
      GlobalCEFApp.LogFile := 'debug.log';
      GlobalCEFApp.LogSeverity := LOGSEVERITY_INFO;
    {$ENDIF}
    end;
    
    {$R *.dfm}
    { TForm1 }
    
    procedure TForm1.BrowserCreatedMsg(var aMessage: TMessage);
    begin
      CEFWindowParent1.UpdateSize;
    end;
    
    procedure TForm1.BrowserDestroyMsg(var aMessage: TMessage);
    begin
      CEFWindowParent1.Free;
    end;
    
    procedure TForm1.Chromium1AfterCreated(Sender: TObject;
      const browser: ICefBrowser);
    begin
      PostMessage(Handle, CEF_AFTERCREATED, 0, 0);
    end;
    
    procedure TForm1.Chromium1BeforeClose(Sender: TObject;
      const browser: ICefBrowser);
    begin
      FCanClose := True;
      PostMessage(Handle, WM_CLOSE, 0, 0);
    end;
    
    procedure TForm1.Chromium1BeforePopup(Sender: TObject;
      const browser: ICefBrowser; const frame: ICefFrame;
      const targetUrl, targetFrameName: ustring;
      targetDisposition: TCefWindowOpenDisposition; userGesture: Boolean;
      const popupFeatures: TCefPopupFeatures; var windowInfo: TCefWindowInfo;
      var client: ICefClient; var settings: TCefBrowserSettings;
      var extra_info: ICefDictionaryValue; var noJavascriptAccess, Result: Boolean);
    begin
      Result := (targetDisposition in [WOD_NEW_FOREGROUND_TAB,
        WOD_NEW_BACKGROUND_TAB, WOD_NEW_POPUP, WOD_NEW_WINDOW]);
    end;
    
    procedure TForm1.Chromium1Close(Sender: TObject; const browser: ICefBrowser;
      var aAction: TCefCloseBrowserAction);
    begin
      PostMessage(Handle, CEF_DESTROY, 0, 0);
      aAction := cbaDelay;
    end;
    
    procedure TForm1.Chromium1LoadEnd(Sender: TObject; const browser: ICefBrowser;
      const frame: ICefFrame; httpStatusCode: Integer);
    var
      TempJSCode: string;
    begin
      Chromium1.LoadURL('file:///jsExtensionClickEvent.html');
      TempJSCode := 'document.body.addEventListener("click", function (evt) { ' +
        ' function getpath(n) {' +
        ' var result = document.getElementById(n.id).getAttribute("data-prop"); ' +
        ' return result; ' + ' } '
      +' myextension.mouseclick(getpath(evt.target), ' +
        quotedstr(MOUSECLICK_MESSAGE_NAME) + ');});';
      frame.ExecuteJavaScript(TempJSCode, 'about:blank', 0);
    end;
    
    procedure TForm1.Chromium1ProcessMessageReceived(Sender: TObject;
      const browser: ICefBrowser; const frame: ICefFrame;
      sourceProcess: TCefProcessId; const message: ICefProcessMessage;
      out Result: Boolean);
    begin
      Result := False;
    
      if (message = nil) or (message.ArgumentList = nil) then
        exit;
    
      // This function receives the messages with the JavaScript results
    
      if (message.Name = MOUSECLICK_MESSAGE_NAME) then
      begin
        Fid := message.ArgumentList.GetString(0);
        PostMessage(Handle, MINIBROWSER_SHOWSECONDFORM, 0, 0);
        // this doesn't create/destroy components
        Result := True;
      end;
    end;
    
    procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    begin
      CanClose := FCanClose;
    
      if not(FClosing) then
      begin
        FClosing := True;
        Visible := False;
        Chromium1.CloseBrowser(True);
      end;
    end;
    
    procedure TForm1.FormCreate(Sender: TObject);
    begin
      FCanClose := False;
      FClosing := False;
    end;
    
    procedure TForm1.FormShow(Sender: TObject);
    begin
      // GlobalCEFApp.GlobalContextInitialized has to be TRUE before creating any browser
      // If it's not initialized yet, we use a simple timer to create the browser later.
      if not(Chromium1.CreateBrowser(CEFWindowParent1, '')) then
        Timer1.Enabled := True;
    end;
    
    procedure TForm1.ShowSecondForm(var aMessage: TMessage);
    begin
      Form2.Label1.Caption := Fid;
      Form2.ShowModal;
    end;
    
    procedure TForm1.Timer1Timer(Sender: TObject);
    begin
      Timer1.Enabled := False;
      if not(Chromium1.CreateBrowser(CEFWindowParent1, '')) and
        not(Chromium1.Initialized) then
        Timer1.Enabled := True;
    end;
    
    procedure TForm1.WMEnterMenuLoop(var aMessage: TMessage);
    begin
      inherited;
    
      if (aMessage.wParam = 0) and (GlobalCEFApp <> nil) then
        GlobalCEFApp.OsmodalLoop := True;
    
    end;
    
    procedure TForm1.WMExitMenuLoop(var aMessage: TMessage);
    begin
      inherited;
    
      if (aMessage.wParam = 0) and (GlobalCEFApp <> nil) then
        GlobalCEFApp.OsmodalLoop := False;
    
    end;
    
    procedure TForm1.WMMove(var aMessage: TWMMove);
    begin
      inherited;
    
      if (Chromium1 <> nil) then
        Chromium1.NotifyMoveOrResizeStarted;
    end;
    
    procedure TForm1.WMMoving(var aMessage: TMessage);
    begin
      inherited;
    
      if (Chromium1 <> nil) then
        Chromium1.NotifyMoveOrResizeStarted;
    end;
    
    end.
    
    unit uSecondForm;
    
    interface
    
    uses
      Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
      System.Classes, Vcl.Graphics,
      Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
    
    type
      TForm2 = class(TForm)
        Label1: TLabel;
      private
        { Private declarations }
      public
        { Public declarations }
      end;
    
    var
      Form2: TForm2;
    
    implementation
    
    {$R *.dfm}
    
    end.
    
    [1012/202103.335:ERROR:CEF4Delphi(1)] PID: 6708, TID: 2872, PT: Renderer - JavaScript extension registered successfully!
    [1012/203832.621:ERROR:CEF4Delphi(1)] PID: 6660, TID: 6748, PT: Renderer - JavaScript extension registered successfully!
    [1012/203832.688:ERROR:CEF4Delphi(1)] PID: 5436, TID: 6016, PT: Renderer - JavaScript extension registered successfully!
    
    Thread Start: Thread ID: 1732. Process Cef4DelphiJsExtension.exe (7156)
    Thread Exit: Thread ID: 1732. Process Cef4DelphiJsExtension.exe (7156)
    Thread Start: Thread ID: 1180. Process Cef4DelphiJsExtension.exe (7156)
    Thread Exit: Thread ID: 2076. Process Cef4DelphiJsExtension.exe (7156)
    Thread Exit: Thread ID: 6592. Process Cef4DelphiJsExtension.exe (7156)
    Thread Start: Thread ID: 7200. Process Cef4DelphiJsExtension.exe (7156)
    Thread Start: Thread ID: 7220. Process Cef4DelphiJsExtension.exe (7156)
    Thread Start: Thread ID: 7276. Process Cef4DelphiJsExtension.exe (7156)
    Thread Exit: Thread ID: 7276. Process Cef4DelphiJsExtension.exe (7156)
    Thread Start: Thread ID: 7376. Process Cef4DelphiJsExtension.exe (7156)
    Thread Exit: Thread ID: 7376. Process Cef4DelphiJsExtension.exe (7156)
    Thread Exit: Thread ID: 7220. Process Cef4DelphiJsExtension.exe (7156)