RAD Studio 10.4.1的TEdgeBrowser与javascript交互-基于Chromium的Edge浏览器控件用法之二

目录

RAD Studio 10.4.1的TEdgeBrowser与javascript交互-基于Chromium的Edge浏览器控件用法之二

       一、先看看运行代码的效果吧

        1.1、很通畅的浏览Bootstrap响应式H5页面

        1.2、可变比例捕获页面图片只需一句话搞定

         1.3、执行javascript也是一句话的事情

       二、delphi代码如何实现的

        1.1、编写html页面代码,在其中加入页面元素的点击事件

        1.2、编写delphi事件代码

       三、粘上所有delphi源码给你

        1.1、界面部分详见上一篇《RAD Studio 10.4.1新的基于Chromium的Microsoft Edge浏览器的TEdgeBrowser控件用法》

         1.2、代码如下:

 附:本博客相关博文:

 喜欢的话,就在下面点个赞、收藏就好了,方便看下次的分享:


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与中间件服务器交互》

 喜欢的话,就在下面点个赞、收藏就好了,方便看下次的分享:

猜你喜欢

转载自blog.csdn.net/pulledup/article/details/109934357