目录
RAD Studio 10.4.1的TEdgeBrowser与javascript交互-基于Chromium的Edge浏览器控件用法之二
1.1、界面部分详见上一篇《RAD Studio 10.4.1新的基于Chromium的Microsoft Edge浏览器的TEdgeBrowser控件用法》
RAD Studio 10.4.1的TEdgeBrowser与javascript交互-基于Chromium的Edge浏览器控件用法之二
现在delphi在服务器有了TEdgeBrowser可以很方便的与javascript相互沟通了,这都得益于基于Chromium的Microsoft Edge浏览器不仅如此,delphi的桌面c/s多层分布式应用程序的客户端,TEdgeBrowser也可以让Edge浏览器很通畅的访问Bootstrap响应式H5页面,再也不会走形变样啦。
一、先看看运行代码的效果吧
1.1、很通畅的浏览Bootstrap响应式H5页面
1.2、可变比例捕获页面图片只需一句话搞定
1.3、执行javascript也是一句话的事情
二、delphi代码如何实现的
1.1、编写html页面代码,在其中加入页面元素的点击事件
比如:<p>标签这个页面元素,其id="mytestclick",内联显示为“测试你点我”,为其编写οnclick="handleclick()的点击事件,即如下图所示部分,其余<webview>及其对应我写的的外部js文件引用“webview2.js”可以不管它:
代码如下:
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>我是mytest.html的网页标题</title>
</head>
<body>
<webview style="width:100%;height:100%;" id="webview" >
<p onclick="handleclick()" id="mytestclick">测试你点我</p>
<script type="text/javascript">
function handleclick() {
try{
window.chrome.webview.postMessage(
{ data: 'Message from Edge Chromium', url: window.document.url }
);
}catch(error){
console.log("postMessage error: " + error);
}
console.log("测试这个点击事件执行了");
}
</script>
</webview>
<script src="js_lib/webview2.js" type="text/javascript"></script>
</body>
</html>
1.2、编写delphi事件代码
写按钮【调用js】的点击事件:
- 需要注意的是:
1.2.1、TEdgeBrowser.ExecuteScript执行js字符串与Intraweb等的调用方式稍有不一样,不能加入<script></script>标签,则否是不会执行的,你只需把它其中的js代码加入即可,就跟引入外部.js文件一样的方式;
1.2.2、js函数若有返回值,若需将其和html界面元素关联,请关联赋值,以内联的方式,比如:document.getElementById("mytestclick").innerHTML = "我就把它改了!谁让你点(打)我呢!";
1.2.3、养成使用线程的习惯。b/s的客户端:"浏览器"和我们的c/s客户端App一样,否则"浏览器"的UI也会出现“好像有点卡嘛”的不良UE体验。
procedure TForm_EdgeBrowser01.BitBtn_HandlePostmessageClick(Sender: TObject);
var LJs:string;
begin
LJs:=
//'<script type="text/javascript">'//:千万不能加,加了就不执行了:跟外部js文件类似,可从外部导入
//'return 12;'+//:一旦返回:就不弹出了:
'window.alert("既然你让我弹出,那你一点’‘确定’‘我就:");'
(*'webview.contentWindow.postMessage.postMessage('
+'{ data: "既然你让我弹出,那你一点’‘确定’‘我就:", , "*" }'
+');'//*)
+'document.getElementById("mytestclick").innerHTML = "我就把它改了!谁让你点(打)我呢!";'
+'console.log("delphi按钮:【调用js】执行了其中的js代码事件");'
//+'return 12;'//:一旦返回:就不弹出了
//+'</script>'//:千万不能加,加了就不执行了:跟外部js文件类似,可从外部导入
;
TThread.CreateAnonymousThread(
procedure begin
TThread.Synchronize( TThread.Current,
procedure begin
Memo1.Lines.Add('TEdgeBrowser控件开始让页面元素执行javascript事件代码......');
EdgeBrowser1.ExecuteScript(LJs);
end);
end).Start;
end;
写回调事件:
procedure TForm_EdgeBrowser01.EdgeBrowser1ExecuteScript(
Sender: TCustomEdgeBrowser; AResult: HRESULT;
const AResultObjectAsJson: string);
begin
//:回调:TCustomEdgeBrowser.ExecuteScript(const JavaScript: string);
//:AResult:结果错误码; AResultObjectAsJson:结果对象的Json
TThread.CreateAnonymousThread(
procedure begin
while AResult<>S_OK do sleep(0);
TThread.Synchronize( nil,
procedure begin
Memo1.Lines.Add('EdgeBrowser1执行脚本是否成功(0代表成功):'+IntToStr(AResult));
Memo1.Lines.Add('EdgeBrowser1执行脚本后返回的Json数值(怪了不回调返回null,bug?!):'+AResultObjectAsJson);
Memo1.Lines.Add('其实不是的:是因为你的【调用js】中的js没有function返回Json数据!');
end);
end).Start;
end;
- 回调的本质:
1.2.4、 TCustomEdgeBrowser.ExecuteScript // uses Vcl.Edge;
// uses Vcl.Edge;
procedure TCustomEdgeBrowser.ExecuteScript(const JavaScript: string);
begin
if FWebView <> nil then
FWebView.ExecuteScript(PChar(JavaScript),
Callback<HResult, PChar>.CreateAs<ICoreWebView2ExecuteScriptCompletedHandler>(
function(ErrorCode: HResult; ResultObjectAsJson: PWideChar): HResult stdcall
begin
Result := S_OK;
if Assigned(FOnExecuteScript) then
FOnExecuteScript(Self, ErrorCode, string(ResultObjectAsJson));
end));
end;
1.2.5、如上,ExecuteScript 方法回调,是在等待ResultObjectAsJson返回结果赋给OnExecuteScript中的AResultObjectAsJson,其中错误码ErrorCode赋给AResult: HRESULT,因此,它本质是线程的同步事件,需要时间来等待。
1.2.6、所以,还是需要把它放入一个新的线程中来执行线程同步Synchronize,需要注意的是,有同学习惯性的在其中使用TThread.Current当前线程,最好使用nil,因为在多线程环境下,一个线程出了问题,其它全部挂起,把它交给delphi的线程库来统一调度其上下文,与操作系统内核线程对接。
三、粘上所有delphi源码给你
1.1、界面部分详见上一篇《RAD Studio 10.4.1新的基于Chromium的Microsoft Edge浏览器的TEdgeBrowser控件用法》
1.2、代码如下:
unit uEdgeBrowser01;
interface
uses
Winapi.Windows, Winapi.Messages,
System.SysUtils, System.Variants, System.Classes,
Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
Winapi.ActiveX,
WebView2, //:Edge的Web视图DLL的静态类型库WebView2.tlb及其关联库的调用:WebView2
Vcl.Edge, //:Edge的VCL控件的定义:是对 WebView2.pas 的进一步封装
Vcl.StdCtrls, Vcl.Buttons, Vcl.ExtCtrls
//, Winapi.ShLwApi :Edge的底层字符串及路由定义
;
type
TForm_EdgeBrowser01 = class(TForm)
EdgeBrowser1: TEdgeBrowser;
Panel1: TPanel;
BitBtn_Navigate: TBitBtn;
Panel2: TPanel;
Memo1: TMemo;
BitBtn_CapturePreview: TBitBtn;
BitBtn_HandlePostmessage: TBitBtn;
procedure BitBtn_NavigateClick(Sender: TObject);
procedure EdgeBrowser1NavigationStarting(Sender: TCustomEdgeBrowser;
Args: TNavigationStartingEventArgs);
procedure EdgeBrowser1ContentLoading(Sender: TCustomEdgeBrowser;
IsErrorPage: Boolean; NavigationID: TUInt64);
procedure EdgeBrowser1CreateWebViewCompleted(Sender: TCustomEdgeBrowser;
AResult: HRESULT);
procedure EdgeBrowser1CapturePreviewCompleted(Sender: TCustomEdgeBrowser;
AResult: HRESULT);
procedure BitBtn_CapturePreviewClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure EdgeBrowser1ExecuteScript(Sender: TCustomEdgeBrowser;
AResult: HRESULT; const AResultObjectAsJson: string);
procedure EdgeBrowser1WebMessageReceived(Sender: TCustomEdgeBrowser;
Args: TWebMessageReceivedEventArgs);
procedure EdgeBrowser1WebResourceRequested(Sender: TCustomEdgeBrowser;
Args: TWebResourceRequestedEventArgs);
procedure BitBtn_HandlePostmessageClick(Sender: TObject);
procedure EdgeBrowser1SourceChanged(Sender: TCustomEdgeBrowser;
IsNewDocument: Boolean);
private
{ Private declarations }
public
{ Public declarations }
Fif_SSL:Boolean;
end;
var
Form_EdgeBrowser01: TForm_EdgeBrowser01;
implementation
{$R *.dfm}
procedure TForm_EdgeBrowser01.FormCreate(Sender: TObject);
var LICoreWebView2:ICoreWebView2;
begin
Memo1.Lines.Add('默认开始初始化(UI设计时的)EdgeBrowser1或初始化动态产生的TEdgeBrowser');
GetInterface( StringToGUID('{189B8AAF-0426-4748-B9AD-243F537EB46B}'),LICoreWebView2);
if LICoreWebView2 = EdgeBrowser1.DefaultInterface then
Memo1.Lines.Add('LICoreWebView2接口TGUID:'+'{189B8AAF-0426-4748-B9AD-243F537EB46B}' );
//GUIDToString StringToGUID
end;
procedure TForm_EdgeBrowser01.FormShow(Sender: TObject);
begin
self.Top:=0; self.Height:=960;
Memo1.Lines.Add('EdgeBrowser浏览器内部执行的进程ID必须放在其事件中取引用>0的,否则访问的是其基础进程ID(0):'
+EdgeBrowser1.BrowserProcessID.ToString );
end;
procedure TForm_EdgeBrowser01.BitBtn_NavigateClick(Sender: TObject);
var LHResult_PostWebMessage:HResult;
begin
if EdgeBrowser1.WebViewCreated then//:必须的,否则接口调用报错:
begin
Memo1.Lines.Add('必须EdgeBrowser1.WebViewCreated,否则接口调用报错!'+sLineBreak
+'有这样一些接口:'+sLineBreak
+'DefaultInterface: ICoreWebView2'+sLineBreak
+'ControllerInterface: ICoreWebView2Controller'+sLineBreak
+'EnvironmentInterface: ICoreWebView2Environment'+sLineBreak
+'SettingsInterface: ICoreWebView2Settings'+sLineBreak
);
Memo1.Lines.Add('TEdgeBrowser:支持异步产生WebView控件:'+sLineBreak
+'有这样一些方法:'+sLineBreak
+'TEdgeBrowser.CreateWebView'+sLineBreak
+'TEdgeBrowser.CloseWebView'+sLineBreak
+'TEdgeBrowser.CloseBrowserProcess'+sLineBreak
);
Memo1.Lines.Add('EdgeBrowser浏览器内部执行的进程ID必须放在其事件中取引用:'
+EdgeBrowser1.BrowserProcessID.ToString +sLineBreak );
Memo1.Lines.Add('TEdgeBrowser需要初始化一些参数,以使一些方法或接口生效:' +sLineBreak
+'EdgeBrowser1.BuiltInErrorPageEnabled :=true;' +sLineBreak
+'EdgeBrowser1.DefaultContextMenusEnabled :=true;' +sLineBreak
+'EdgeBrowser1.ZoomControlEnabled :=true;' +sLineBreak
+'EdgeBrowser1.StatusBarEnabled :=true;' +sLineBreak
+'EdgeBrowser1.DevToolsEnabled :=true;' +sLineBreak
+'EdgeBrowser1.DefaultScriptDialogsEnabled :=true;' +sLineBreak
+'EdgeBrowser1.ScriptEnabled :=true;' +sLineBreak
+'EdgeBrowser1.WebMessageEnabled :=true;' +sLineBreak
);
EdgeBrowser1.BuiltInErrorPageEnabled :=true; //:替代了浏览器内部错误提示
EdgeBrowser1.ZoomControlEnabled :=true; //:控制用户是否可以影响WebView的缩放
EdgeBrowser1.DevToolsEnabled :=true; //:控制用户是否能够使用上下文菜单或键盘快捷键打开DevTools窗口
EdgeBrowser1.StatusBarEnabled :=false;//:控制是否显示状态栏
EdgeBrowser1.DefaultContextMenusEnabled :=true;//:控制是否在WebView中向用户显示默认上下文菜单
//:若false:则网页代码中所有涉及Menu的事件将被屏蔽
{
EdgeBrowser1.DefaultScriptDialogsEnabled :=true;//:控制在显示JavaScript对话框时是否启动OnScriptDialogOpening
EdgeBrowser1.ScriptEnabled :=true;//:控制是否在以后的所有导航视图中启用JavaScript的执行
//:特别注意:这两个属性:只要引用无论false或true,
//,网页下载的原生js将不在生效:由代码控制
//:ScriptEnabled :并不影响执行方法ExecuteScript(const JavaScript: string);
//}
EdgeBrowser1.WebMessageEnabled :=true;//:必须的,否则接口PostWebMessageAsString调用报错:
//(*
LHResult_PostWebMessage
:=EdgeBrowser1.DefaultInterface.PostWebMessageAsString(
PWideChar('{ICoreWebView2:true}') ) ;//:0接口函数执行成功 -1失败
if LHResult_PostWebMessage> -1 then
Memo1.Lines.Add('开始访问TEdgeBrowser的接口对象'
+IntToStr(LHResult_PostWebMessage) +sLineBreak
+'调用JavaScript也是类似的方式ExecuteScript()' +sLineBreak
);
//:开始访问TEdgeBrowser的接口对象
//*)
TThread.CreateAnonymousThread(
procedure
begin
TThread.Synchronize( nil,
procedure //var AHTMLContent: string;
begin //EdgeBrowser1.NavigateToString(AHTMLContent);
if EdgeBrowser1.Navigate(//:如果返回了错误码:=true,否则:=false
'https://www.cpuofbs.com/index.html' )=false then
//if EdgeBrowser1.Navigate(ExtractFilePath(ParamStr(0))+ 'mytest.html')=false then
Memo1.Lines.Add('开始请求浏览网页!' +sLineBreak );
end);
end
).Start;//}
end;
//EdgeBrowser1.Navigate('https://www.cpuofbs.com/index.html' );
//:养成使用线程的习惯:b/s的客户端:"浏览器"和我们的c/s客户端App一样
//:否则"浏览器"的UI也会出现“好像有点卡嘛”的不良UE体验
end;
procedure TForm_EdgeBrowser01.EdgeBrowser1ContentLoading(
Sender: TCustomEdgeBrowser; IsErrorPage: Boolean; NavigationID: TUInt64);
begin
Memo1.Lines.Add('TEdgeBrowser组件NavigationID:'+IntToStr(NavigationID)
+',SizeRatio'+EdgeBrowser1.SizeRatio.ToString +sLineBreak
+':正在调取页面内容'+EdgeBrowser1.LocationURL +sLineBreak);
end;
procedure TForm_EdgeBrowser01.EdgeBrowser1CreateWebViewCompleted(
Sender: TCustomEdgeBrowser; AResult: HRESULT);
begin
//
end;
procedure TForm_EdgeBrowser01.BitBtn_CapturePreviewClick(Sender: TObject);
begin
if EdgeBrowser1.WebViewCreated then
begin
EdgeBrowser1.ZoomFactor:=100/100;
EdgeBrowser1.CapturePreview(
System.SysUtils.ExtractFilePath(
System.ParamStr(0))+'CapturePreview01.png',PNG);
end;
end;
procedure TForm_EdgeBrowser01.EdgeBrowser1CapturePreviewCompleted(
Sender: TCustomEdgeBrowser; AResult: HRESULT);
begin
Memo1.Lines.Add('TEdgeBrowser组件CapturePreview输出呈现部分的图片:CapturePreview01.png ,AResult='+IntToStr(AResult)+sLineBreak );
end;
procedure TForm_EdgeBrowser01.EdgeBrowser1NavigationStarting(
Sender: TCustomEdgeBrowser; Args: TNavigationStartingEventArgs);
var Lname,Lvalue : PWideChar;
LHeaders: ICoreWebView2HttpRequestHeaders;
Literator: ICoreWebView2HttpHeadersCollectionIterator;
LhasNext, LhasCurrent: Integer;
Luri: PWideChar;
Lnavigation_id: Largeuint;
begin
Memo1.Lines.Add('TEdgeBrowser组件在执行Navigate时,触发NavigationStarting准备开始浏览网页!'+sLineBreak );
Args.ArgsInterface.Get_uri(Luri);//:获取当前浏览的网页的uri
Fif_SSL:= string(Luri).Contains('https');
if Fif_SSL=true then
begin
Memo1.Lines.Add('您当前浏览的网页是https安全链接!'+sLineBreak );
// Args.ArgsInterface.Set_Cancel(0);
// ShowMessage('您当前浏览的网页是https安全链接,请放心使用!');
end else
begin
Memo1.Lines.Add('您当前浏览的网页不是https安全链接!'+sLineBreak );
// Args.ArgsInterface.Set_Cancel(-1);
// ShowMessage('您当前浏览的网页不是https安全链接,本程序禁止运行!');
end;
Args.ArgsInterface.Get_RequestHeaders(LHeaders);
LHeaders.GetIterator(Literator);
Memo1.Lines.BeginUpdate;
Literator.MoveNext(LhasNext);
while LhasNext<>0 do
begin
Literator.Get_HasCurrentHeader(LhasCurrent);
Literator.GetCurrentHeader(Lname,Lvalue);
Memo1.Lines.AddPair('头信息LhasNext:'+LhasNext.ToString+',LhasCurrent:'+LhasCurrent.ToString+',名称:'+Lname,',值:'+Lvalue);
Literator.MoveNext(LhasNext);
end;
Memo1.Lines.Add('请求的uri:'+Luri);
Memo1.Lines.Add('navigation_id:'+Lnavigation_id.ToString);
Memo1.Lines.Add(sLineBreak);
Memo1.Lines.EndUpdate;
end;
procedure TForm_EdgeBrowser01.EdgeBrowser1SourceChanged(
Sender: TCustomEdgeBrowser; IsNewDocument: Boolean);
begin
if IsNewDocument then Memo1.Lines.Add('您打开的新的文档进行浏览!');
end;
procedure TForm_EdgeBrowser01.EdgeBrowser1WebResourceRequested(
Sender: TCustomEdgeBrowser;
Args: TWebResourceRequestedEventArgs);
//:交互式的事件,除非页面元素的事件的javascript代码触发了它:
var
LRequest: ICoreWebView2WebResourceRequest;
LMethod: PWideChar;
LResponse: ICoreWebView2WebResourceResponse;
begin
Args.ArgsInterface.Get_Request(LRequest);//:拿到请求接口
LRequest.Get_Method(LMethod);
Memo1.Lines.Add('请求的Method:'+LMethod);
Args.ArgsInterface.Get_Response(LResponse);//:拿到响应接口
end;
procedure TForm_EdgeBrowser01.EdgeBrowser1WebMessageReceived(
Sender: TCustomEdgeBrowser;
Args: TWebMessageReceivedEventArgs);
//:交互式的事件,除非页面元素的事件的javascript代码postMessage触发了它:
(*//:比如:
<!DOCTYPE html>
<html>
<body>
<p onclick="handleClick()">发送消息</p>
<script>
function handleClick() {
window.chrome.webview.postMessage({ data: 'Message from Edge Chromium', url: window.document.URL });
}
</script>
</body>
</html>
*)
var
LSource: PWideChar;
LwebMessageAsJson: PWideChar;
LwebMessageAsString: PWideChar;
begin
Args.ArgsInterface.Get_Source(LSource);
Memo1.Lines.Add('接收到的Web消息-资源:'+LSource);
Args.ArgsInterface.TryGetWebMessageAsString(LwebMessageAsString);
Memo1.Lines.Add('尝试接收的Web消息-字符串:'+LwebMessageAsString);
Args.ArgsInterface.Get_webMessageAsJson(LwebMessageAsJson);
Memo1.Lines.Add('接收到的Web消息-Json数据:'+LwebMessageAsJson);
MessageBox(Handle, LwebMessageAsJson, PChar('牵引软件-提醒您:'), MB_OK);
//:window.chrome.webview.postMessage的url请求资源的路径Rest要正确否则不回调:...
//AddScriptToExecuteOnDocumentCreated
end;
procedure TForm_EdgeBrowser01.EdgeBrowser1ExecuteScript(
Sender: TCustomEdgeBrowser; AResult: HRESULT;
const AResultObjectAsJson: string);
begin
//:回调:TCustomEdgeBrowser.ExecuteScript(const JavaScript: string);
//:AResult:结果错误码; AResultObjectAsJson:结果对象的Json
TThread.CreateAnonymousThread(
procedure begin
while AResult<>S_OK do sleep(0);
TThread.Synchronize( nil,
procedure begin
Memo1.Lines.Add('EdgeBrowser1执行脚本是否成功(0代表成功):'+IntToStr(AResult));
Memo1.Lines.Add('EdgeBrowser1执行脚本后返回的Json数值(怪了不回调返回null,bug?!):'+AResultObjectAsJson);
Memo1.Lines.Add('其实不是的:是因为你的【调用js】中的js没有function返回Json数据!');
end);
end).Start;
end;
procedure TForm_EdgeBrowser01.BitBtn_HandlePostmessageClick(Sender: TObject);
var LJs:string;
begin
LJs:=
//'<script type="text/javascript">'//:千万不能加,加了就不执行了:跟外部js文件类似,可从外部导入
//'return 12;'+//:一旦返回:就不弹出了:
'window.alert("既然你让我弹出,那你一点’‘确定’‘我就:");'
(*'webview.contentWindow.postMessage.postMessage('
+'{ data: "既然你让我弹出,那你一点’‘确定’‘我就:", , "*" }'
+');'//*)
+'document.getElementById("mytestclick").innerHTML = "我就把它改了!谁让你点(打)我呢!";'
+'console.log("delphi按钮:【调用js】执行了其中的js代码事件");'
//+'return 12;'//:一旦返回:就不弹出了
//+'</script>'//:千万不能加,加了就不执行了:跟外部js文件类似,可从外部导入
;
TThread.CreateAnonymousThread(
procedure begin
TThread.Synchronize( TThread.Current,
procedure begin
Memo1.Lines.Add('TEdgeBrowser控件开始让页面元素执行javascript事件代码......');
EdgeBrowser1.ExecuteScript(LJs);
end);
end).Start;
end;
end.
附:本博客相关博文:
1、《RAD Studio 10.4.1新的基于Chromium的Microsoft Edge浏览器的TEdgeBrowser控件用法》
2、《Delphi Restful之客户端javascript与中间件服务器交互》