TWebBrowser コンポーネントを使う

今更〜〜〜ですが,必要にせまられて作ってたら忘れそうなのでメモメモ。

DelphiIE コンポーネントを使うなら下記のサイトがまとまってます。

ま,TWebBrowser とか IWebBrowser2 とかでググると今でも結構たくさんのページがヒットしますんで。

(上記ページでも言及されていますが)そのまま TWebBrowser を使うといろいろ不具合があります。対処法もいろいろあるのですが,面倒なら下記の TUIWebBrowser コンポーネントを使うと楽らしいです(今回の案件では使いませんでした)。

GET じゃなく POST で Navigate したい

Navigate(URL, Flags, TargetFrameName, PostData, Headers) という引数のプロシージャがあるので PostData に適切に設定すれば POST になる……のですが,ヘッダの Content-Type に application/x-www-form-urlencoded を指定するのを忘れてはいけません。たぶん Content-Length も指定したほうがいいかも。

試してみて色々気をつけるべきことを書きました⇒TWebBrowser での POST - daily dayflower

読み込まれたドキュメントの DOM をいじりたい

例えば自動ログインとかのオートクルーズ機能をつけるときに DOM をプログラムから操作したくなりますよね。

DocumentComplete イベントが発生した後であれば,OleObject の Document プロパティに JavaScript でいうところの document オブジェクトが存在するので,JavaScript で DOM をいじるかのようにアレコレできます。実際には IHTMLDocument(1〜5)インタフェースを介するわけですが,Delphi なので透過的に扱えます。オートメーション万歳*1

e := WebBrowser1.OLEObject.Document.documentElement.getElementById('ほげほげ');

とか。

オンメモリで作成した HTML を読み込ませたい

TWebBrowser.Document プロパティを IStream と見做して Load するのが正攻法といわれてますが(⇒http://www.monazilla.org/document/directwrite.html),正直めんどうくさいですよね。

先ほどの DOM の件を応用して考えると,

WebBrowser1.OLEObject.Document.documentElement.innerHTML := 'ほげほげ〜';

みたくすればいけるかなと思うのですが,このプロパティ,読込はできるんですが(⇒http://hpcgi1.nifty.com/MADIA/DelphiBBS/wwwlng.cgi?print+200511/05110006.txt)書込しようとすると

innerHTML プロパティを設定できませんでした。この操作に 対して無効なターゲット要素です。

と怒られてしまいます。

んで,解決策としては,まず about:blank に Navigate してやり,DocumentComplete イベントを待ちます。この時点で documentElemement.innerHTML は

<HEAD></HEAD>
<BODY></BODY>

というブランクに非常に近いものになっているんですが,ここで WebBrowser1.OLEObject.Document.write(ほげほげ) してやると,上記の内容は消されて新しい html を表示することができます。

ただし,これ,実質 document.write(〜) を行っているようなものなので,連続して行うと,どんどん追記されてしまいます。なので新たに読み込みさせたい場合,その都度 about:blank に Navigate() してやる必要があります。結局この方法も面倒になってしまいました。

別に about:blank である必要はありませんでした。DocumentComplete イベント後,初回の Document.write() 時のみ,既存のドキュメントは上書きされます。

なお,body 要素の中だけ書き換えればいいのなら,Document.body.innerHTML であれば書込可能ですので,それが一番楽かも。

TWebBrowser を非表示にして色々やりたい

TWebBrowser を Visible := false にして使おうと思っても,NavigateComplete2 イベントは発生するものの,DocumentComplete イベントは発生しません。事実上使えないわけです。印刷とか。

で,対処法は,TWebBrowser コンポーネントを親コンポーネントから見えない領域に追いやってしまえばよいです。

  • たとえば TWebBrowser コンポーネントを Width 800, Height 600 で作成
  • Left を -800 とかにする(念のためもうちっと大きめなほうがいいかも)
  • コンポーネント(フォーム等)の AutoScroll プロパティを false にする(フォームにスクロールバーが出てしまうため)

単純な仕組みですが思いつくのに半日かかりました。このへんをダイナミックにいじると,表示したり隠したりもできます。

*1:Visual C++ でも Compiler COM Support を利用すればそんなに大変ではありませんが