一時ファイルに書き出すのもかっこ悪いしパフォーマンスも悪そうだし、やっぱりストリームからロードしたい。と思ってちょっと調べたら、Loading HTML content from a Streamなんていうのを発見。
イベントをハンドリングしなくちゃいけないので、Connection Point関係を自前でやらなくちゃいけないのが面倒だけど、それほど手間ではなさそう。
でもそうすると、今度はmultipart/relatedで格納されているインラインイメージとかをロードするときのフックが必要(cid:....というURIからストリームにマップする)なんだけど、それはどうやるんだろうか?
上に書いたやり方でHTMLをロードするとちょっと問題があることが発覚。
まともなHTMLならば問題なさそうなのだけれど、ちゃんとしていないHTMLをロードするとテキスト扱いになる。たとえば、いきなり<br>とかから始まるようなものとか。
で、色々と調べた結果、IWebBrowser2からget_DocumentしてIHTMLDocument2を取得して、そのインターフェイスに対して、open、write、closeを順番に呼び出していくことにした。これだと、MediaTypeを指定できるし(今のところtext/htmlしかサポートしていないみたいだけれど)、変なHTMLもそれなりに解釈してくれる。スクリプトからだったらよくつかったけど、こんなところで使うとは思わなかった。
ちなみに、2002/01/30に書いたセキュリティの制限は、多少ならばホストにIDispatchを実装して対応できるらしい(WebBrowser Customization)。今回の場合、ATL側のホストのIDispatchはいじれないのでダメだけど。
上に書いたインラインイメージのハンドルは、IInternetProtocolを実装して、IServiceProviderのQueryServiceから返すことでできそう(ServiceIDがわからないけれど、IID_IInternetProtocolを要求してくる)。WebBrowserが"cid:..."というURLを見つけるとURLモニカがバインドするときに、IInternetProtocolを要求するようなので、このURLのIDと等しいContent-IDを持つパートのデータを用意して、IInternetProtocol::Readが呼ばれたときに返してやれば良さそう。
こんなことをしなくてもMHTML (Multipart HTML)を使えばよいじゃんという話もあるんだけれども、
1. 一度ファイルに落とさずにMHTMLをロードさせる方法がわからない
2. IE4(少なくともHPC2Kのやつ)はMHTMLをサポートしていない
というようなことによりメモリ中でぐりぐりやるのでした。