Toggle FullScreen
More Info

VBAでInternetExplorerのインスタンスを捕まえる方法と、DocumentCompleteイベントの判別方法

VBAでInternetExplorerのインスタンスを捕まえる方法

すでに開いているInternetExplorerを制御したいときに、どうやってそのインスタンスを捕まえるかというのは、一つの問題です。
いろいろな方法がWeb上に公開されていますが、Microsoftの解説で、オフィシャルなものがありましたので、ご紹介します。

How to connect to a running instance of Internet Explorer(http://support.microsoft.com/kb/176792)

残念ながら邦訳見当たらず。解説に載っているサンプルは、フォーム上のオブジェクトに結果を表示するようになっているので、今回は手っ取り早くメッセージボックスに表示させるように変更しました。
サンプルを走らせるには、”Microsoft Internet Controls”(Shdocvw.dll)と”Microsoft HTML Object Library”(Mshtml.dll)を参照設定しておきます。

Sub test()
'How to connect to a running instance of Internet Explorer
'It is necessary to add a reference to
'"Microsoft Internet Controls" (Shdocvw.dll)
'"Microsoft HTML Object Library" (Mshtml.dll)

Dim SWs As New SHDocVw.ShellWindows
Dim IE As SHDocVw.InternetExplorer
Dim doc As Variant
  MsgBox SWs.Count
    For Each IE In SWs
      MsgBox "IE.LocationName:" & IE.LocationName
      If TypeOf doc Is HTMLDocument Then
        MsgBox "doc.Title" & doc.Title
      End If
    Next
End Sub

とってもスマート。変数の宣言の仕方、大変参考になりました。
私の力では、なかなかこういう風にできないんだよなー。


DocumentCompleteとNavigateComplete2を組み合わせた、読み込み完了の判断方法

VBAで、InternetExplorerを操作しているときに、InternetExplorerの画面遷移が完全に完了したかどうかを確認する必要があります。

以前の記事(VBAでInternet Explorerの画面遷移をイベントドリブン的に制御する)では、トップフレームのURIとDocumentCompleteイベントの引数の一つであるURLが一致したかどうかを確認していました。
あれから時間が経って、Microsoftがより適切な方法を解説しているのを見つけたので、ご紹介します。

How To Determine When a Page Is Done Loading in WebBrowser Control

機械翻訳がいまいちだったので、僭越ながらざっくり邦訳させてもらいました。

How To Determine When a Page Is Done Loading in WebBrowser Control(http://support.microsoft.com/kb/180366/en-us)

ページが WebBrowser コントロール内の読み込みを実行するときに確認する方法(http://support.microsoft.com/kb/180366/ja)

  • In the case of a page with no frames, DocumentComplete is fired once after everything is done.
  • In case of multiple frames, DocumentComplete gets fired multiple times. Not every frame fires this event, but each frame that fires a DownloadBegin event fires a corresponding DocumentComplete event.
  • The DocumentComplete event has a IDispatch* parameter, which is the IDispatch of the frame (shdocvw) for which DocumentComplete is fired.
  • The top-level frame fires the DocumentComplete in the end. So, to check if a page is done downloading, you need to check if the IDispatch* parameter is same as the IDispatch of the WebBrowser control.



This approach works when the WebBrowser control navigates to a page that changes the top-level frame. Say if the navigation occurs within a frame itself, then the final DocumentComplete that is fired is that of the frame and not the top-level frame. For example, consider the following scenario.


The WebBrowser control is hosting a frameset. Within one frame of the frameset, the user clicks on a link that opens a new page in the frame itself and keeps the rest of the frameset intact. The new page could contain multiple frames again. So, there will be multiple DocumentComplete notifications (one for each new frame). But, since the top-level frame has not changed, the final DocumentComplete would be that of the frame that has changed.


If you are interested in checking for the final document complete in this scenario, you could do the following:
Check if the IDispatch parameter of the DocumentComplete is the same as the IDispatch parameter of first NavigateComplete2 event. Since the first NavigateComplete2 is of the top-level frame and the last DocumentComplete is also of the top-level frame, doing a comparison in such a fashion will tell whether the page is done downloading.

—-ざっくりな邦訳ここから—-

  • ページにフレームが含まれていない場合、DocumentCompleteは全てが終わった後、1回だけ発火する。
  • フレームが複数含まれている場合、DocumentCompleteは複数回発火する。ただし、全てのフレームで発火するわけではなく、DownloadBeginイベントが発火したフレームについて、それぞれに対応したDocumentCompleteイベントが発火する。
  • DocumentCompleteイベントは、IDispatchパラメータを引数にとる。そのIDispatchパラメータはDocumentCompleteが発火したフレーム(shdocvw)のIDispatchである。
  • 最上位のフレームは、最後にDocumentCompleteを発火する。従って、ページのダウンロードが完了したかどうかを確かめるには、IDispatchパラメータがWebBrowserコントロールのIDispatchと同じであることを確認すればいい。



このアプローチがあてはまるのは、WebBrowserコントロールが、最上位のフレームが変わるページに移動する場合である。要するに、もしもページの遷移がある一つのフレームにしか起きなかったら、最後のDocumentCompleteは最上位のフレームではなく、その遷移があったフレームで起きることになる。たとえば次の例を見てみよう。


そのWebBrowserコントロールには、一つのフレームセットがある。そのフレームセットの中の、あるフレーム内のリンクをユーザーがクリックをすると、そのクリックによって、そのフレームの中で新しいページが開くが、他のフレームセットには変化がおこらない。新しいページには複数のフレームを含んでいるケースもあり、その際には、複数のDocumentCompleteイベントが発火する(新しく開いたフレームに対応して。)。しかし、最上位のフレームには変化がないので、最後のDocumentCompleteイベントは、その変化が起きたフレームになるだろう。


このようなケースで、最後のドキュメントの読み込みが完了したかどうかを知るには、次のようにすればよい。
DocumentCompleteのIDispatchパラメーターが、最初にNavigateCompleteイベントを発火したIDispatchパラメーターと同じであることを確かめる。なぜなら、最初のNavigateComplete2イベントは最上位のフレームで発火したものであり、また、最後のDocumentCompleteイベントもまた最上位のフレームで発火したものであるのだから、このようなやり方でチェックすることによって、ページのダウンロードが完了したかどうかを知ることができるのである。

—-ざっくりな邦訳ここまで—-

DocumentComplete Eventのオフィシャル解説

また、MSDNに載っているDocumentComplete Eventの解説(http://msdn.microsoft.com/en-us/library/aa768329(VS.85).aspx)には、次のように記載されています。

  • In pages with no frames, this event fires one time after loading is complete.
  • In pages where multiple frames are loaded, this event fires for each frame where the DownloadBegin event has fired.
  • This event pDisp parameter is the same as the IDispatch interface pointer of the frame in which this event fires.
  • In the loading process, the highest level frame, which is not necessarily the top-level frame, fires the final DocumentComplete event. At this time, the pDisp parameter is the same as the IDispatch interface pointer of the highest level frame.

—-ざっくりな邦訳ここから—-

  • ページの中にフレームがない場合、このイベントは読み込みが完了した後1回だけ発火する。
  • ページの中で複数のフレームを読み込んだ場合、DownloadBeginイベントが発火したフレームについて、このイベントは発火する。
  • このイベントのpDispパラメーターは、このイベントが発火したフレームのIDispatch interface pointerと同じである。
  • 読み込みの過程で、最上位レベルのフレーム(それは必ずしもトップレベルのフレームというわけではない。)が最後にDocumentCompleteを発火する。その際、pDispパラメーターはその最上位レベルのフレームのIDispatch interface pointerと同じになる。

—-ざっくりな邦訳ここまで—-

DownloadBeginイベントとNavigateComplete2イベントの解説には、それほど詳しいことは書かれていません。



NavigateComplete2 EventのURLを活用する

これらの資料から分かるのは、少なくともDownloadBeginとDocumentCompleteは1対1であるということ。
もうひとつは、最上位レベルのフレームが最初にNavigateComplete2イベントを発火し、最後にDocumentCompleteイベント発火するということ。

私の認識では、VBAではpDispパラメーターは使えないので、もう一つの引数であるURLで比較することになります。そもそもDownloadBeginイベントには引数がないし。


よって、VBAからInternetExplorerを制御して、クリックをエミュレートし、画面遷移(読み込み)の完了を確認するには、クリック後、最初に発火したNavigateComplete2イベントのURLを保持しておいて、その後発火するDocumentCompleteイベントのURLと突合させ、一致したら読み込み完了と判断する。
こうすると、画面遷移ごとのURLを調べておかなくても、汎用的に画面遷移の完了のタイミングを探知できるようになります。

ちなみに、DocumentCompleteイベントの解説(http://msdn.microsoft.com/en-us/library/aa768334(v=VS.85).aspx)

URL
String expression that evaluates to the URL, UNC file name, or PIDL that was navigated to. Note that this URL can be different from the URL that the browser was told to navigate to. One reason is that this URL is the canonicalized and qualified URL; for example, if an application specified a URL of “www.microsoft.com” in a call to the Navigate or Navigate2 method, the URL passed by Navigate2 will be “http://www.microsoft.com/”. Also, if the server has redirected the browser to a different URL, the redirected URL will be reflected here.

とあるように、実際にどんなURLが渡されるかは、やってみないと分からないという側面があるので、ひとつひとつ確認してみないといけない場合もでてくるんですよね。



だれかのお役に立てたかな?



Twitter from tokidokidokin
アーカイブ
Portfolio Categories
  • カテゴリーなし
Portfolio Tags