既存プログラムの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本体
面白いですね。非常に貴重な投稿です。ありがとうございます。
もしよろしければ、経験の範囲で結構ですのでお教えくださればありがたく存じます。
TWeb browser時代、自分もタブブラウザを作成して使っていたことがあります。
edge browserに少し触った限りですと、印刷や印刷プレビューなどはおっしゃる通り、簡単なJavaスクリプトを実行してすぐに実装できました。
しかし、タブブラウザを作成するにはそれだけでは足りず、貼り付けや全て選択、ページ内検索、などのような基本的な動作なども一通り実装する必要がありますが、edge browserにもそのような標準メソッドは実装されていないようでした。
こう言った機能も、やはりJavaスクリプトを介してアクセス、実装する道が大きく開かれているのでしょうか。
また、以前はインターネットオプションのような、基本的な設定を一手に担ってくれる設定ダイアログがありましたが、edge browserにはそのような標準的な設定ダイアログは存在するのでしょうか。(web browserでは、インターネットオプション画面を、プロキシなどの設定や信頼済サイトの設定、履歴削除の時などに使っていました。)
使い回しできるところは使い回ししつつ、自分の望む機能を追加できるのであればいいな、と都合の良いことを思いつつ、感触などお聞かせくだされば非常にありがたく存じます。
実際のところ、このコンポーネントはもっと局所的な、限定的な使途を想定しているのかもしれませんが…
私も少しだけ触ってみました。
ご参考まで。
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
「あ」 さんが「実際のところ、このコンポーネントはもっと局所的な、限定的な使途を想定しているのかもしれませんが…」と書いているように、
少なくとも、私が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」さんのページも、移行作業の途中で拝見させていただきました。
ありがとうございました。
佐藤様、引き続きの貴重なコメント、誠にありがとうございます。非常に参考となりました。
自分はDelphi6パーソナルから入った口で、初心者の頃はタブブラウザを作るサイトを見ながら、オリジナルブラウザ作っておりました。
間も無くそのブラウザも役目を終えることになりますので、後釜を作れればと思いましたが、作業規模が途方もなく大きくなり、現実的ではなさそうかな、という気がしておりました。
寂しいですが、時代の流れでしょうか。
mam様、実は自分も貴方様のページを見て勉強しておりました。その節は意識しておりませんでしたが、大変お世話になりました。
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.
これ実に面白い情報すね。
おそらく参考にならないとは思いますが自分の場合
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をそのまま送ったりも頑張れば出来ました。
take さん
ありがとうございました。
そんな方法もあるのかと参考になりました。
WebView4Delphiを使うと、Webview2がかなり楽に扱えますね。
onNewWindowというイベントもあります。
しかも、Delphi XE10.2Tokyoでも使えました。
あ勧めです。
https://github.com/salvadordf/WebView4Delphi
mr. x-ray様のサイトに使い方があります。
http://mrxray.on.coocan.jp/Delphi/plSamples/936_WebView4Delphi.htm
Webview4Delphiのインストールとソースコードサンプルを掲載しました。
https://mam-mam.net/delphi/tedgebrowser_webview4delphi.html
ご参考まで。
ツイート | ![]() |