none
webBrowserコントロールを利用してInvokeMember("Click")で遷移するとDocumentCompletedが発生しない RRS feed

  • 質問

  • こんにちは。windowsフォームアプリケーションを作成しています。
    webBrowserコントロールを利用してページ遷移をした際に、DocumentCompletedが発生しない時があり困っています。
    同じURLなのですが、webBrowser.Navigate(url)で遷移すると発生し、InvokeMember("Click")で遷移すると発生しません。

    このような流れになっています。

    ■発生しないパターン
    ボタン1のクリックイベント内で、
    webBrowser.Navigate(ページAのurl)でページAを読み込む。
    →Navigating、Navigated、DocumentCompletedの順でイベントが発生します。

    次に、DocumentCompletedイベント内で、ページAのhtml内のページBのリンクをInvokeMember("Click")でクリックしてページBに遷移します。
    →Navigatingが発生せず、Navigatedが2回発生し、DocumentCompletedも発生しません。
    フォームに貼り付けたwebBrowserで確認すると、ページBが表示されています。

    DocumentCompletedで処理を続けたいので、DocumentCompletedが発生してほしいのですが、
    Navigatingが発生しないことも変ですし、Navigatedが2回発生していることも変ですよね。


    試しにwebBrowser.NavigateでページBに遷移しますと、イベントは正常に発生しました。
    ■発生するパターン
    ボタン1のクリックイベント内で、
    webBrowser.Navigate(ページAのurl)でページAを読み込む。
    →Navigating、Navigated、DocumentCompletedの順でイベントが発生します。

    ボタン2のクリックイベント内で、
    webBrowser.Navigate(ページBのurl)でページBを読み込む。
    →Navigating、Navigated、DocumentCompletedの順でイベントが発生します。
    フォームに貼り付けたwebBrowserで確認すると、ページBが表示されています。


    いくつか過去記事を読みまして、DocumentCompletedが発生しない現象はあるようなのですが、解決策がいまいち判明しませんでした。

    待つためのループとDoEventsは使用しておりません。

    osはwindows10で、vs community 2017 c#で実装しています。
    webBrowserのバージョンはレジストリでIE11を指定しています。

    何かわかる方がいらっしゃいましたら、宜しくお願いします。
    この辺りを探ってみれば?などでも構いませんので、宜しくお願いします。


    2017年9月15日 9:58

回答

  • MSDN ライブラリの WebBrowser コントロールの説明には以下のように書いてあります。(Current Version の日本語版は機械翻訳で意味不明なので英文を載せておきます)

    The following members let you navigate the control to a specific URL, move backward and forward through the navigation history list, and load the home page and search page of the current user:

    •Url
    •Navigate
    •GoBack
    •GoForward
    •GoHome
    •GoSearch

    If the navigation is unsuccessful, a page indicating the problem is displayed. Navigation with any of these members causes the Navigating, Navigated, and DocumentCompleted events to occur at different stages of navigation.

    ということは、上記のメンバーを使わないと Navigating, Navigated, DocumentCompleted イベントは発生しないということのように読めます。

    > InvokeMember("Click")でクリックしてページBに遷移します。

    InvokeMember("Click") では上記の条件に該当しないので DocumentCompleted イベントは発生しないということではないかと思われます。(自分が思うだけで確証はないです)

    2017年9月15日 11:15
  • 別の観点で、

    ページの読み込みにはネットワーク遅延などのコストがかかるだけでなく、ドキュメント全体の再描画が必要となり、ユーザーエクスペリエンス上も好ましくありません。

    この問題を解決するため、Googleなど著名なサイトでは、クリック時の動作として、「ページ遷移」ではなくJavaScriptによるページ書き換え及びアドレスバーのURL書き換えによりあたかもページ遷移したかのように見せかける手法がとられています。
    Webブラウザーでもこれを支援する機能としてHistory APIが用意されています。

    このようなサイトの場合、ページ遷移は発生していないためWebBrowserコントロールとしてもNavigating、Navigated、DocumentCompletedの各イベントがページ遷移と同等に発行されるわけではありません。

    このような例があるため、旧来のDocumentCompletedに依存するコードは時代にマッチしていません。

    2017年9月15日 20:45
  • > 目的は何であろうと関係ないと思います。通常でない手段でアクセスされてそれが業務妨害と相手が判断すれば相手は告訴するかもしれません。告訴するのは相手の自由でしょうから。(普通はそこまでしないかもしれませんが、責任逃れとかが絡むと厄介なことになるかも。慎重に対応されることをお勧めします)

    わかりました。ご忠告ありがとうございます。

    > > 今回はとりあえずNavigateで遷移させて、DocumentCompletedを発生させて進めることにしました。

    > それで解決ということであればこのスレッドはクローズしてください。
    > その際、できればもう少し具体的にどのような方法を取ったのか書いていただけると幸いです。

    元々は取得したhtmlの中身からaタグを取得して、クリックしたいリンクはわかっていますのでループで見て、該当するものをクリックしようとしていました。

    HtmlElement.InvokeMember("Click"); です。

    これを、aタグからhrefにセットされているurlを取得して、
    webBrowser.Navigate(url); としました。


    > 厳しいですが、質問者さんのスキルの問題であり、私からすればWebBrowserコントロールは自由度が高く痒い所に手が届く印象です。

    5年ぶりの開発のため、スキル不足は自覚してはいるのです。調べてもわからなかったので、こちらで質問しようとした次第です。。。
    2017年9月20日 2:51

すべての返信

  • MSDN ライブラリの WebBrowser コントロールの説明には以下のように書いてあります。(Current Version の日本語版は機械翻訳で意味不明なので英文を載せておきます)

    The following members let you navigate the control to a specific URL, move backward and forward through the navigation history list, and load the home page and search page of the current user:

    •Url
    •Navigate
    •GoBack
    •GoForward
    •GoHome
    •GoSearch

    If the navigation is unsuccessful, a page indicating the problem is displayed. Navigation with any of these members causes the Navigating, Navigated, and DocumentCompleted events to occur at different stages of navigation.

    ということは、上記のメンバーを使わないと Navigating, Navigated, DocumentCompleted イベントは発生しないということのように読めます。

    > InvokeMember("Click")でクリックしてページBに遷移します。

    InvokeMember("Click") では上記の条件に該当しないので DocumentCompleted イベントは発生しないということではないかと思われます。(自分が思うだけで確証はないです)

    2017年9月15日 11:15
  • SurferOnWwwさん、ご返信ありがとうございます。
    英語のヘルプまで、すみません。

    > InvokeMember("Click") では上記の条件に該当しないので DocumentCompleted イベントは発生しないということではないかと思われます。

    本当ですね・・・そのように読めます。

    しかしこれではWebBrowserはけっこう制限のある、扱いにくいコントロールという印象です(^_^;)


    2017年9月15日 13:11
  • > しかしこれではWebBrowserはけっこう制限のある、扱いにくいコントロールという印象です(^_^;)

    それはちょっと違うのではないでしょうか・・・

    WebBrowser コントロールは shdocvw.dll のマネージラッパーで、.NET アプリで shdocvw.dll を容易に操作できるようにした(=扱いやすくした)ということのようですから、制限があるのは当たり前だと思うのですが。

    WebBrowser コントロールでは提供されてないイベントを利用したい場合は、WebBrowser を拡張するか、Aximp.exe を使ってラッパーコントロールを生成してそれを使うという話になると思います。(それで質問者さんのやりたいことができるかどうかは分かりませんが)

    Extended .NET 2.0 WebBrowser Control
    https://www.codeproject.com/Articles/13598/Extended-NET-2-0-WebBrowser-Control

    SHDocVw.dll と AxSHDocVw.dll の作り方と使い方
    http://surferonwww.info/BlogEngine/post/2012/06/23/how-to-produce-and-use-shdocvw-and-axshdocvw.aspx

    2017年9月15日 14:38
  • 別の観点で、

    ページの読み込みにはネットワーク遅延などのコストがかかるだけでなく、ドキュメント全体の再描画が必要となり、ユーザーエクスペリエンス上も好ましくありません。

    この問題を解決するため、Googleなど著名なサイトでは、クリック時の動作として、「ページ遷移」ではなくJavaScriptによるページ書き換え及びアドレスバーのURL書き換えによりあたかもページ遷移したかのように見せかける手法がとられています。
    Webブラウザーでもこれを支援する機能としてHistory APIが用意されています。

    このようなサイトの場合、ページ遷移は発生していないためWebBrowserコントロールとしてもNavigating、Navigated、DocumentCompletedの各イベントがページ遷移と同等に発行されるわけではありません。

    このような例があるため、旧来のDocumentCompletedに依存するコードは時代にマッチしていません。

    2017年9月15日 20:45
  • > WebBrowser コントロールは shdocvw.dll のマネージラッパーで、.NET アプリで shdocvw.dll を容易に操作できるようにした(=扱いやすくした)ということのようですから、制限があるのは当たり前だと思うのですが。

    なるほど、プログラムからさわれる様になっているだけ、ありがたいということですね。
    つい、他のコントロールのようにもっと自由度が高いものが当たり前だと思ってしまっていました。すみません(^_^;)

    > WebBrowser コントロールでは提供されてないイベントを利用したい場合は、WebBrowser を拡張するか、Aximp.exe を使ってラッパーコントロールを生成してそれを使うという話になると思います。

    リンクありがとうございます。
    難しそうですが、参考にしてみます。


    佐祐理さん、ご返信ありがとうございます。
    別の観点で大変勉強になりました。

    > ページの読み込みにはネットワーク遅延などのコストがかかるだけでなく、ドキュメント全体の再描画が必要となり、ユーザーエクスペリエンス上も好ましくありません。

    はい、確かに画面の読み込みなどで遅くならないようにしたいです。

    > この問題を解決するため、Googleなど著名なサイトでは、クリック時の動作として、「ページ遷移」ではなくJavaScriptによるページ書き換え及びアドレスバーのURL書き換えによりあたかもページ遷移したかのように見せかける手法がとられています。
    Webブラウザーでもこれを支援する機能としてHistory APIが用意されています。
    > このようなサイトの場合、ページ遷移は発生していないためWebBrowserコントロールとしてもNavigating、Navigated、DocumentCompletedの各イベントがページ遷移と同等に発行されるわけではありません。

    そうなのですか・・・では、今回もそういうサイトなのかもしれないですね。
    History APIを調べてみたところ、アクセスしていないページでも履歴にURLを追加してアクセスしたことにしてそのURLへ遷移する、こういった使い方をすれば良いということでしょうか?

    > このような例があるため、旧来のDocumentCompletedに依存するコードは時代にマッチしていません。

    なるほど、そうでしたか・・・これも知りませんでした。ありがとうございます。
    ということはHistory APIで遷移するコードを書くのは、DocumentCompletedではなく、どのイベントが適しているのでしょうか?
    例えばですがボタンクリックイベントやタイマーイベントなどでもOKということでしょうか?

    しかし、History APIはJavaScriptで書くもののようなので、そもそもwindowsフォームアプリケーションのイベントで書けるものではないようにも思えます。。

    ざっくりとでも構いませんので、大変お手数ですがコードの書き方のヒントをいただけないでしょうか?

    今回の作り方は、過去の掲示板などを見て、ボタンクリックイベントで最初のwebBrowser.Navigate(url)を行い、遷移後の続きはDocumentCompletedイベント内で処理をcase文で分けて書くのがセオリーなのかな、と思いまして、そのようにしていました。

    宜しくお願いします。
    2017年9月16日 2:50
  • > なるほど、プログラムからさわれる様になっているだけ、ありがたいということですね。

    たぶん思い違いしてますね。

    shdocvw.dll を「プログラムからさわれる」のは WebBrowser を使わなくても可能です。

    「プログラムからさわれる」は「.NET の Windows Forms アプリ用に WebBrowser クラスが提供されていて、shdocvw.dll のラッパーを自力で作る必要がない」と言うべきだと思います。

    佐祐理さんのレスの話は WebBrowser では対応できないケースを言われていると理解しています。もし違ったらすみません。その場合は説明をお願いします。> 佐祐理さん

    2017年9月16日 4:59
  • > たぶん思い違いしてますね。
    > shdocvw.dll を「プログラムからさわれる」のは WebBrowser を使わなくても可能です。
    > 「プログラムからさわれる」は「.NET の Windows Forms アプリ用に WebBrowser クラスが提供されていて、shdocvw.dll のラッパーを自力で作る必要がない」と言うべきだと思います。

    わかりました。本来は自力でラッパーを作る必要があるんですね。やはり有り難いことです。

    > 佐祐理さんのレスの話は WebBrowser では対応できないケースを言われていると理解しています。

    あ、そういうことかもしれないのですね。
    もしそうなら、どんな感じの作りになるのでしょうか?

    最初はWebBrowserではなく、HttpWebRequestを利用して通信をするようにしていました。
    ただ、その方法は断念しました。サーバーからの戻りのhtml内のjavascriptを、実行させる必要があり、できませんでした。
    WebBrowserですと、javascriptを自動で実行して、その結果を反映してくれましたので。
    javascriptを実行して、その結果をブラウザと同じように受け取る方法があれば問題ないのですが、見つけることができませんでした。

    そもそもこういう作りも違うのもしれませんが、最初はこのように考えていました。
    2017年9月19日 2:27
  • >> 佐祐理さんのレスの話は WebBrowser では対応できないケースを言われていると理解しています。

    > あ、そういうことかもしれないのですね。

    佐祐理さんは Google 検索を例にとって話をされていると理解していますが、そうであれば「そういうこと」(WebBrowser では対応できないケース)です。

    #どのようなことをしても 100% 絶対に不可能とまで言い切る自信はないですが、WebBrowser でクローラーを作って情報収集などを行うのは、相手が Google ですと現実的には無理だろうぐらいは言えます。

    > もしそうなら、どんな感じの作りになるのでしょうか?

    Google 検索の場合でしたら、Google から API が提供されているそうなので、WebBrowser の利用は諦めて、提供されている API を利用するという話になると思います。

    > そもそもこういう作りも違うのもしれませんが、最初はこのように考えていました。

    HttpWebRequest では JavaScript が動かないので WebBrowser を使うことにしたのは正しい判断だと思います。

    ただ、Google のようにクローラーに WebBrowser を使用するのが適してないサイトも多々あるようです。

    でも、今回の問題は、多分そこまでの話ではなくて、「InvokeMember("Click")で遷移するとDocumentCompletedが発生しない」という問題に対応できればいいのですよね。

    であれば、私のレスに書きましたように、WebBrowser コントロールでは提供されてないイベントを利用するために、WebBrowser を拡張するか、Aximp.exe を使ってラッパーコントロールを生成してそれを使うことを検討してはいかがですか。

    それで質問者さんのやりたいことが実現できるかどうかは分かりませんが、検討するだけしてみてはいかがですか?

    ところで、WebBrowser でアクセスする際、アクセス先のサイトの所有者・管理者には WebBrowser などで自動アクセス(ですよね?)することの許可は得ているのでしょうか?

    クローラーを作ってアクセスしたら業務妨害で逮捕されたという話もありますので、気を付けてください。(その事例は関係者の責任のなすりあいが絡んだ特殊な話のようにも見えますが、そういう事例は確かにあります)


    • 編集済み SurferOnWww 2017年9月19日 3:58 一部追記
    2017年9月19日 3:54
  • SurferOnWwwさん、何度もありがとうございます。

    > HttpWebRequest では JavaScript が動かないので WebBrowser を使うことにしたのは正しい判断だと思います。
    > ただ、Google のようにクローラーに WebBrowser を使用するのが適してないサイトも多々あるようです。

    すみません、最初に言えば良かったのですが、今回はクローラーの開発ではなくて、ネットショップからの購入(仕入れ)です。
    毎回同じ情報の入力などが面倒なため、ボタンクリック一発で購入を自動化しようとしていました。
    あとは速度的に手動では遅く、買うことができない時があるため、速度アップも兼ねて、です。

    > でも、今回の問題は、多分そこまでの話ではなくて、「InvokeMember("Click")で遷移するとDocumentCompletedが発生しない」という問題に対応できればいいのですよね。
    > であれば、私のレスに書きましたように、WebBrowser コントロールでは提供されてないイベントを利用するために、WebBrowser を拡張するか、Aximp.exe を使ってラッパーコントロールを生成してそれを使うことを検討してはいかがですか。
    > それで質問者さんのやりたいことが実現できるかどうかは分かりませんが、検討するだけしてみてはいかがですか?

    ヒントをいただき、ありがとうございます。
    今回はとりあえずNavigateで遷移させて、DocumentCompletedを発生させて進めることにしました。
    Navigateでセッションidが引き継げるのかが懸念点でしたが、先に進めましたので大丈夫そうです。

    > ところで、WebBrowser でアクセスする際、アクセス先のサイトの所有者・管理者には WebBrowser などで自動アクセス(ですよね?)することの許可は得ているのでしょうか?クローラーを作ってアクセスしたら業務妨害で逮捕されたという話もありますので、気を付けてください。(その事例は関係者の責任のなすりあいが絡んだ特殊な話のようにも見えますが、そういう事例は確かにあります)

    それは怖い話ですね・・・
    確かに無駄にクローラーから何度もアクセスされると迷惑ですしね。
    2017年9月19日 5:23
  • > 今回はクローラーの開発ではなくて、ネットショップからの購入(仕入れ)です。

    余計なお世話かもしれませんが・・・

    目的は何であろうと関係ないと思います。通常でない手段でアクセスされてそれが業務妨害と相手が判断すれば相手は告訴するかもしれません。告訴するのは相手の自由でしょうから。(普通はそこまでしないかもしれませんが、責任逃れとかが絡むと厄介なことになるかも。慎重に対応されることをお勧めします)

    > 今回はとりあえずNavigateで遷移させて、DocumentCompletedを発生させて進めることにしました。

    それで解決ということであればこのスレッドはクローズしてください。

    その際、できればもう少し具体的にどのような方法を取ったのか書いていただけると幸いです。

    2017年9月19日 5:48
  • しかしこれではWebBrowserはけっこう制限のある、扱いにくいコントロールという印象です(^_^;)

    厳しいですが、質問者さんのスキルの問題であり、私からすればWebBrowserコントロールは自由度が高く痒い所に手が届く印象です。

    自動制御する際、HTML / JavaScriptに精通する必要があります。行いたい操作をJavaScriptで表現することができたら、あとはそれをVBに移植するだけとなります。例えばVBのDocumentCompletedイベントにとらわれているから制御できないだけであり、JavaScriptにはそもそもそのようなイベントは存在しません。

    2017年9月19日 13:31
  • > 目的は何であろうと関係ないと思います。通常でない手段でアクセスされてそれが業務妨害と相手が判断すれば相手は告訴するかもしれません。告訴するのは相手の自由でしょうから。(普通はそこまでしないかもしれませんが、責任逃れとかが絡むと厄介なことになるかも。慎重に対応されることをお勧めします)

    わかりました。ご忠告ありがとうございます。

    > > 今回はとりあえずNavigateで遷移させて、DocumentCompletedを発生させて進めることにしました。

    > それで解決ということであればこのスレッドはクローズしてください。
    > その際、できればもう少し具体的にどのような方法を取ったのか書いていただけると幸いです。

    元々は取得したhtmlの中身からaタグを取得して、クリックしたいリンクはわかっていますのでループで見て、該当するものをクリックしようとしていました。

    HtmlElement.InvokeMember("Click"); です。

    これを、aタグからhrefにセットされているurlを取得して、
    webBrowser.Navigate(url); としました。


    > 厳しいですが、質問者さんのスキルの問題であり、私からすればWebBrowserコントロールは自由度が高く痒い所に手が届く印象です。

    5年ぶりの開発のため、スキル不足は自覚してはいるのです。調べてもわからなかったので、こちらで質問しようとした次第です。。。
    2017年9月20日 2:51
  • > これを、aタグからhrefにセットされているurlを取得して、
    > webBrowser.Navigate(url); としました。

    情報の提供をありがとうございました。

    WebBrowser の拡張等の面倒なことをする必要のないスマートな解決策だと思います。

    2017年9月20日 3:08