gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-03 06:56:30 25 4
gpt4 key购买 nike

我是德尔福的初学者。目前使用 Delphi Berlin 版本。
我正在尝试从 JavaScript 调用 Delphi 函数/方法。例如,我想在单击带有附加数据属性的 html 按钮时打开一个新的 Delphi 表单。
代码


<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>

单击按钮时,将打开一个新的 Delphi [第二个窗体],它将显示 数据 Prop TLabel 上的按钮。
[更新 - 2020 年 12 月 10 日]
我尝试在 JSExtension 演示的帮助下创建应用程序。我尝试添加 javascript 点击事件,但 html 上的点击事件未触发且第二个表单未加载。
这是一些代码
HTML [jsExtensionClickEvent.html]
<!DOCTYPE html>
<html>
<body>

<form method="POST">
<input type="button" name="btnEx" value="Button" id="edit" data-prop="1"></button>
<input type="button" name="anotherBtn" value="Another Button" id="edit2" data-prop="24"></button>
</form>
</body>
</html>
德尔福
扩展处理程序类 [uExtensionHandler.pas]
    unit uExtensionHandler;

{$I cef.inc}

interface

uses
{$IFDEF DELPHI16_UP}
Winapi.Windows,
{$ELSE}
Windows,
{$ENDIF}
uCEFRenderProcessHandler, uCEFBrowserProcessHandler, uCEFInterfaces,
uCEFProcessMessage,
uCEFv8Context, uCEFTypes, uCEFv8Handler;

const
MOUSECLICK_MESSAGE_NAME = 'mouseclick';

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

implementation

{ TExtensionHelper }

function TExtensionHelper.Execute(const name: ustring;
const object_: ICefv8Value; const arguments: TCefv8ValueArray;
var retval: ICefv8Value; var exception: ustring): Boolean;
var
TempMessage: ICefProcessMessage;
TempFrame: ICefFrame;
begin
Result := False;

try
if (name = 'mouseclick') then
begin
if (length(arguments) > 1) and arguments[0].IsString and arguments[1].IsString
then
begin
TempMessage := TCefProcessMessageRef.New(arguments[1].GetStringValue);
TempMessage.ArgumentList.SetString(0, arguments[0].GetStringValue);

TempFrame := TCefv8ContextRef.Current.Browser.MainFrame;

if (TempFrame <> nil) and TempFrame.IsValid then
TempFrame.SendProcessMessage(PID_BROWSER, TempMessage);
end;

Result := True;
end;

finally
TempMessage := nil;
end;

end;

end.

主窗体 [uMainForm.pas]
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.
第二种形式 [uSecondForm.pas]
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!
单击“调试事件日志”下方的按钮时。 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()”。然后使用 TChromium.OnConsoleMessage 事件并检查“aMessage”参数。如果 aMessage 具有“OpenMyNewFormInDelphi”,则向主窗体发送一条 Windows 消息,以在主应用程序线程中显示您的新窗体。这个解决方案是最简单的,它不是很优雅,但它可以完成工作。见 DOM访客演示了解更多详情。
  • 您还可以使用 CEF4Delphi 注册“JavaScript 扩展”以从 JavaScript 执行 Delphi 代码。这是迄今为止最复杂的解决方案,因为它涉及从 TCefv8HandlerOwn 继承的自定义类的创建和注册。该类将接收来自您的 JS 代码的调用,如果您的应用程序需要响应该 JS 调用,您可以向主浏览器进程发送 IPC 消息。见 JS扩展 JSRTTIExtension 演示以获取更多信息。

  • JavaScript 扩展的完整解释有点长,但你可以在这里阅读:
    https://github.com/salvadordf/CEF4Delphi/blob/d44db3bf2a3ead0654ca90178161b09bfbe33602/demos/Delphi_VCL/JavaScript/JSExtension/uJSExtension.pas#L122
    请记住,所有 TChromium 和 GlobalCEFApp 事件都在与主应用程序线程不同的 CEF 线程中执行。 VCL 不是线程安全的,如果您在这些事件中创建、销毁或修改 Windows 控件,您可能会遇到问题。 CEF4Delphi 演示过于简单,您应该始终将 VCL 代码移到这些事件之外。出于这个原因,第一个解决方案将 Windows 消息发送到主窗体。

    关于javascript - 如何使用 Cef4Delphi 从 JavaScript 调用 Delphi 函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64303829/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com