2002-01-30 HTMLメールの表示 [長年日記]

HTMLメールの表示

HTMLメールの表示コードを入れる。

全体は、SDKベースで書いてあるので(MFC/ATLは使わず)、IEのコントロールをホストするのに、ActiveXコントロールコンテナのコードを全部書かなくちゃいけないかと思っていたら、ATLのAtlAxWinInitというのが使えた。

これを呼び出すと、"AtlAxWin"という名前のウィンドウクラスを登録してくれて、そのウィンドウクラスでウィンドウを作成するとActiveXコントロールコンテナになってくれるというもの。しかも使うAPIは、AtlAxWinInitとAtlAxGetControlだけで済みそうなので、LoadLibraryでATL.DLLをロードして、GetProcAddressしてやればATLなし環境ではHTMLメールサポートができないだけでほかは問題なく動作させられそう。

適当にスーパークラス化して使うことにする。

ついでにはまったのは、CoInitializeExを自分で呼んでいたのだけど、フリースレッドで初期化すると上手く動作しない。ウィンドウはできるが、子ウィンドウにならずにポップアップウィンドウになる。多分、コンテキスト境界を越えてしまうので(ActiveXコントロールはアパートメントスレッドなので)、ウィンドウハンドルを渡すときにマーシャリングしなくちゃいけなくて、それはダメだよというのが原因かと。

CEだとアパートメントスレッドはサポートされていないし、どうなるんだろう?

IMultiLanguage(2|3)?

Windows(IEというべきか?)IMultiLanguage(2|3)?というインターフェイスがある。これは多言語サポート用のインターフェイスで、たとえば、ConvertStringなんていうメソッドがあったりする。

ところが、このConvertString系のメソッドがいけていなくて、書き込み先のバッファが足りないとエラーになる。まあエラーになるのはいいんだけれども、できたところまでのデータも返して欲しい。どこまで変換に成功したかはすでに返してくれるのだから、入るところまで変換して、何処まで書き込んだかを返してくれた方がありがたい。しかも、失敗した後で書き込み先のバッファの中を見ると変換後の文字列が入っているので、後は長さだけ入れてくれれば使えるのに。長さがわからないので結局変換後の文字列は捨てるしかない。

結局、一回呼び出して必要な長さを取得して動的にメモリを確保してから、もう一度呼び出さなくちゃいけないので無駄がおおい。ちゃんと書き込んだ長さを返してくれれば、固定バッファでループを回せばすむのに。

ついでに、IMultiLanguage2::ConvertStringInIStreamもいまいち。ConvertStringFromUnicodeなんかだと、ISO2022系のモード変換が入るコードの場合、Unicodeから変換するときには一気に文字列を渡してやらないと、そのたびにモード変換用のエスケープシーケンスが入る。ConvertStringInIStreamを使って入力ストリームから順次読み出せるようにしておけば、途中にエスケープシーケンスは入らないと思っていたんだけれども、入る。どうも、内部でバッファリングしているらしく、入力ストリームからある程度よんでバッファにたまるとConvertStringを呼び出して、その結果を出力ストリームに書き出しているらしく、その内部バッファのサイズのところでエスケープシーケンスが入ってしまう。んー、意味は一緒だけど止めて欲しいぞ。

IWebBrowser2でセキュリティゾーン

IWebBrowser2を使った場合に、ローカルファイルを表示する場合でもインターネットゾーンのセキュリティにしたい。で、どうやるのか調べたところ、SAMPLE: Secumgr.exe Overrides Security Manager for WebBrowser Host (Q246227)なんかが参考になりそうというわけでちょっとコードを見てみたところ、以下のような感じ。

1. IInternetSecurityManagerを実装する

2. IOleClientSiteを実装したオブジェクトにIServiceProviderを実装し、QueryServiceで、SID_SInternetSecurityManagerを要求されたら、1で実装したクラスのオブジェクトを返す

3. そうすると、IWebBrowser側がIInternetSecurityManagerのメソッドを呼び出してくるのでインターネットゾーンの設定(もっと厳しくてもいいかな?)を返す

ところで、上にも書いたようにIWebBrowser2をAtlAxWinInitを使ってホストする場合、IOleClientSiteを実装したオブジェクトはATL側にあるので手出しができない。やっぱりATLを普通に使ってやるしかないかと思って、ATLのソースを読んでみたところ、ATLのCAxHostWindowのIServiceProvider::QueryServiceの実装では、m_spServicesというメンバに外部IServiceProviderを登録できるようになっていてそっちに呼び出しを委譲してくれる。んでもって、このメンバはどうやって設定するかというと、IObjectWithSite::setSiteで設定したSiteオブジェクトに対してQueryInterfaceをしてIServiceProviderを要求し、それを保存している。

ということで、CreateWindowでコントロールとコンテナを作成した後で、AtlAxGetHostでコンテナのIUnknownを取得し、QueryInterfaceでIObjectWithSiteを取得して、自分で実装したIServiceProviderをセットしてしまえば、きっと呼び出してくれるに違いない。でも、コンテナ側なのになんでIObjectWithSiteを実装しているのかが謎。というところで今日は終了。

ちなみに、もしかするとIDocHostUIHandlerも実装しなくてはいけないかなと思って、やっぱり外部からATLを使うのは無理かと思ったけれど、ATLのソースを見てみたところ、CAxHostWindowのIDocHostUIHandlerの実装では、外部ハンドラに委譲するようにしていて、それはIAxWinHostWindow::SetExternalUIHandlerで設定できる。これもATL独自インターフェイスだが、COMインターフェイス経由で設定できるので外部から設定できそう。簡単なプロパティなら、IAxWinAmbientDispatch経由で設定できるみたい。

うまくいった

ためしに実装してみたら上手くいった。基本的に実装する必要があるのは、IInternetSecurityManager::ProcessUrlActionのみでよくて、後はINET_E_DEFAULT_ACTIONを返しておけばデフォルトが使われる。

ProcessUrlActionではとりあえず、URLPOLICY_DISALLOWを返すことにしておく。スクリプトもJavaもActiveXなどなども全て禁止になってしまうけど、問題ないよね、多分。

本日のツッコミ(全2件) [ツッコミを入れる]
# valp (2008-10-29 15:59)

IMultiLanguageのConvertString系ですが、MSDNライブラリには成功したら、大きさを指定した引数にsrc読み取り/dst書き込みした数が書き込まれる(そのため[in, out]になっている)ようです。バッファサイズが足りないときでもそうなってくれればいいのですがどうなんでしょう。それを試してみたという話がないかググっているのですが、見つかっていません。そのうち自分で試そうと思います。

# snak (2008-10-31 12:43)

上の話は、そういう場合(バッファのサイズが足りない場合)に長さが入っていなくて困るなぁという話だった気がします。もうだいぶ前の話なので、最近の実装は変わっているかもしれませんけど。


トップ 最新 次の日記(2002-01-31)»