none
WebBrowser1のGoBack処理から戻れない

    質問

  • VisualC++ 2010 Express(C++/CLI)にて開発しています。
    過去に作成したライブラリを使っているためにManaged+Native環境でアプリケーションを作っている状況です。

    アプリケーションの主要な機能は、画面上のボタンでStartを押すと、処理用のThreadを起動して、指定のWebページを自動巡回して、情報を収集して画面表示する機能を実装していますが、WebBrowserのGoBack処理から処理が戻らず困っています。
    WebBrowserは、標準のコントロールを、レジスタを書き換えてIE11状態で使用しています。

    今回お教えいただきたい事象が発生する手順は、以下です。

    ①画面のStartボタンをクリックする(情報収集用のThreadが起動)

    ②そのThread内で以下の処理が呼び出される
    _OutputDebugString("this->webBrowser1->GoBack() コールします");
    if( this->webBrowser1->CanGoBack == true ){
     this->webBrowser1->GoBack();
    }
    _OutputDebugString("this->webBrowser1->GoBack() 処理から戻りました");
    ※_OutputDebugString()は、自作のデバッグ情報を出力する関数

    ③↑の処理は、通常時は正常に処理も戻り期待動作もしている

    ④メインWindowの右上の×ボタンを、GoBack()処理が呼ばれてWebページが読み込まれるまでの間に押す

    ⑤↑のタイミングで×ボタンを押すと、GoBack()処理から処理が戻らず該当Threadが終了できなくなります。
    その際のイミディエイトウィンドウ内のログは、以下となります。
    this->webBrowser1->GoBack() コールします
    ~Form1

    ⑥この状態で終了処理を継続した場合、RCWの不正開放エラーになります。
    そりゃぁ、処理中の状態で開放すれば、出るのは当たり前ですが。

    ×ボタンを押すタイミングを④のタイミング以外にするとエラー無くThread終了処理等が行えます。

    上記のように、特定タイミングで×ボタンを押すと、GoBack処理から戻れない為に対処方法に困っています。
    ×ボタン押された後に、~Form1()処理までの間にGoBack処理の戻りを待てれば解決出来そうなのですが。
    何か対応方法ご存知の方は、お教えください。

    2017年11月12日 5:26

回答

すべての返信

  • コードの全貌をきっちりと把握して検証したわけではないので、的確な回答かどうかわかりませんが、フォームの×ボタンを押すと FormClosing イベントが発生します。そこでスレッドを待つようにしてはどうでしょうか?
    2017年11月12日 5:41
  • 早速ありがとうございます。
    トライしてみましたが、やはりGoBack()から戻ってこないですね。

    ↓既存のコードを修正
    _OutputDebugString("this->webBrowser1->GoBack() コールします");
    if( this->webBrowser1->CanGoBack == true ){
     m_bIsGoBack = TRUE;
     this->webBrowser1->GoBack();
     m_bIsGoBack = FALSE;
    }
    _OutputDebugString("this->webBrowser1->GoBack() 処理から戻りました");

    ↓新規コード追加
    private: System::Void Form1_Closing(System::Object^ sender, System::ComponentModel::CancelEventArgs^ e)
    {
     UINT uiCountGoBackWait = 0;
     char szDebugString[256];

     //GoBack処理完了待ち
     if( m_bIsGoBack == TRUE ){
      _OutputDebugString("Form1_Closing() m_bIsGoBack=TRUEの為にGoBack処理待ちを開始します");  
      while( 1 ){
       //GoBack処理待ち
       Sleep(WAITTIME_GOBACK);
       uiCountGoBackWait++;
       if( m_bIsGoBack == FALSE ){
        _snprintf_s(szDebugString, sizeof(szDebugString), "Form1_Closing() %d回待ちうけました", uiCountGoBackWait);
        _OutputDebugString(szDebugString);  
        break;
       }
      }
     }
    }

    Form1_Closing()内のm_bIsGoBack==FALSE待ちうけループで無限Loopに入ってしまいます。

    2017年11月12日 6:26
  • WebBrowser::GoBack()中に限らず、Form::FormClosingイベントのタイミングでWebBrowser::Stop()を呼んでみては?
    2017年11月12日 6:38
  • 想像ですが、メインスレッドと GoBack() を行うスレッドでデッドロックが起こっているのではないでしょうか?

    Sleep のタイミングなどで System::Windows::Forms::Application::DoEvents(); を呼んでみてメインスレッドのイベントを処理するようにしてみてはどうでしょうか?

    2017年11月12日 6:51
  • ありがとうございます。
    確かにForm::FromClosingのタイミングでWebBrowser::Stop()を不正は開放は発生しませんでした。
    この方法で対応したいと思います。

    2017年11月12日 7:03
  • 仰るとおり私も真っ先にデッドロックを疑ったのですが、WebBrowserコントロールはThread側のみで操作しておりますし、確認した結果もデッドロックはしておりませんでした。
    2017年11月12日 7:05
  • 自己レスですが、Form::FormClosingイベントのタイミングでWebBrowser::Stop()を使わない場合の処理メモを記載しておきます。
    (納得のいかない方法ですが・・・)

    ①Form::FormClosingイベントのタイミングで終了できない条件の場合は
    m_bUserWantClose = TRUE;
    e->Cancel = true;
    として、処理を抜ける
    ②別Threadでm_bUserWantCloseを監視しておいて、終了可能条件の場合は終了処理する

    としてもリークせずに対応できます。WebBrowser::Stop()が最適ですが。

    2017年11月12日 7:07