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

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

ただいまの
回答率

88.60%

C# 例外処理について

受付中

回答 4

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 1,553

warks1

score 12

C#(プログラミング)の勉強中です。
visual studio 2017を使用して
タイマー(Windows.Forms.Timer)を利用して下記のloopを定期的に実行させるプログラムを作成しています。
例外処理のところでよく理解できておらず苦戦していますのでご教授お願いします。

webbrowserでページが表示出来ない・DownloadFileでダウンロード出来ない場合
例外処理が発生すると思いますが、例外処理が発生したときの処理は終了して
再度タイマーの時間がきた際には新規でloopを実行したいです。

現状、下記のソースで可能かと思っていましたが、疎通が取れるようになっても
lavel1が開始にならず、接続エラーの状態になっています。

原因・理由や改善方法等、出来るだけ初心者向けに回答していただけますと幸いです。
お手数おかけしますが回答お願いいたします。

追記

どんな例外処理が発生しても、その処理は終了して再度時間がきた際には1から「loop」の処理を開始したいです。
例えば、物理的にLANケーブルが抜けていた場合、webbrowserコントロールでもDownloadFileでも例外が発生すると思います。その処理は停止します。
途中でLANケーブルを挿しなおせば再度繋がるようになり、その後からタイマーの時間が来た際は例外処理が発生しないと思いますのでその際は普通に処理するという流れが良いです。

現状では挿しなおした後も、例外処理が発生する状態です。

# using は省略

namespace WindowsFormsApp
{
    public partial class Form1 : Form
    {
        // キャンセレーショントークンソース
        private CancellationTokenSource CancellationTokenSource;
        //タイマーの宣言
        System.Windows.Forms.Timer timer2 = new System.Windows.Forms.Timer();
        public Form1()
        {
            InitializeComponent();
            //ボタン
            button1.Enabled = true;
            button2.Enabled = false;
            //タイマー
            loop(Main_loop);
        }
        public void button1_Click(object sender, EventArgs e)
        {
            //開始ボタンは有効・停止ボタンは無効
            button1.Enabled = false;
            button2.Enabled = true;

            //タイマーのインターバル設定
            timer2.Interval = intVal * 60000;
            //タイマー有効化
            timer2.Enabled = true;
        }
        private void button2_Click(object sender, EventArgs e)
        {
            //タイマーの停止
            timer2.Enabled = false;

            //開始ボタンは有効・停止ボタンは無効
            button1.Enabled = true;
            button2.Enabled = false;
        }

        public bool OpenWebWait()
        {
            try
            {
                //読み込み完了まで待つ
                while (webBrowser1.IsBusy || webBrowser1.ReadyState != WebBrowserReadyState.Complete)
                {
                    //無処理
                    System.Windows.Forms.Application.DoEvents();
                    System.Threading.Thread.Sleep(300);
                }

                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }


        //numericUpDown1 * 60秒 でループするタイマー
        public void loop(EventHandler eventHandler)
        {
            timer2.Tick += new EventHandler(eventHandler);
            timer2.Enabled = false;
        }
        private void Main_loop(object sender, EventArgs e)
        {
            try
            {
                //TLS1.2を使用・SSL自己証明書のスルー
                ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(OnRemoteCertificateValidationCallback);
                ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
                //ファイルの保存先指定
                string filepath = (保存先);  

                //label1の表示変更
                label1.Text = "接続中";
                label1.Refresh();


                //接続
                webBrowser1.Navigate(接続先URL);

                //ページの読み込み完了まで待つ
                OpenWebWait();

                // クッキーを取得する。
                string cookieStr = webBrowser1.Document.Cookie;

                // WebClientを生成する。
                WebClient wc = new WebClient();
                Encoding enc = Encoding.UTF8;

                // WebClientのヘッダ設定を行う。
                wc.Headers[HttpRequestHeader.Cookie] = cookieStr;

                //label1の表示変更
                label1.Text = "ダウンロード中";
                label1.Refresh();

                //ダウンロード
                wc.DownloadFile(ダウンロードURL);
                wc.Dispose();



            }
            catch (Exception w) when (w is ObjectDisposedException || w is InvalidOperationException || w is ArgumentOutOfRangeException || w is COMException)
            {
                //label1の表示変更
                label1.Text = "接続エラー";
                label1.Refresh();

                //Console.WriteLine(w.Message);
                //1秒待つ
                Task.Delay(1000).Wait();
            }
            catch (Exception w) when (w is COMException)
            {
                Console.WriteLine(w.Message);
                Application.Exit();
            }
        }


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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

質問への追記・修正、ベストアンサー選択の依頼

  • Wind

    2018/11/22 10:34

    lavel・・・

    キャンセル

  • Wind

    2018/11/22 10:35

    Timerを使って、もっとシンプルに例外を再現出来るソースコードを書いてみた方がいいのでは無いでしょうか?

    キャンセル

  • warks1

    2018/11/22 10:39

    webbrowserコントロールを設置してもらってグーグルでもなんでも指定してLANケーブルを抜いてやっていただければ再現は出来るかと思いますが、、、

    キャンセル

回答 4

0

例外の話ではないんですが、設計がまずいという話です

OpenWebWait で待っている間にフォームを閉じようとすると
「アプリケーションが応答していません」ってのが出ませんか?

基本的に、Formsから呼び出された処理は、出来るだけ早く関数を終了しないといけません
それをせずに内部で無限ループを作った場合、いろいろ問題が置きます
ラベルが「開始」にならないのもそれが原因です

なぜなら、ラベルを開始と表示するためには、ラベルを書き換える必要がありますが、
そのためには、ラベルクラスに一旦処理を渡さないといけません
しかし、Windows.Forms.Timerが内部で無限ループを作って、処理を終わらせないので、
ラベルを更新する処理に飛んできません

このような関数の途中でSleepを挟みたい、みたいなことをする場合は、
async/awaitの機能を使う必要があります

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/11/22 11:43

    回答ありがとうございます。
    ループ間隔を遅らせればよいでしょうか。
    それとも、呼び出すまでループするという考え方自体に問題があるということでしょうか。
    理解が出来ず申し訳ありません。
    async/await機能を使うとのことですが、具体的な例などいただけないでしょうか。
    非同期処理を実施する際に使うものだと認識しているのですが、どう書けば良いかわからない状況です。

    キャンセル

  • 2018/11/22 11:56 編集

    処理が終わるまでループするのが駄目です
    Sleepするのではなく、returnして、もう一度Forms.Timerから呼び出されたときに続きの処理を行ってください

    キャンセル

  • 2018/11/22 12:45

    丁寧に回答していただきありがとうございます。returnの使い方が正直まだ理解できていない為。すぐに対応できそうにないので試行錯誤してみます。

    キャンセル

0

以下のTimer.Tickイベントの使い方、正しいのでしょうか?

        //numericUpDown1 * 60秒 でループするタイマー
        public void loop(EventHandler eventHandler)
        {
            timer2.Tick += new EventHandler(eventHandler);
            timer2.Enabled = false;
        }

そもそも、タイマースタート(timer2.Enabled = true;)後、タイマーの時間が来たら(つまりtimer2.Tickのイベントが発生したら)どの処理が走るのでしょうか?
まずはそこを確認するのが先決ではないでしょうか?

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/11/22 17:29

    一部切り取りしてしまったので、そこは問題ないです。
    Main_loopを指定しています。
    すみません。。

    キャンセル

  • 2018/11/22 18:03

    eventHandlerはMain_loopなのですね。
    ところで、タイマーの使い方ですが、例外が発生したときにタイマーをスタートして、リトライした方がよいのではないかと思います。(もし正常に終了したらtimer2.Enabled = false;で止める)
    理由は、例外発生する前にタイマー時間が来てしまったら、DL処理等に支障が出るように思ったからです。
    また、タイムアウト用のタイマーが必要であれば、上記とは別に用意した方がよいでしょう。

    キャンセル

0

基礎的なことを学んでみて今ちょうど欲しい物を付け焼刃の知識で書いてる感じですかね
大抵の人はForm1に実装処理書いてるところから作り直したくなる衝動に駆られるような気がします

全部作るというよりはその中の小さい処理からちゃんと動くかどうかを書いて確認したり、クラス分けをできるようにしていったほうがいいような気がします

ついでに言えばWindowsFormなので全体的に何をしようとしているのかコードだけで判断できない人が多数のようにも感じます

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/11/26 09:59

    回答ありがとうございます。
    クラス分けについても検索してみたりはしたのですが、どうも継承のところが理解しきれておらずうまく出来ませんでした。検索していると簡単なソースであればクラス分けする必要もないとも見ましたが、実際よく分かっていません。ソースが確認しやすくなる他には何か動作的に変わるものがあるのでしょうか。

    キャンセル

  • 2018/11/26 10:29

    クラス分けをするとインスタンスを作成して呼び出す必要があるのでよりオブジェクト指向らしい動作になりますが、ソフトの見た目やどういう挙動をするかは何を書くか次第です。可読性が上がればメンテナンス性も向上し、結果的に作業が効率化されるという利点は確実にあります。完成品の予定が掲載しているコードからせいぜい10行程度しか増えないのであれば必要はありません。個人的に今後ボタンを更に追加したり、他の機能を追加したりするんでないかと思ったのでクラス分けを推奨しました。継承は今別に理解しなくてもいいんでないでしょうか。継承がなにかは必要になったとき割とすぐ理解できるので

    キャンセル

  • 2018/11/26 10:51

    ご丁寧に回答していただき、ありがとうございます。
    すぐに理解して反映させることは難しいですが、クラス分けも今後の課題としてより良いプログラム作成が出来るようにしていきます。

    キャンセル

-1

タイマーを再設定してないからでは?

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/11/26 11:20

    その場合、例外が起こったことが原因で動作しなくなるのではなく、動作しないことを示すために例外が起こっているのでしょう。

    キャンセル

  • 2018/11/26 12:01

    動作しないことを示すために例外が起こっている場合、どのように対策すれば改善されますでしょうか。

    キャンセル

  • 2018/11/26 12:02

    まずは原因を特定してください。

    キャンセル

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

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

関連した質問

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