質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Q&A

3回答

16327閲覧

C# アプリ終了時の Invoke による例外処理について

GuielNo4

総合スコア88

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

0グッド

0クリップ

投稿2017/05/04 10:44

編集2022/01/12 10:55

###前提・実現したいこと
Invoke を理解したく「動作仕様」と「対処」について質問させて頂きました。

タイマーで定期処理を行うアプリケーション終了時、タイマーは止めているのに、Invoke に例外処理が発生してしまいます。(後述コードを参照願います。)

① 動作仕様を教えてください。
アプリケーション終了時、if ( null == timer ) がブレイクに引っかからずに this.Invoke(…) で例外エラーが出るので、form1 のインスタンスはなくなっていると判断しました。
この例外ではform1 のインスタンスがないのに、Invoke で指定している関数を処理していると考えて良いでしょうか?
→Invokeはインスタンスが無いのに処理されるような機能なんでしょうか?
理解が正しいか質問させて頂きました。

② 対処について
下記コードを修正して this.Invoke(…) を try catch にすることで、
例外を catch 出来ることは確認しているのですが、
コードとしてはそもそもアプリケーション終了時に、例外を発生させないようにするべきではないかと考えております。
何か方法をご存知の方、アドバイス頂けないでしょうか?

###発生している問題・エラーメッセージ
アプリケーションを終了させると、以下のエラーが発生します。

型 'System.ObjectDisposedException' の例外が System.Windows.Forms.dll で発生しましたが、ユーザー コード内ではハンドルされませんでした

###該当のソースコード

using System; using System.Timers; using System.Windows.Forms; partial class Form1 { /// プロジェクト生成時のコードは省略 protected override void Dispose(bool disposing) { if (timer != null) { timer.Stop(); timer.Dispose(); timer = null; } if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } } public partial class Form1 : Form { public Form1() { InitializeComponent(); // タイマーの生成 timer = new System.Timers.Timer(); timer.Elapsed += ElapsedTimer; timer.Interval = 5; // タイマーを開始 timer.Start(); } private delegate void UpdateTextDelegate(); private void ElapsedTimer(object sender, EventArgs e) { if ( null == timer ) { return; } this.Invoke(new UpdateTextDelegate(UpdateText)); } private void UpdateText() { textBox1.Text = "test." }

###補足情報(言語/FW/ツール等のバージョンなど)
visual studio 2015
.NET Framework 3.5

①と②についてアドバイスを頂きたく、よろしくお願い申し上げます。

【修正】
コードに記載のないメソッドを用いていましたので、削除しました。
すみません。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

→Invokeはインスタンスが無いのに処理されるような機能なんでしょうか?

インスタンスはあります。
インスタンスがあるがDiposeされた状態だからObjectDisposedExceptionというエラーになっている状態だと思います。
Disposeされた状態でInvokeを呼ばないようにしなければいけません。

何か方法をご存知の方、アドバイス頂けないでしょうか?

timer.Interval = 5;

は5ms間隔で実行することになっていますが意図していますか?

5ms間隔が意図した値ではない場合、System.Windows.Forms.TimerではなくSystem.Timers.Timerを使用している理由はありますか?

なければ、System.Windows.Forms.Timerを使ったほうが良いです。
System.Timers.Timerはマルチスレッドで動作するので、Invokeを使用するかSynchronizingObjectプロパティにFormのインスタンスを渡すなどをしないとUIコントロールを操作できません。

System.Windows.Forms.TimerはUIスレッドで動作するのでInvokeの必要がありません。

投稿2017/05/04 12:28

hmmm

総合スコア818

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

GuielNo4

2017/05/04 13:33

アドバイス有難うございます。 5ms間隔 は意図したものになります。 https://garafu.blogspot.jp/2015/01/c-timer.html このページを参考にして設計しているのですが、System.Windows.Forms.Timer は精度が劣るため、 System.Timers.Timerを利用しています。 コードサンプルには示していませんが、pythonのサーバとやり取りを行うため、 c++で組んだパイプのdll経由で通信することを目的としていまして、 どうしても5ms間隔で定期処理を行う必要があり、この数値で設計した次第です。 もしInvokeでうまくいかないときは、 「SynchronizingObjectプロパティにFormのインスタンスを渡す」方法を試してみたいと思います。 できればInvokeで済ませたいのですが…
hmmm

2017/05/04 14:07

@GuielNo4 頻繁に更新を行いたいというのであれば設計を変更したほうがよいと思います。そもそもUIを5msで更新というのがそもそも無理があります。System.Timers.Timerでバックグラウンドで定期処理で内部データを更新。System.Windows.Forms.Timerで百ms程度で内部データを使用してUIを更新。のような2段構えのほうが良いと思います。
GuielNo4

2017/05/04 15:28

アドバイスありがとうございました。 最終的にはUIを16.666ms(1フレーム)にするため、System.Timers.Timerでどこまで出来るか、試そうと思います。それでも無理な場合、Formではなくフレーム処理が可能な Unity に切り替えたいと思います。 SynchronizingObject 等のヒント、ありがとうございました。
guest

0

同じかどうか分かりませんが、類似の症状で困って MSDNで質問したのですが、どうも仕方のない事みたいでした。
で、検索の仕方を変えたら、こちらの質問と、同様の質問がありましたので、ここにリンクを置きます。
Invoke 中に FormClosed になる場合の対処法
タイミングも問題で、外部(別スレッド)から、データ更新を行うタイミングでクローズが発生すると例外が発生する事があるようです。回避方法は、例外を捕捉するしかない? ようです。

解決済みとなっていなかったので、参考情報として、記載します。

投稿2020/02/19 12:43

pepperleaf

総合スコア6383

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

  • OnElapsed_TimersTimer がありません。
  • Counter の内容が更新されません。

以上二点を修正すると、うちでは問題なく動きました。
しかし、問題なく動いたのはたまたまだと思います。他に次の点を修正してください。

  • Dispose(bool disposing) の中の if (timer != null) {} を if (disposing && (components != null)) {} のブロック内に入れる
  • ElapsedTimer 内の if ( null == timer ) を if (null == timer && !IsDisposed) に変える

投稿2017/05/04 12:38

Zuishin

総合スコア28656

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

GuielNo4

2017/05/04 13:08

アドバイス有難うございます。 先にご指摘をいただいたコードミス2点を修正しました。
GuielNo4

2017/05/04 13:17

アドバイス頂きましたコード修正、①「ブロック内への移動」②「if文条件の変更」を試してみましたが、 同じエラーが発生してしまいました。 動作させてみて気が付いたのですが、これまで、アプリ終了→timer=null→Invokeでエラーがったのですが、 ①の修正後はアプリ終了 → Invokeでエラーとなっています。 なにか、解決のヒントになればよいのですが…
Zuishin

2017/05/04 13:25

アプリ終了後に Invoke してはいけません。私の書いた二点の修正は、それをしないためのものです。
Zuishin

2017/05/04 13:28

条件に !IsDisposed を加えることにより、フォームが閉じられた後でタイマーイベントが行われることを防ぎます。また、disposing == true のブロック内に入れることにより、Form1 のデストラクタから呼ばれることを防ぎます。 ですから、他にコードが無ければ、おっしゃるような例外は起こらないはずです。修正箇所を間違えていないか、またはここに記載されていない部分に間違いがないかを精査してみてください。
Zuishin

2017/05/04 13:34 編集

いえ、間違えていたのは私でした。 if (null == timer && !IsDisposed) と書きましたが、if (null == timer || IsDisposed) の間違いです。すみませんでした。 || と ! が違います。
GuielNo4

2017/05/04 13:59

上記の修正を加えたのですが、同じエラーが発生しました。 アドバイス「アプリ終了後に Invoke してはいけません。」より、上記のアドバイスを頂いていることを理解致しました。 少々気になりますのが、Disposeメソッドで timer = Null になることを確認しているのですが、 IsDisposed の状態を見るために、コードを if (null == timer) { if (IsDisposed) { return; } } (実際はカッコで改行していますが…) のように修正し、 if (IsDisposed) にブレイクを仕掛けたのですが、引っかからず、 Invokeの例外でプログラムがストップします。 IsDisposedを見たいのですが、なにか方法があったりしますでしょうか?
Zuishin

2017/05/04 15:23

timer は null にならないはずです。ですので、その中にも入りません。なるとしたら、私のコードとは違います。
Zuishin

2017/05/04 22:09 編集

Dispose が呼ばれるタイミングには二種類あります。一つは明示的に呼ばれる場合で、もう一つはデストラクタから呼ばれる場合です。 明示的に呼ばれた場合には Dispose(bool disposing) の引数が true になります。 この場合にはマネージリソースの解放ができます。false の場合はデストラクタから呼ばれているので他のオブジェクトが解放済みの可能性があり、マネージリソースを操作してはいけません。 このソースでは Dispose() は一度も呼ばれていないので、timer が null になるタイミングはありません。 代わりに、フォームがクローズされていたらタイマー処理をしないようにしています。 タイマーはアプリケーションの終了とともに消滅するので、放っておいて大丈夫です。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問