WebBrowserのNavigate処理を途中で完全に止めるには

解決


yoshida  2011-03-29 03:34:47  No: 40332

いつもお世話になっております。1点質問をお願いします。

【症状】

今、TWebBrowserを利用したタブブラウザを作っています。
そのブラウザでは、あるボタンをクリックすると、

WebBrowser.Free

という形でコンポーネントを削除するようになっているの
ですが、このWebBrowserがページを読み込んでいる最中(Navigate中)
だと、WebBrowser.Freeの後、InternetExplorerが立ち上がり、
そこでページを表示するようになってしまいます。

【やりたいこと】

WebBrowser.Freeとしたら、一切の処理をそこで停止させて、
WebBrowserを閉じたい。
(IEが立ち上がって、そちらで処理が継続されるような形には
したくない。)

【これまでやったこと】

1  WebBrowser.Freeの前に、WebBrowser.Stopを入れました。
  ですが、IEが立ち上がる現象は相変わらず発生しています。

  例えばこことかを開いたとき、その症状がでます。
  http://syarecowa.moo.jp/

2  WebBrowser.Freeの前に、

  WebBrowser.Navigate('about:blank')
  while WebBrowser.ReadyState <> READYSTATE_COMPLETE do
      Application.ProcessMessages;

  をいれこみ、
  強制的に処理を中断させようとしました。
  これは、1よりは多少効果が上がったのですが、重いページ
  を開いたりすると、やはり1と同じような現象が発生しました。

【環境】

環境は、Win XP Home + IE8 + Delphi 6 personalです。


monaa  2011-03-30 18:58:20  No: 40333

再現しません。
Windows7 + IE8 + Delphi2009
//http://www.wwlnk.com/boheme/delphi/tips/tec1620.htm
procedure TForm1.Button1Click(Sender: TObject);
var
  TabSheet : TTabSheet;
  WebBrowser : TWebBrowser;
begin
  TabSheet  :=  TTabSheet.Create(Self);
  TabSheet.Parent       :=  PageControl1;
  TabSheet.PageControl  :=  PageControl1;
  TabSheet.Caption      :=  'ブラウザ';
  WebBrowser  :=  TWebBrowser.Create(Self);
  TOleControl(WebBrowser).Parent  :=  TabSheet;
  WebBrowser.Align      :=  alClient;
  WebBrowser.Navigate ('http://syarecowa.moo.jp/');
  WebBrowser.Free;
end;


yoshida  2011-03-31 03:54:48  No: 40334

わざわざ検証していただき、ありがとうございます。
ですが、Navigateした直後、すぐさまFreeするのであれば、確かに
現象は起きません。問題となるのは、Navigateしてしばらくして、
ページが一部読み込まれている最中に起こるのです。

  Browser.Freeをするタイミングなのですが、

  WebBrowser.Free;

の部分だけ切り離して、別のボタンを押したときなどに実行してみると、
再現するのがわかっていただけるのではないかと思います。
(このボタンをタイミングとしては、このURLを開いたときに出てくる
「スクリプトエラー」のダイアログのボタンをクリックして閉じた直後
あたりです。このときにFreeすると、IEが立ち上がると思います。)


やっぱり  2011-03-31 04:26:21  No: 40335

>(このボタンをタイミングとしては、このURLを開いたときに出てくる
>「スクリプトエラー」のダイアログのボタンをクリックして閉じた直後
>あたりです。このときにFreeすると、IEが立ち上がると思います。)

そのようにしても、IEが立ち上がる現象は再現されないけど。


KHE00221  2011-03-31 15:02:16  No: 40336

「スクリプトエラー」が出るってことは
読み込みは終了していないか?


monaa  2011-03-31 21:45:21  No: 40337

FreeだけをButton2Clickに移動させて20回ほど試しましたが再現しません。
5%以下の発生率では確認のしようがありません。
yoshidaさんの環境では上記コードでどの程度の頻度で発生するのでしょうか?
なんか他のコードが挟まってる気がしてしょうがないです。


yoshida  2011-04-01 05:59:40  No: 40338

みなさん、いろいろな書き込み、ありがとうございます。
  今、実行していましたら、新たな発見がありました。
確かに、単純にFreeするだけなら問題はありませんでした。

次のコードのうち、OnNavigateComplete2と、OnDocumentComplete
の部分の、「Navigateが終わったら、WebBrowserにフォーカスを
当てる」という処理の部分に問題があったようです。おそらく、
このコードでしたら問題が再現するはずです。(少なくとも、今
当方では再現しました。)

  Freeの前に各イベントを無効にしていたので、原因を探る際、
うっかり見落としていました。みなさまにお手数をおかけしてしまい、
申し訳ありませんでした。

  ただ、「Navigateが終わったら、WebBrowserにフォーカスを当てる」
という処理は、マウスのホイールを利用して快適にブラウジングを行う
ためにどうしても行いたい処理なので、これを実現しつつ、IEを
立ち上がらないようにしたいのです。

  当初の質問と、微妙に変わってしまいましたが、ここ最近ずっと悩んで
いる問題で、ぜひ解決していきたいと考えておりますので、どうぞよろしく
お願いいたします。

---------------------------------------------------------

// Usesには、ActiveXを手動で追加する必要があります。

procedure TForm1.Button1Click(Sender: TObject);
begin
  WebBrowser.Navigate('http://syarecowa.moo.jp/');
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  // Button1を押し、Navigateされたページで出たスクリプトエラー
  // ダイアログを消した後に押す。
  WebBrowser.Stop;
  WebBrowser.OnNavigateComplete2 := nil;
  WebBrowser.OnDocumentComplete := nil;
  WebBrowser.Free;
end;

procedure TForm1.WebBrowserNavigateComplete2(Sender: TObject;
  const pDisp: IDispatch; var URL: OleVariant);
var
  Rect: TRect;
begin
  if WebBrowser.Document <> nil then begin
    Windows.GetClientRect(WebBrowser.Handle, Rect);
    (WebBrowser.Application as IOleObject).DoVerb(OLEIVERB_UIACTIVATE,
      nil, WebBrowser, 0, WebBrowser.Handle, Rect);
  end;
end;

procedure TForm1.WebBrowserDocumentComplete(Sender: TObject;
  const pDisp: IDispatch; var URL: OleVariant);
var
  Rect: TRect;
begin
  if WebBrowser.Document <> nil then begin
    Windows.GetClientRect(WebBrowser.Handle, Rect);
    (WebBrowser.Application as IOleObject).DoVerb(OLEIVERB_UIACTIVATE,
      nil, WebBrowser, 0, WebBrowser.Handle, Rect);
  end;
end;

initialization
  OleInitialize(nil);
finalization
  OleUninitialize;


yoshida  2011-04-01 06:07:37  No: 40339

補足させてください。

上記コードは、

Button1: TButton
Button2: TButton
WebBrowser: TWebBrowser

の3つのコントロールのみ、新規プロジェクトのフォームに
ドロップして配置し、イベントを静的に割り当てたものです。
コントロールの動的生成等は行っていませんが、問題を単純化する
ため、あえて上記のような形とさせていただいております。


確かに  2011-04-01 07:37:55  No: 40340

このWebBrowserにフォーカスを当てるためのコードがあると、
「Freeした時にIEが立ち上がる」という現象が起きることが確認できた。

  if WebBrowser.Document <> nil then begin
    Windows.GetClientRect(WebBrowser.Handle, Rect);
    (WebBrowser.Application as IOleObject).DoVerb(OLEIVERB_UIACTIVATE,
      nil, WebBrowser, 0, WebBrowser.Handle, Rect);
  end;

でも、このコードがどうしても行いたい処理であったとしても、
これを OnNavigateComplete2と OnDocumentCompleteの両方に
入れる必要はなくて、OnDocumentCompleteの時だけでいいと思うけど。

「Navigateが終わったら、WebBrowserにフォーカスを当てる」じゃなくて、
「Document読込みが完了したら、WebBrowserにフォーカスを当てる」だね。


monaa  2011-04-01 23:08:36  No: 40341

再現しました。
なんかジレンマ的なプログラムですね。
Free中にDocumentを呼び出すと親の終了時に子を作るみたいな動作になってるようです。(確信はないです)
なのでDocumentのコードを別のものに置き換えてください。
今回だとコメントアウトだけでOKです。

procedure TForm1.Button1Click(Sender: TObject);
begin
  WebBrowser.Navigate('http://syarecowa.moo.jp/');
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  //WebBrowser.Stop; //削除してもOK
  //WebBrowser.OnNavigateComplete2 := nil; //削除してもOK
  //WebBrowser.OnDocumentComplete := nil;  //削除してもOK
  WebBrowser.Free;
end;

procedure TForm1.WebBrowserNavigateComplete2(ASender: TObject;
      const pDisp: IDispatch; var URL: OleVariant);
var
  Rect: TRect;
begin
  //if WebBrowser.Document <> nil then begin           //削除
    Windows.GetClientRect(WebBrowser.Handle, Rect);
    (WebBrowser.Application as IOleObject).DoVerb(OLEIVERB_UIACTIVATE,
      nil, WebBrowser, 0, WebBrowser.Handle, Rect);
  //end;
end;

procedure TForm1.WebBrowserDocumentComplete(ASender: TObject;
  const pDisp: IDispatch; var URL: OleVariant);
var
  Rect: TRect;
begin
  //if WebBrowser.Document <> nil then begin            //削除
    Windows.GetClientRect(WebBrowser.Handle, Rect);
    (WebBrowser.Application as IOleObject).DoVerb(OLEIVERB_UIACTIVATE,
      nil, WebBrowser, 0, WebBrowser.Handle, Rect);
  //end;
end;


確か?  2011-04-02 00:33:39  No: 40342

>でも、このコードがどうしても行いたい処理であったとしても、
>これを OnNavigateComplete2と OnDocumentCompleteの両方に
>入れる必要はなくて、OnDocumentCompleteの時だけでいいと思うけど。

そのイベントはかなりの間隔があいて発生することがあるので、

>マウスのホイールを利用して快適にブラウジングを行うためにどうしても行いたい

という要求を満たすためには両方に入っていてもおかしくはない。
どちらかだけにするとしても、それはOnNavigateComplete2の方だろう。


yoshida  2011-04-03 01:12:51  No: 40343

ご回答ありがとうございます。

今、monaa様のご提示いただいたコードを試して、しばらく操作していました。
その結果、きちんと望みどおりの動作になりました。長期間にわたり抱えて
いた問題でしたので、本当にうれしい状況です。どうもありがとうございました。

今、1時間ほど実使用した感じでは、全く問題ない様子です。もうしばらく、
プログラムを実使用してみて、問題がなさそうでしたら、解決として閉じさせて
いただきたいと思います。

ご回答をいただけて、本当に助かりました。どうもありがとうございました。

-----------------------------------------------------------------

なお、同じコードを2つのイベントに設定しているのは、次のような理由です。

・快適に見るのであれば、少しでも早くフォーカスを設定したい。
  →  OnNavigateComplete2
・画像ファイル1枚からなるようなページの場合だと、上記イベントだけでは
  フォーカスが有効にならない場合がある。
  →  保険として、OnDocumentComplete


KHE00221  2011-04-03 01:17:05  No: 40344

procedure TForm3.Button1Click(Sender: TObject);
begin
    WebBrowser1.Navigate('http://syarecowa.moo.jp/');
    Timer1.Interval := 2000;//1000でも2000でも
    Timer1.Enabled :=True;
end;

procedure TForm3.Timer1Timer(Sender: TObject);
begin
    WebBrowser1.Free;
end;

これで再現できる

WebBrowser.Navigate ('http://syarecowa.moo.jp/');
をやって完全に表示される前に Free すると
IEで表示されるみたい。

フレームが多いやつだと IE が複数表示されるみたい。


yoshida  2011-04-11 05:04:43  No: 40345

お世話になります。
  あれから、十分時間を取ってチェックしました。いろいろ無茶な使い方
や、想定しない動作なども思いつく限り、相当繰り返してやってみましたが、
この部分を変更したことによるエラーは一切出ませんでした。非常に良好な
動きを確認できました。そのため、本件は解決とさせていただきます。

  直接のご回答をいただいたmonaa様を初め、参考となる情報をいろいろと
書き込んでくださった方々、ご協力、ありがとうございました。こちらでの
「バグが再現しない」といったご指摘がなければ、きっと今頃も同じことで
悩んでいたことと思います。本当にどうもありがとうございました。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加