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

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

ただいまの
回答率

90.37%

  • C#

    9487questions

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

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

受付中

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 3,724

GuielNo4

score 71

前提・実現したいこと

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

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

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

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

+2

→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 22: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で済ませたいのですが…

    キャンセル

  • 2017/05/04 23:07

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

    キャンセル

  • 2017/05/05 00:28

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

    キャンセル

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 22:59

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

    キャンセル

  • 2017/05/05 00:23

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

    キャンセル

  • 2017/05/05 07:08 編集

    Dispose が呼ばれるタイミングには二種類あります。一つは明示的に呼ばれる場合で、もう一つはデストラクタから呼ばれる場合です。

    明示的に呼ばれた場合には Dispose(bool disposing) の引数が true になります。
    この場合にはマネージリソースの解放ができます。false の場合はデストラクタから呼ばれているので他のオブジェクトが解放済みの可能性があり、マネージリソースを操作してはいけません。

    このソースでは Dispose() は一度も呼ばれていないので、timer が null になるタイミングはありません。

    代わりに、フォームがクローズされていたらタイマー処理をしないようにしています。
    タイマーはアプリケーションの終了とともに消滅するので、放っておいて大丈夫です。

    キャンセル

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

  • ただいまの回答率 90.37%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る

  • C#

    9487questions

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