2009-03-17 11 views
2

VS2005で実行されているプログラムと実行ファイルを直接実行しているプログラムに奇妙な違いがあります。基本的に、Application.DoEvents()呼び出し内のメソッドで例外がスローされると、Visual Studio内で実行されているときに例外が検出される可能性があります。コンパイルされた実行可能ファイルを実行すると、例外は捕捉されず、プログラムがクラッシュします。Application.DoEvents()の内部から例外をキャッチすることはできますか?

問題を示す簡単なコードを次に示します。標準的なwinforms定型句と2つのボタンとラベルを想定します。

これを実行するには、開始ボタンをクリックして10秒カウントを開始します。 10秒間が経過する前に、中止ボタンを押してください。 DoEvents()の内部に例外がスローされます。例外は捕捉されるべきです。これは、Visual Studio内で実行している場合にのみ発生します。

private void StartButton_Click(object sender, EventArgs e) { 
     DateTime start = DateTime.Now; 

     try { 
      while (DateTime.Now - start < new TimeSpan(0, 0, 10)) { 
       this.StatusLabel.Text = DateTime.Now.ToLongTimeString(); 
       Application.DoEvents(); 
      } 

      MessageBox.Show("Completed with no interuption."); 
     } catch (Exception) { 
      MessageBox.Show("User aborted.");     
     } 
    } 

    private void ButtonAbort_Click(object sender, EventArgs e) { 
     throw new Exception("aborted"); 
    } 

私はこれらの例外を捕捉したいと考えています。それを動作させる方法はありますか?

更新:

私は再入-頭痛を誘発DoEvents()以外のアプローチを検討するつもりです。しかし、私はより良く働くように見えるものは見つけていません。私のシナリオでは、私はいくつかの科学機器を制御している長い実行ループがあり、しばしば温度が安定するか何かを待たなければならないということです。ユーザーにプロセスを中止する機能を与えたいので、プロセスが最初に開始されたサイトでキャッチする予定のカスタム例外をスローするアボートボタンがあります。それは完璧な解決策に見えました。それは何らかの理由でうまくいかないという事実を除いて。

これを機能させることができない場合は、より良いアプローチがありますか?

アップデート2:

私はVSではない、それは実行可能ファイルとして動作しますメインの最初の行()、としてこれを追加し、しかし、その状況が逆転しています。狂ったことは、それがノーオペレーションであるように見えるということです。私はこれがどのようになるかを理解することができます。

Application.ThreadException += delegate(
     object sender, 
     System.Threading.ThreadExceptionEventArgs e 
    ) 
    { throw e.Exception; }; 

これは非常識です。

答えて

6

実際にDoEventsを使用するがありますか?それはデバッグするのが非常に難しい再入門につながります。

私は、アプリケーションからの再入室を取り除くと、例外をより簡単にキャッチする場所を見つけることができると考えています。

編集:はい、間違いなくこれを行うより良い方法があります。長いスレッドを別のスレッドで実行してください。 UIスレッドは、のみUI操作を実行する必要があります。あなたの "中止"ボタンを長時間実行しているタスクが定期的にチェックするフラグに設定します。 WinFormsスレッディングの例についてはWinForms threading page、別のスレッドが監視するフラグを設定するスレッドの例については volatility pageを参照してください。

編集:私はちょうど(私の記事をカバーしていない - それはpre-.NET 2.0書かれた)BackgroundWorkerがいることを思い出しましたCancelAsync方法を持っている - 基本的には、この(基本的に扱っCancellationPendingWorkerSupportsCancellationで使用"キャンセルするように設定するフラグがあります"というものがあります。ワーカースレッドからCancellationPendingをチェックし、アボートボタンクリックハンドラからCancelAsyncを呼び出してください。

+0

私は意図的にこれをやりました。ユーザーに、長時間実行している外部プロセスを待っている時間を費やしている長時間実行中のプロセスを中止するアボートボタンを提供したいと考えています。 DoEventsなしでこれを行う最善の方法は何ですか? (OPの新しいアップデートも参照してください) – recursive

+0

くそ、Skeet。私はあなたがこれを編集している間に慎重に私の小さな答えを作り出していました。 –

+0

私はいつもスレッドを避けなければならないと聞いてきました。可能性があるのであれば、デバッグが難しいからです。あなたの編集の文言は、UI以外の操作(すべてのアプリケーション)を持つすべてのアプリケーションがマルチスレッドでなければならないことを示唆しています。よくわかりません。 – recursive

2

DoEventsは、基礎となるメッセージループの原因となるため、アプリケーションの動作を予測不能にする再入力(stated in MSDN)につながる可能性があります。

私はあなたの仕事(温度が安定するのを待っている)をスレッドに移して、何らかの種類の信号を使用して待機が終了したらユーザーインターフェイスに伝えることをお勧めします。これにより、Application.DoEventsを使用せずにユーザーインターフェイスの応答性を維持できます。

1

Application.ThreadExceptionイベントを使用して例外をキャッチする可能性があります。

まずコントロールを作成する前に、アプリケーションの開始時に Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);を設定する必要があります。次に、ThreadExceptionイベントのイベントハンドラを追加します。

これが機能しない場合は、BackgroundWorkerを使用することをお勧めします。つまり、DoEventsを使用する必要はなく、より良い解決策になります。

編集:スレッド例外ハンドラのあなたの例では、別の例外がスローされます。考え方は、例外処理コードをそこに置いて、別の例外を発生させないことでした。

しかし、私はあなたのループコードを実行するためにBackgroundWorkerを使用するよう依頼したいと思います。

1

古いニュースを出そうとしませんが、私はこの同じ問題で苦労し、解決策を見つけられませんでした。あなたがバックグラウンドワーカーを実行していて、ユーザーがバックグラウンドワーカーにある同じ機能を実行したいと思っている場合、Application.DoEvents()は最良の方法のように思えます。これを使って。問題は、RunWorkerCompletedイベントにコードが含まれていることにあります。 RunWorkerCompleted内に存在するCancelAsyncを使用してワーカー自身をキャンセルすると、そのジョブが実行されます。問題は、通常、RunWorkerCompletedにあるものがGUIにアクセスしていて、Application.DoEvents()の実行中にGUIにアクセスできなくなり、RunWorkerCompmletedからコードを削除してReportProgress関数に組み込むことがより受け入れられることです。ただし、メインスレッドが別の実行を要求していないことを確認するには、reportprogress関数の直前にcheckイベントをスローする必要があります。 reportprogress前に、この右のようなので、何か:

if (backgroundWorker1.CancellationPending == true){ 
    e.Cancel = true; 
    return; 
} 
backgroundWorker1.ReportProgress(0); 

だからバックグラウンドスレッドは、そのすべての仕事をしているし、次にあなたが持っている右の前に、それはそこには、このチェックを投げるReportProgress。

if (backgroundWorker1.IsBusy == true){ 
    backgroundWorker1.CancelAsync(); 
    While(backgroundWorker1.CancellationPending == true){ 
      Application.DoEvents(); 
    } 
} 

これは明らかに使用することは悪い習慣ですが、それは取り払う実現:あなたのユーザーはちょっと私は実際に彼らがこのような何かをループ内で待機していることになる他の項目にこのクエリを実行したいと言っている場合は、この方法例外の私が作業していたプロジェクトを修正し始めたときに、エラーが発生した唯一の理由は、同じ時間にメインスレッドにアクセスしていたためです。おそらくこのために炎上するでしょうが、答えがあります。

関連する問題