まぁ、仕事でやってたんですけどね?
IEを取得する方法は様々あるんですけれど、
HWNDから取得する方法はそれほど多くはありません。
まずFindWindow等によってIEFrameの様な物を探すのが一般的なブラウザの取得方法だと思います。
しかし、これだけではWindowハンドルを取得できるだけで何も出来ません。
何かするのであれば、IWebBrowserを取得する必要があります。
WebBrowserもしくはIWebBrowser2インターフェイスはWebBrowserコントロールを使ってプログラムを組んだことがある人なら判ると思いますが、IEもWebBrowserコントロールをそのまんま利用して作られているというか?
IEの一部を公開しているに過ぎないので
IE自体も上手いことIWebBrowser2インターフェイスを取得してあげれば簡単にいじくり回せるわけです。
しかし、実はIWebBrowser2を取得するのはとても大変です。
と言うか、大変でした。
判ってしまえば対したことではありません。関数などを使わなくても総コード数は100行も行かないでしょう。とくにC#を使ったので、コードはかなり短めです。
ハッキリ言いましょう、こう言った作業はC++の方が楽なのです。
c#で行うので大変になりました。
今後の同胞のために重要な部分について書いていきます。
まず、IWebBrowser2を直接取得する手段はありません。
かならずIHTMLDocumentを経由して取得します。
IHTMLDocumentの取得方法はそれほど面倒ではありません。
でもソースの量から言えば、IHTMLDocumentからIWebBrowser2を取得するよりもHWNDからIHTMLDocumentを取得する方が多いです。
要点をまとめると
Internet Explorer_Serverを見つけてWM_HTML_GETOBJECTを送信しObjectFromLresultでIHTMLDocumentを取得します。
まぁ、一般的にはIHTMLDocument2を取得すると思いますが?
大した差はありません。
ではそれぞれの解説を・・・
・Internet Explorer_Serverを探す方法
全てのウインドウを探すわけですからFindWindowExがお勧めです。
これにはクラス名を指定することが出来ますが、"Internet Explorer_Server"はかなり深い子ウインドウなので、FindWindowExで直接見つける方法はありません。
なので、再帰処理をして、子ウィンドウの子ウィンドウをと、どんどん深く潜って探す必要があります。
そして、見つけたウインドウがInternet Explorer_ServerかどうかをGetClassNameを使って全部調べる必要があります。
・WM_HTML_GETOBJECTを送信する。
WM_HTML_GETOBJECTを送信するのは、普通のSendMessageではなくSendMessageTimeoutを使うのが一般的のようです。別のプロセスであるIEはフォアグラウンド処理されていないので、反応がちょっと遅いです。
場合によってはFreezeしているかも知れません。
なので、タイムアウト機能付きのを使う様です。
・IHTMLDocument2を取得する。
SendMessageTimeoutので取得するlpdwResultの値は、すでにIHTMLDocument2等のインターフェイスらしいです。
なのでこれをオブジェクトに変換をします。
ObjectFromLresultを利用します。
これで、IHTMLDocument2を取得する所までが完了です。
ここまでも、C#にはない機能を使うために幾つかAPIを追加する必要がありますが、ごく一般的で、これを行わなくても追加してあるかも知れません。
ですが、ここから先は、あたしが今まで使ったこともないものを追加していきます。
まず、インターフェイスを切り替えIServiceProviderにします。
QueryService(QueryInterfaceではない)を使ってブラウザ本体を取得したら
IWebBrowser2を取得して終了です。
・定義の作成
まず、IServiceProviderを定義します。
c#にはIServiceProviderが元々ありますが、全然違うものです。
[ComImport]
[Guid("6d5140c1-7436-11ce-8034-00aa006009fa")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IServiceProvider
{
void QueryService(ref Guid guidService, ref Guid riid,
[MarshalAs(UnmanagedType.Interface)] out object ppvObject);
}
他にもメソッドがあるかもしれませんが、QueryServiceしか使わないので、定義は要りません。
・IServiceProviderへの切り替え
IHTMLDocument2からIServiceProviderに変更するためにIDispatchを取得します。
GetIDispatchForObjectでOKです。
戻り値をQueryInterfaceに掛けてIServiceProviderを取得します。
このままではただのIntPtrなのでGetTypedObjectForIUnknownを掛けてIServiceProviderにします。
・ブラウザ本体を取得
作成したIServiceProviderでQueryServiceを実行し、SID_STopLevelBrowserのIServiceProviderを取得します。
この工程がどの程度必要なのかは判りません。
これにQueryServiceを実行しSID_SWebBrowserAppのIWebBrowser2を取得します。
これで、IWebBrowser2の取得が完了しました。なんならDocumentを取得して一致しているか確認しても良いでしょう。
今回のキモは、QueryServiceがC#では提供されていないと言うことです。
GetIDispatchForObjectでIDispatchが取得手出来て、QueryInterfaceが実行できるのにね!
|