TEdgeBrowserへの移行について(参考になれば)

解決


佐藤  2022-05-18 14:15:22  No: 150206  IP: [192.*.*.*]

既存プログラムのTEdgeBrowserへの移行が苦労しながらもおおむね順調に進んでいます。
個人の感想としては、TWebBrowserと比較として便利な点は多いです。特に、ExecuteScriptでJavaScriptを実行できるのは本当にありがたいです。
ただし、アナウンスに「イベントの多くは、Edge WebView2 コントロールから呼び出されるため、メインのユーザー インターフェイス スレッド以外のスレッドで呼び出される可能性がある点に注意してください。」とあるように、これまではWebBrowser上のマウスの立ち振る舞いを、ApplicationEventsで拾えたものが、スレッドが異なるため拾えなくなるなど、それなりの改修の工夫は必要でした。(Timerで拾うことで解決)
ユーザーのパソコンに、Microsoft WebView2 ランタイムのインストールを促す必要があるため、レジストリーの値を読み取ってインストール済かどうか判断し、必要であればインストーラーを起動することにしました。
参考になるかもしれませんので、以下に私が作成したUnit掲載します。
もっと、うまい方法がある方は、書き込みをしてください。

事前準備、
次のUrlから
      https://go.microsoft.com/fwlink/p/?LinkId=2124703
「MicrosoftEdgeWebview2Setup.exe」がダウンロードできます。
これを、Delphiで作成したプログラムの.exeのあるフォルダに「\bin」を作成しその中にコピーし、配布します。

Delphiのソースの中での使用
Delphiで作成したプログラムの起動時(もしくはTEdgeBrowserを使用するFormのonCreatew)のタイミングで
InstallMicrosoftWebView2RunTime()を呼び出すと、
インストール済みかどうか調べて、インストールを開始します。


環境はDelphi Ver10.4、Win10です。

//////  以下、Unit1本体  ///////
unit Unit1;

interface

uses Windows, SysUtils, Registry, Vcl.Dialogs, Vcl.Controls, Forms;

//プログラムの実行をして、その終了を待つ
function WinExecAndWait32(FileName: string; Visibility: Integer): Longword;

//OSが32bitか64bitか
function Is64bitWindows: Boolean;
//レジストリーが存在するかどうか
function ExistRegKey(RegKey: String): Boolean;
//Microsoft WebView2 ランタイムのインストール
function InstallMicrosoftWebView2RunTime(): Boolean;


implementation

//プログラムの実行をして、その終了を待つ
function WinExecAndWait32(FileName: string; Visibility: Integer): Longword;
var
  StartupInfo: TStartupInfo;
  ProcessInfo: TProcessInformation;
begin
  FillChar(StartupInfo, SizeOf(StartupInfo), #0);
  StartupInfo.cb          := SizeOf(StartupInfo);
  StartupInfo.dwFlags     := STARTF_USESHOWWINDOW;
  StartupInfo.wShowWindow := Visibility;

  if not CreateProcess(nil,
    PChar(FileName),      // pointer to command line string
    nil,                  // pointer to process security attributes
    nil,                  // pointer to thread security attributes
    False,                // handle inheritance flag
    CREATE_NEW_CONSOLE or // creation flags
    NORMAL_PRIORITY_CLASS,
    nil,                  // pointer to new environment block
    nil,                  // pointer to current directory name
    StartupInfo,          // pointer to STARTUPINFO
    ProcessInfo)          // pointer to PROCESS_INF
  then Result := WAIT_FAILED
  else
  begin
    WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
    GetExitCodeProcess(ProcessInfo.hProcess, Result);
    CloseHandle(ProcessInfo.hProcess);
    CloseHandle(ProcessInfo.hThread);
  end;
end; { WinExecAndWait32 }


//OSが32bitか64bitか
function Is64bitWindows: Boolean;
var
  Wow64Proc : function(hProcess: THandle; var Wow64: BOOL): BOOL stdcall;
  RetFlag   : LongBool;
begin
  // Windows Vista以上の場合
  @Wow64Proc := GetProcAddress(GetModuleHandle('Kernel32.dll'), 'IsWow64Process');
  if @Wow64Proc <> nil then
  begin
    Wow64Proc(GetCurrentProcess, RetFlag);

    if SizeOf(THandle) = 4 then
    begin
      Result := RetFlag;
    end
    else
    if SizeOf(THandle) = 8 then
    begin
      Result := True;
    end;
  end
  else
  begin
    // Windows XPの場合、64ビット版か
    Result := CheckWin32Version(5, 2);
  end;
end;

//レジストリーが存在し値が正常化どうか
//検索する Rootkey は HKEY_LOCAL_MACHINE, HKEY_CURRENT_USER
function ExistRegKey(RegKey: String): Boolean;
var
  Reg: TRegistry;
  RegV: String;
begin
  Reg := TRegistry.Create;
  try
    Reg.Rootkey := HKEY_LOCAL_MACHINE;
    Result := Reg.OpenKey(RegKey, False);
    RegV := Reg.ReadString('pv');
    if Result = False
    then
    begin
      Reg.Rootkey := HKEY_CURRENT_USER;
      Result := Reg.OpenKey(RegKey, False);
      RegV := Reg.ReadString('pv');
    end;
  finally
    Reg.CloseKey;
    Reg.Free;
    //pvが ''の場合は False
    if RegV = '' then Result := False;
  end;
end;

//Microsoft WebView2 ランタイムのインストール
function InstallMicrosoftWebView2RunTime(): Boolean;
var
  PrgName: String;
  RegKey: String;

  function InstalledRunTime(): Boolean;
  var
    RegKey: String;
  begin
    RegKey := 'Software\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}';
    if ExistRegKey(RegKey)
    then
    begin
      Result := True;
      Exit;
    end
    else
    begin
      if Is64bitWindows
      then
       //64bit
       RegKey := 'SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}'
      else
       RegKey := 'SOFTWARE\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}';

      if ExistRegKey(RegKey)
      then
      begin
       Result  := True;
       Exit;
      end
      else
        Result := False;
    end;
  end;

begin
  //インストール済かどうか
  if InstalledRunTime()
  then
    begin
      Result := True;
      Exit;
    end
  else
  begin
    if MessageDlg('このプログラムでは、Webの表示に Microsoft WebView2 が必要です'  + #13#10 +
                  'Microsoft WebView2ランタイム をインストールをしてよろしいですか' + #13#10 + #13#10 + #13#10 +
                  '管理者権限でないとインストールに失敗する場合があります' + #13#10 +
                  '失敗した場合は、改めて管理者権限で実行してください',
                  mtConfirmation, [mbYes, mbAbort], 0)
       = mrYes
    then
    begin
      PrgName := (ExtractFilePath(Application.ExeName) + '\bin\MicrosoftEdgeWebview2Setup.exe');
      WinExecAndWait32(PrgName, SW_SHOW);

      //インストールに成功したかどうか、レジストリーを確認
      if InstalledRunTime()
      then begin
        MessageDlg('インストール成功',
                mtInformation, [mbClose], 0);
        Result  := True;
     end
     else begin
        MessageDlg('インストール失敗',
              mtWarning, [mbClose], 0);
        Result := False;
      end;
     end
  end;
end;

end.

//ここまで、Unit1本体

編集    削除
 2022-05-18 19:48:49  No: 150208  IP: [192.*.*.*]

面白いですね。非常に貴重な投稿です。ありがとうございます。
もしよろしければ、経験の範囲で結構ですのでお教えくださればありがたく存じます。

TWeb browser時代、自分もタブブラウザを作成して使っていたことがあります。
edge browserに少し触った限りですと、印刷や印刷プレビューなどはおっしゃる通り、簡単なJavaスクリプトを実行してすぐに実装できました。
しかし、タブブラウザを作成するにはそれだけでは足りず、貼り付けや全て選択、ページ内検索、などのような基本的な動作なども一通り実装する必要がありますが、edge browserにもそのような標準メソッドは実装されていないようでした。
こう言った機能も、やはりJavaスクリプトを介してアクセス、実装する道が大きく開かれているのでしょうか。
また、以前はインターネットオプションのような、基本的な設定を一手に担ってくれる設定ダイアログがありましたが、edge browserにはそのような標準的な設定ダイアログは存在するのでしょうか。(web browserでは、インターネットオプション画面を、プロキシなどの設定や信頼済サイトの設定、履歴削除の時などに使っていました。)
使い回しできるところは使い回ししつつ、自分の望む機能を追加できるのであればいいな、と都合の良いことを思いつつ、感触などお聞かせくだされば非常にありがたく存じます。
実際のところ、このコンポーネントはもっと局所的な、限定的な使途を想定しているのかもしれませんが…

編集    削除
mam  URL  2022-05-19 09:49:49  No: 150209  IP: [192.*.*.*]

私も少しだけ触ってみました。
ご参考まで。

Delphi10.4.2(Community Edition)のTEdgeBrowserを使う
https://mam-mam.net/delphi/tedgebrowser.html

Delphi10.4.2のTEdgeBrowserで
face-api.jsを使って写真から顔領域を取得する
https://mam-mam.net/delphi/tedgebrowser_face-api.html

SetVirtualHostNameToFolderMappingを
使ってローカルフォルダをWEBサイトのように扱う 
https://mam-mam.net/delphi/tedgebrowser_webview2.html

編集    削除
佐藤  2022-05-19 14:14:44  No: 150210  IP: [192.*.*.*]

「あ」 さんが「実際のところ、このコンポーネントはもっと局所的な、限定的な使途を想定しているのかもしれませんが…」と書いているように、
少なくとも、私がDelphiの中でWebBrowserを使用するのは、限定的でかつ必要にかられた場合だけです。
Htmlを表示するだけ、あるいは外部のUrlを表示するだけならば、ShellExecuteでも使って、ChromeやEdgeに渡して表示する方が簡単です。実際に迷わずそうします。
それでも、これからもTEdgeBrowseを使用するのは、「ユーザーがBrowserで何を行ったか」を判定して、次の処理に移るような場合です。
具体的な例として、"地図を表示して、クリックした位置の座標を取り込む"ような場合です。
地図を表示するのは、Pascalでは難しく、Htmlを書くことになります。
地図を表示するだけならば、Delphi側でHtmlをShellExecuteでChromeなどに渡して表示すれば済みますが、
クリックした位置の経度を取り込むには、プログラムでコントロールできる範囲にBrowseを置く必要がありTEdgeBrowseを利用するのが便利だと思います。
移行作業で経験したのは、従来のIEベースのTWebBrowserでは、「webBrowser1.Document as IHTMLDocument2」の解析で、ある程度のユーザーが行ったBrowser上の立ち振る舞いの結果が読み取れたのが、
TEdgeBrowseの.innerHTMLあるいは.outerHTMLあるいは.innerTextでは判定できない等の場面があり、Htmlを書き換えててinnerHTMLで確認がとれるようにするなどの見直しが必要でした。
また、先に書いたようにTEdgeBrowseがプログラムとは独立したスレッドで動くため、どのタイミングで.innerTextを確認すればよいのかを、ApplicationEventsでトリガーを拾うということが出来ず、
Timerで1秒おきに.innerTextを確認する方法に代替して、ほかにもっとうまい方法がありそうだと思いながらも実用的にはユーザーストレスも発生せずに済んでいます。
「あ」 さんへの回答になっていないと思いますが、自作のBrowswerを作るのも面白そうではありますが、実用的にはChromeやEdgeで十分なので深く調べたことはなく、私の知識ではご期待に沿えません。

「mam」さんのページも、移行作業の途中で拝見させていただきました。
ありがとうございました。

編集    削除
 2022-05-19 21:01:08  No: 150211  IP: [192.*.*.*]

佐藤様、引き続きの貴重なコメント、誠にありがとうございます。非常に参考となりました。
自分はDelphi6パーソナルから入った口で、初心者の頃はタブブラウザを作るサイトを見ながら、オリジナルブラウザ作っておりました。
間も無くそのブラウザも役目を終えることになりますので、後釜を作れればと思いましたが、作業規模が途方もなく大きくなり、現実的ではなさそうかな、という気がしておりました。
寂しいですが、時代の流れでしょうか。
mam様、実は自分も貴方様のページを見て勉強しておりました。その節は意識しておりませんでしたが、大変お世話になりました。

編集    削除
佐藤  2022-05-23 08:27:36  No: 150213  IP: [192.*.*.*]

TEdgeBrowserへの変更を加えたプログラムを、同時に5本の配布することになり、
全てに、「\bin\MicrosoftEdgeWebview2Setup.exe」を入れおくのはスマートでないと、
インストールをする必要がある場合に、MicrosoftEdgeWebview2Setup.exeをダウンロードすることにしました。
この方法だと、「\bin\MicrosoftEdgeWebview2Setup.exe」を同梱する必要はなくなりました。
以下、改修版 です。

//////  以下、Unit1本体  ///////
unit Unit1;

interface

uses Windows, SysUtils, Registry, Vcl.Dialogs, Vcl.Controls, Forms, UrlMon;

// UrlDownloadToFile関数を使いダウロード
function DownloadFile(SourceUrl, SaveFileName: string): Boolean;

// プログラムの実行をして、その終了を待つ
function WinExecAndWait32(FileName: string; Visibility: Integer): Longword;

// OSが32bitか64bitか
function Is64bitWindows: Boolean;
// レジストリーが存在するかどうか
function ExistRegKey(RegKey: String): Boolean;
// Microsoft WebView2 ランタイムのインストール
function InstallMicrosoftWebView2RunTime(): Boolean;

implementation

// UrlDownloadToFile関数を使いダウロード
// 成功:True 失敗:False
function DownloadFile(SourceUrl, SaveFileName: string): Boolean;
var
  Path: String;
begin
  Path := ExtractFilePath(SaveFileName);
  // フォルダがあるか確認
  // フォルダがない場合は作成
  if not DirectoryExists(Path) then
    ForceDirectories(Path);
  // ダウンロード
  try
    Result := UrlDownloadToFile(nil, PChar(SourceUrl), PChar(SaveFileName), 0,
      nil) = 0;
  except
    Result := False;
  end;
end;

// プログラムの実行をして、その終了を待つ
function WinExecAndWait32(FileName: string; Visibility: Integer): Longword;
var
  StartupInfo: TStartupInfo;
  ProcessInfo: TProcessInformation;
begin
  FillChar(StartupInfo, SizeOf(StartupInfo), #0);
  StartupInfo.cb := SizeOf(StartupInfo);
  StartupInfo.dwFlags := STARTF_USESHOWWINDOW;
  StartupInfo.wShowWindow := Visibility;

  if not CreateProcess(nil, PChar(FileName), // pointer to command line string
    nil, // pointer to process security attributes
    nil, // pointer to thread security attributes
    False, // handle inheritance flag
    CREATE_NEW_CONSOLE or // creation flags
    NORMAL_PRIORITY_CLASS, nil, // pointer to new environment block
    nil, // pointer to current directory name
    StartupInfo, // pointer to STARTUPINFO
    ProcessInfo) // pointer to PROCESS_INF
  then
    Result := WAIT_FAILED
  else
  begin
    WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
    GetExitCodeProcess(ProcessInfo.hProcess, Result);
    CloseHandle(ProcessInfo.hProcess);
    CloseHandle(ProcessInfo.hThread);
  end;
end; { WinExecAndWait32 }

// OSが32bitか64bitか
function Is64bitWindows: Boolean;
var
  Wow64Proc: function(hProcess: THandle; var Wow64: BOOL): BOOL stdcall;
  RetFlag: LongBool;
begin
  // Windows Vista以上の場合
  @Wow64Proc := GetProcAddress(GetModuleHandle('Kernel32.dll'),
    'IsWow64Process');
  if @Wow64Proc <> nil then
  begin
    Wow64Proc(GetCurrentProcess, RetFlag);

    if SizeOf(THandle) = 4 then
    begin
      Result := RetFlag;
    end
    else if SizeOf(THandle) = 8 then
    begin
      Result := True;
    end;
  end
  else
  begin
    // Windows XPの場合、64ビット版か
    Result := CheckWin32Version(5, 2);
  end;
end;

// レジストリーが存在し値が正常化どうか
// 検索する Rootkey は HKEY_LOCAL_MACHINE, HKEY_CURRENT_USER
function ExistRegKey(RegKey: String): Boolean;
var
  Reg: TRegistry;
  RegV: String;
begin
  Reg := TRegistry.Create;
  try
    Reg.Rootkey := HKEY_LOCAL_MACHINE;
    Result := Reg.OpenKey(RegKey, False);
    RegV := Reg.ReadString('pv');
    if Result = False then
    begin
      Reg.Rootkey := HKEY_CURRENT_USER;
      Result := Reg.OpenKey(RegKey, False);
      RegV := Reg.ReadString('pv');
    end;
  finally
    Reg.CloseKey;
    Reg.Free;
    // pvが ''の場合は False
    // if RegV = '' then Result := False;
  end;
end;

// Microsoft WebView2 ランタイムのインストール
function InstallMicrosoftWebView2RunTime(): Boolean;
const
  RegKey1 = 'Software\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}';
  RegKey2 = 'SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}';
var
  SourceUrl, PrgName, RegKey: String;
  function InstalledRunTime(): Boolean;
  var
    RegKey: String;
  begin
    if ExistRegKey(RegKey1) then
    begin
      Result := True;
    end
    else if ExistRegKey(RegKey2) then
    begin
      Result := True;
    end
    else
      Result := False;
  end;

begin
  // ラタイムがインストール済かどうか確認
  if InstalledRunTime() then
  begin
    Result := True;
    Exit;
  end
  else
  begin
    if MessageDlg('このプログラムでは、Webの表示に Microsoft WebView2 が必要です' + #13#10 +
      'Microsoft WebView2ランタイム をインストールをしてよろしいですか' + #13#10 + #13#10 + #13#10 +
      '管理者権限でないとインストールに失敗する場合があります' + #13#10 + '失敗した場合は、改めて管理者権限で実行してください',
      mtConfirmation, [mbYes, mbAbort], 0) = mrYes then
    begin
      PrgName := ExtractFilePath(Application.ExeName) +
        '\bin\MicrosoftEdgeWebview2Setup.exe';
      if not FileExists(PrgName) then
      begin
        // インストーラーをダウンロード
        SourceUrl := 'https://go.microsoft.com/fwlink/p/?LinkId=2124703';
        if not DownloadFile(SourceUrl, PrgName) then
        begin
          MessageDlg('ダウンロード失敗', mtWarning, [mbClose], 0);
          Exit;
        end
        else
        begin
          // インストールの実行をして終了を待つ
          WinExecAndWait32(PrgName, SW_SHOW);
          // インストールに成功したかどうか、レジストリーを確認
          if InstalledRunTime() then
          begin
            MessageDlg('インストール成功', mtInformation, [mbClose], 0);
            Result := True;
          end
          else
          begin
            MessageDlg('インストール失敗', mtWarning, [mbClose], 0);
            Result := False;
          end;
        end;
      end;
    end
    else // インストール中止
      Result := False;
  end;
end;

end.




 

編集    削除
take  2022-05-23 11:09:30  No: 150214  IP: [192.*.*.*]

これ実に面白い情報すね。
おそらく参考にならないとは思いますが自分の場合

1.組むプログラムはDelphi
2.画像やデータの管理はDelphi側で行う
3.データの表示や編集はOSを問わず、WebBrowserからアクセスして処理する

という案件があったので

・DelphiでHTTPサーバープログラムを作成
・自分自身へのデバッグの場合Delphi からShellを使って
・ChromeBrowserを自分自身のIPアドレスと専用ポート宛てにURLを開かせる
・ChromeBrowserからのHTTP通信をDelphi側で受け取りHTMLとJavascriptを返す
・WebBrowserの中で拾いたいイベントあるときはイベント処理内にajax で json形式のデータを送るJavascriptを記述
・それをDelphi側で受け取る

という方法で成功しています。

昔はJavascript側でGUIを設計するのが難しかったのですが
今はslickとかフレームワークを使って
Delphi側のStringGirdをそのまま送ったりも頑張れば出来ました。

編集    削除
佐藤  2022-05-23 17:35:25  No: 150215  IP: [192.*.*.*]

take さん
ありがとうございました。
そんな方法もあるのかと参考になりました。

編集    削除
mam  URL  2022-05-24 16:22:39  No: 150216  IP: [192.*.*.*]

WebView4Delphiを使うと、Webview2がかなり楽に扱えますね。
onNewWindowというイベントもあります。
しかも、Delphi XE10.2Tokyoでも使えました。
あ勧めです。
https://github.com/salvadordf/WebView4Delphi
mr. x-ray様のサイトに使い方があります。
http://mrxray.on.coocan.jp/Delphi/plSamples/936_WebView4Delphi.htm

編集    削除
mam  URL  2022-05-31 21:48:19  No: 150226  IP: [192.*.*.*]

Webview4Delphiのインストールとソースコードサンプルを掲載しました。

https://mam-mam.net/delphi/tedgebrowser_webview4delphi.html

ご参考まで。

編集    削除