TWebBrowserからプログラムに値を渡す方法

解決


AND@岡山  2020-05-11 16:18:19  No: 148729

TWebBrowserで表示したhtml内のjavascriptからプログラムに値を渡す方法を模索しています
プログラム側からhtml内のjavascriptを実行して値を取ることはXRAYさんのサンプル等で出来ることを理解しているのですが
html側のアクション(javascript)からプログラム(Delphi関数)に値を渡すことが出来ません

MSDN等でObjectForScriptingを使って親の関数を呼ぶサンプルは見つけたのですが
https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.forms.webbrowser.objectforscripting?view=netcore-3.1

・TWebBrowser(TCppWebBrowser)にObjectForScriptingのプロパティが無い
・PermissionSetとInteropServices.ComVisibleAttributeの指定方法がDelphiで解らない

と詰まってしまいました
対象のhtmlファイルは自作のローカル側なので記述内容に制限はありません
代替でもいいのですが何か値を取得する方法はありますでしょうか?


AND@岡山  2020-05-12 14:59:20  No: 148732

理想に近いことが下記で出来そうではあるのですが・・・

ScriptGate
https://qiita.com/pik/items/dacb1be7ad528d27803a

・Windows限定なのでFireMonkeyではなくVCLでやりたい(過去資産含め)
・Pascalソースを見たけれどJavaScriptから呼び出す時に使うスキームの指定部分が理解出来ない
・VCLのTWebBrowserとFireMonkeyのTWebBrowserの違いの部分が影響?VCLでは再現不可?

とECBへの移植までは至りませんでした


take  2020-05-14 12:57:16  No: 148739

まさか自分と同じような要望があるとは・・・

TWebBlowserに関係無くjavascriptから送信出来る相手先は今通信を行っているホストサーバーのみです。
HTTPのサーバークラスを用意できたらポートをオープンして待機
接続リクエストがあったら javascript付きのhtml たとえばindex.htmlの中身をHTTP形式で送信
相手がボタックリックなどでjavascript経由の送信を行うと、これもGETやPOSTに似たデータがサーバーに送られてくるので処理

という方法が一連の流れになります。

ただ、こんな物は世の中には無いので自作するしかありません。

htmlの中に書いた <delphi $i />を変数としてブラウザ表示するソフトを作った流れで
上記のようなものを作ったことがありますが、かなり厳しいものと思われます。


AND@岡山  2020-05-15 10:43:15  No: 148744

自分の要件としてはローカルhtmlまたは内部生成のhtmlからなので実際の通信環境の制限までは行き着いていないのですが
上に書いたMicrosoft系(C#)ならObjectForScriptingで、DelphiもFireMonkeyだとjavascriptから外部関数のパラメータ付きの呼び出しと戻り値の取得が出来そうに思えます
表示しているhtml側からのアクションさえ拾えさえすれば複雑な値のやりとりはIHTMLCocument経由でおそらくこなせます

他に反応が無いってことはそろそろVCLを諦めろって状況なんですかね・・・・


Mr.XRAY  2020-05-15 11:19:51  No: 148745

> html側のアクション(javascript)からプログラム(Delphi関数)に値を渡すことが出来ません

JavaScript から Delphi 側に値を返すのではなく,
Delphi 側で JavaScript の関数を実行したら,その結果を受け取る,
という方式はダメですか ?
ScriptGate の場合は Eval 関数がそれに相当しますね.

ScriptGate の説明記事に

< 逆に Delphi のメソッドを JavaScript からコールできます。

とありますが,これは JavaScript の実行とは違うような気がします.


Mr.XRAY  2020-05-15 11:56:04  No: 148746

> Delphi 側で JavaScript の関数を実行したら,その結果を受け取る,
> という方式はダメですか ?

ゴメンなさい.
これだと,JavaScript を必ず Delphi から実行する必要がありますね.
ブラウザから,あるいは HTML コードの何かが JavaScript 実行のトリガーになっている
場合は無理ですね.


Mr.XRAY  2020-05-15 12:25:08  No: 148747

意味不明の変なレスをして申し訳ありませんでした.
以下のようなのは利用できないでしょうか.

[ 943_IE オブジェクトのイベントシンク ]
http://mrxray.on.coocan.jp/Delphi/plSamples/943_IEObject_EventsSink.htm


Mr.XRAY  2020-05-15 12:41:56  No: 148748

TMSHTMLHTMLScriptEvents

というのがあります.これが使えそうな気がします.


AND@岡山  2020-05-15 13:07:24  No: 148749

ScriptGateだとコンストラクタで与えたスキーム経由でJavaScriptからDelphiメソッドの呼び出しが出来るっぽいですよね
TScriptGate.CallEventで何かやってると思うんですが自分では理解出来ませんでした
ObjectForScriptingが目的直球なのでこれが動けば楽にいけそうなんですけどねぇ

>[ 943_IE オブジェクトのイベントシンク ]
>http://mrxray.on.coocan.jp/Delphi/plSamples/943_IEObject_EventsSink.htm
>TMSHTMLHTMLScriptEvents

上記を少し掘り下げてみます
生粋のDelphi使いでは無いためpascalサンプルからMSDN引いて内容を理解し
バージョン差異に引っかかりつつECBに書き直してテストするって
山なりボールで着地点を狙うのでちょっと時間がかかりそうですが;;


take  2020-05-15 14:11:56  No: 148750

やりたいことをもう少し説明して頂けるとアドバイスできるかも知れません。

VCLかどうかは関係無くHTTPサーバーとしての通信をどこまで制御出来るかで変わります。
ブラウザを問わず、Indyで多分出来るかな?といった方向で説明します。

「javascriptから」ということですが反対にjavascriptを使わなければ
IndyなどのHTTPサーバークラスを使ってサーバーを起動する
値入力FORMとGETやPOST用のボタンのHTMLを index.htmlに記述する
ブラウザから自分自身localhost宛のURLで起動する。
ブラウザで値を入力してボタンを押す。
Indyのサーバークラスのイベント?でGET、POSTを検知してさらに値も入手する

Indyを使ったことが無いですがこういう処理になります。

「javascriptから」という指定からするとブラウザの画面移動なしで値だけ送りたいのかな?
と感じたのでその場合

javascrit関数
function request(page)
{
    if(page == '') return;
    httpRequest.abort();
    httpRequest.open('GET', 'index.htm' + page + '', true);
    httpRequest.onreadystatechange = function() {
        if(httpRequest.readyState == 4) {
            if(httpRequest.status == 200) {
                document.getElementById('sw_text').value = httpRequest.responseText;
            }
        }
    }
    httpRequest.send(null);
}

HTMLの方
 <a href="javascript:request(1);">request(1)</a><br />
 <textarea id="sw_text" style="width: 100%; height: 200px;"></textarea>

こんな感じで javascriptを実行すると

index.html 1 ←引数の値

が HTTPサーバーに送られ、サーバー側で返事をするとHTML の textarea に入ります。

そういえばもう枯れて滅んだ技術ですがDelphi4ぐらいのときにWEBサーバーIISとDelphiの連携で
サーバーアプリケーションを使う comやら ActiveXやら書籍やらがあってそれを使った覚えがあります。

今は無いのかなと思ったらちゃんとありました!

Delphi新規プロジェクト WebBroker  Webサーバーアプリケーションで普通に作れますね。
※DelphiXE5で確認

フォームに入力して送信してDelphiで受け取ってブラウザで表示するサンプルもありました
https://mam-mam.net/delphi/web.html


AND@岡山  2020-05-15 14:44:04  No: 148753

>takeさん
>やりたいことをもう少し説明して頂けるとアドバイスできるかも知れません。

やりたいことはもっともっとシンプルです
ここでやってることのDelphi版です
http://www.balard.sakura.ne.jp/vb/ajax/javascript_vb.php

これをVCLでやろうとして詰まっているところです


take  2020-05-15 16:41:13  No: 148754

そのサンプルは内部で複雑なことをしているのですがDelphiで何とか出来ないか考えました。

HTMLファイル たとえば index.html に下記の様に記述します。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<HTML>
<HEAD>
<TITLE>TWebBrowserTest</TITLE>
<script>
function javascripttodelphi()
{
window.location.href = 'value=1234'
}
</script>
</HEAD>
<BODY>
 <a href="javascript:javascripttodelphi();">javascripttodelphi()</a><br />
</BODY>
</HTML>

リンクで javascript の javatodelphi が呼ばれます。
結果がリンク先として渡されます。
値 value=1234を URLとして移動するのでブラウザ側ではエラーになりますがそこは
index.html?value=1234 になるなど工夫して下さい。

Delphi側ではTWebBrowserを置きます。
TWebBrowserのイベントonBeforeNavigate2を実装します。

Caption := URL;

URLの変更を検知してフォームのキャプションに表示

あとはWebBrowser1.Navigate(URL);
などして上記の index.htmlを読み込ませます。

リンクをクリックすると JavaScriptからDelphiが呼ばれて
JavaScriptの値ごとDelphiに届きます。


Mr.XRAY  2020-05-16 10:27:48  No: 148756

> 対象のhtmlファイルは自作のローカル側なので記述内容に制限はありません 

ということなので,リスナーを使用しないサンプルを作成してみました.

[ 05_window.external を使用して JavaScript の実行結果を取得 ]
http://mrxray.on.coocan.jp/Delphi/plSamples/943_IEObject_EventsSink.htm#05

> ここでやってることのDelphi版です
http://www.balard.sakura.ne.jp/vb/ajax/javascript_vb.php

と同じようなサンプルにしてみました.
上のリンクの記事のコードをコピペして作成しようと思ったら,
< や > とか空白文字が全角 !! 

> 他に反応が無いってことはそろそろVCLを諦めろって状況なんですかね・・・・

そういうことではないと思いますが・・・


AND@岡山  2020-05-18 09:49:27  No: 148759

>Mr.XRAY さん
ありがとうございます
まとめると以下の2パターンで対応可能と感じました

1:URLの変更を検知してイベント発生(TWebBrowserのみで対応可)
2:TEmbeddedWBを使用してJavaScriptのwindow.externalを使用して連携

2の方がスマートですが個人的には1の方が好みだったりします
#標準コンポーネントのみで対応が出来るため

これから
・TEmbeddedWBのインストールと理解
・Pascal記述>C++記述の翻訳
・実装及び耐久テスト
をしてみます
#Mr.XRAY さんがどこかに書かれていたTWebBrowserの耐久力が気になりますが

>> 他に反応が無いってことはそろそろVCLを諦めろって状況なんですかね・・・・
>そういうことではないと思いますが・・・ 

資産があるのとKylixのトラウマがあってなかなかFireMonkeyへの完全移行が踏み出せなかったりします
時代の流れに合わせたUIデザインを、と考えるとFireMonkeyの方がいいんでしょうけどねぇ
おっさんはここらで引退して若い者に任せたい所です(笑


AND@岡山  2020-05-18 10:26:05  No: 148760

今回のTWebBrowserもですが(他に別の場所で書いたTPrinterも)
VCL経由だと触れないメソッドやプロパティがあるのが辛い所です
MSDNやC#のサンプル見てVCLで実装・・・の時に引っかかります

元の追加分について更新されないってのはやっぱり互換性の問題なんでしょうかねぇ


Mr.XRAY  2020-05-19 11:51:42  No: 148766

解決済みにはなっていますが,

> #標準コンポーネントのみで対応が出来るため

という方もいるかも知れません.
後日の参考用に. ,
TWebBrowser で window.external を使用可能にするための記事を追加しました.

Delphi 10 Seattle 以降用
[ 06_WebBrowser で window.external を使用可能にする方法 ]
http://mrxray.on.coocan.jp/Delphi/plSamples/943_IEObject_EventsSink.htm#06

Delphi XE8 以前用
[ 07_window.external を使用可能にする IDocHostUIHandle の実装 ]
http://mrxray.on.coocan.jp/Delphi/plSamples/943_IEObject_EventsSink.htm#07


pik  2020-05-19 13:07:24  No: 148767

もう遅いと思いますが TWebBrowser から値を取る場合は IHTMLDocument2.Script を使います。
https://docs.microsoft.com/en-us/previous-versions//aa752604(v=vs.85)?redirectedfrom=MSDN
詳しくはこちらに。
https://qiita.com/pik/items/d9ca19c3eef6a48132f7#windows-%E3%81%A7-javascript-%E9%96%A2%E6%95%B0%E3%81%AE%E6%88%BB%E3%82%8A%E5%80%A4%E3%82%92%E5%8F%96%E3%82%8B


AND@岡山  2020-05-20 14:39:36  No: 148769

>Mr.XRAY さん
またまたありがとうございます
この方法が一番良さそうですので実装(移植)をしてみます
特にXE8以前のことまでまとめて頂いているのが非常に助かります
#実はまだ資産の関係でXE3からバージョンアップ出来ていない

Mr.XRAYさんのここのページは質問前に何回も読んでいてhtml側からはまとまっているのに
外からは何故無いんだろう、ひょっとして需要無しか他にもっと楽な方法があるのかも?
とか勝手に妄想していました(笑
同じページにまとめて頂いていていますので今後自分と同じ需要が出た場合の参考なると思います

>pik さん
申し訳ない無い、引用して頂いた所はDelphi>html(JS)のことで
自分が求めていたhtml(JS)>Delphiとは逆の内容になります
ScriptGateのVCL版があればそのまま使ってたかもしれないんですけどね・・・


Mr.XRAY  2020-05-25 17:06:15  No: 148772

> ScriptGateのVCL版があればそのまま使ってたかもしれないんですけどね・・・ 

もしかしたら FireMonkey て同じことをしたい方がいるかも知れません.
作者の解説記事があるのですすが,私もテストしてみました.参考になれば.
ScriptGate には eval という JavaScript を実行して戻値を取得するメソッドもあります.

[ ScriptGate のテスト ]
http://mrxray.on.coocan.jp/Delphi/Others/000-004.htm


Mr.XRAY  2020-05-25 17:23:32  No: 148773

> ScriptGate には eval という JavaScript を実行して戻値を取得するメソッドもあります.

失礼しました.
eval は CallScript メソッドで使用しいる実装ですね.
eval は JavaScript の関数です.
当サイトのサンプルでも使用しています.

[ 05_JavaScript の関数の return 値の取得 ]
http://mrxray.on.coocan.jp/Delphi/Others/JavaScript_TWebBowser.htm#05


※返信する前に利用規約をご確認ください。








  このエントリーをはてなブックマークに追加