C#バックグラウンド処理からテキスト描画

このQ&Aのポイント
  • C#でバックグラウンドワーカーを使用した時間表示の処理を行い、終了時にエラーメッセージが出る問題について解決方法を教えてください。
  • バックグラウンド処理中に「破棄されたオブジェクトにアクセスする事は出来ません。」というエラーメッセージが表示されます。
  • バックグラウンド処理の開始時にバックグラウンドワーカーを実行し、定期的に時間を表示する処理を行っていますが、終了時にエラーメッセージが出ています。エラーの解決方法を教えてください。
回答を見る
  • ベストアンサー

C#でバックグラウンド処理からテキスト描画

C#でバックグラウンドワーカーを使用した時間表示の処理を行っております。 アプリケーションが起動している時は良いのですが、終了時に(1)の所で次のようなメッセージが出ます。 「破棄されたオブジェクトにアクセスする事は出来ません。」 「オブジェクトForm1です」 エラーを無くす方法についてご存知のかたいらっしゃいましたらご教授頂けないでしょうか。 //バックグラウンド処理の開始 private void Form1_Shown(object sender, EventArgs e) {   BackGroundWorker1.RunWorkerAsync(); //バックグラウンド処理開始 } //バックグラウンド処理 private void BackGroundWorker1_DoWork(object sender, DoWorkEventArgs e) {   while (true){ Invoke(new SetTextDelegate(SetTime));//テキスト描画・・・・・(1) } } //バックグラウンドから呼び出されるデリゲート関数 delegate void SetTextDelegate(); void SetTime() {    //時間の表示 label1.Text = DateTime.Now.ToString("日時:yyyy年MM月dd日 HH:mm:ss"); }

質問者が選んだベストアンサー

  • ベストアンサー
  • chie65535
  • ベストアンサー率43% (8522/19371)
回答No.1

InvokeメソッドはFormクラスのメンバ関数です。 Invokeが呼べるのはForm1オブジェクトが生きている間のみです。 bool GoBackGround = true; など、フラグを作っておいて、FormClosingあたりでGoBackGround = false;にします。 で、 while (GoBackGround){ Invoke(new SetTextDelegate(SetTime));//テキスト描画・・・・・(1) } とかやれば、Form1がクローズ(破棄)されるとInvokeが呼ばれない為、エラーが出なくなるでしょう。 てゆ~か、アプリ終了時に、自分が作成したスレッドやバックグラウンドを放置して修了するのは、やっちゃいけないと思います。 アプリ内で作成したスレッドやバックグラウンドは、修了前に「自分で後始末」しましょう。 つまり「修了前に、スレッドやバックグラウンドを自分で破棄しないと駄目」です。 ちゃんと自分で破棄すれば、変なエラーは出ませんし、上記のようなフラグ操作も要りません。

flowergoo
質問者

お礼

ご回答頂きありがとうございます。 ご教授頂いたとおり修正した所解決しました。 内容といたしましては次のようになりました。 (1)bool GoBackGround = true;を追加 (2)FormClosingにGoBackGround = false;を追加  ※この行のあとにApplication.DoEvents();を追加 (3)while (GoBackGround)とする //バックグラウンド処理の開始 private void Form1_Shown(object sender, EventArgs e) {   GoBackGround = true;・・・・・・・・・・・・・・・(追加)   BackGroundWorker1.RunWorkerAsync(); //バックグラウンド処理開始 } //バックグラウンド処理 private void BackGroundWorker1_DoWork(object sender, DoWorkEventArgs e) {   while (GoBackGround){・・・・・・・・・・・・・・(trueからGoBackGroundに変更) Invoke(new SetTextDelegate(SetTime));//テキスト描画・・・・・(問題が起きていた箇所) } } //バックグラウンドから呼び出されるデリゲート関数(この関数は変更無し) delegate void SetTextDelegate(); void SetTime() {    //時間の表示 label1.Text = DateTime.Now.ToString("日時:yyyy年MM月dd日 HH:mm:ss"); } // 閉じる前に呼ばれる private void Form1_FormClosing(object sender, FormClosingEventArgs e) { GoBackGround = false;・・・・・・・・・・・・・・・(追加) Application.DoEvents();・・・・・・・・・・・・・(追加)※重要 } 以上、助かりました。 ありがとうございます。

関連するQ&A

  • VC++

    フォームアプリを作成しており、実装に関して不明点があるため質問させて頂きます。 やりたいこととしては、Formでないファイル(クラス)でFormのコンポーネントを 操作したいことです。 具体的に、重たい処理を別スレッドで処理させて、処理中の経過をFormの プログレスバーに表示することです。 重たい処理は別ファイル(クラス)で行います。 以下に大まかなコードを記載します。 ----------------------------------------------------- ---- Form.h ---- #include "Main.h" namespace XXX { public ref class Form1 : public System::Windows::Forms::Form {  Main^ mMain;    // ボタン押下  private: System::Void btn_Click(System::Object^ sender, System::EventArgs^ e) {   backgroundWorker1->RunWorkerAsync();  }  // ワーカ  private: System::Void backgroundWorker1_DoWork(System::Object^ sender, System::ComponentModel::DoWorkEventArgs^ e) {   mMain->func();    } }; } ---- Main.c ---- Main::func() {  // 処理をしつつプログレスバーのValueを更新 } ----------------------------------------------------- 重たい処理をForm内で処理するのであればデリゲートを使用すれば出来そうです。 上記のようなケースでも適用できますか? また、別の方法があれば教えてください。

  • C# バックグランドのキャンセル方法???

    SMTP でメールを送る際に 4000KB 以上ある画像ファイルを添付しております 当然時間が掛かりますので送信部分を切り出してバックグランド化致しました 主要部分は次の通りです System.Net.Mail.SmtpClient smtp = new System.Net.Mail.SmtpClient(); smtp.Host = "smtp.MyMail.com"; string MailSubject = ""; string MailBody = ""; MailMessage oMsg = new MailMessage("○○○@○○○.com", "□□□@□□□.com", MailSubject, MailBody); oMsg.Attachments.Add(new Attachment(@"大きな画像.bmp")); smtp.Send(oMsg); でもやはり送信には時間が掛かりますのでキャンセル機能を追加いたいと思い、その方法を調べました するとバックグランドの中の重たい処理の途中で定期的にキャンセルの有無をチェックしろとあり、サンプルとして下の例が示されています このForループの中に処理を入れろと言われても送信部分は一本道です、ループの中に組込みようがありません、どうすれば良いのでしょうか? ★ それとも私は基本的な誤解をしているのでしょうか??? private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { for (int i = 0; i < 100; i++) { //処理中にキャンセルされていないかを定期的にチェックする if (this.backgroundWorker1.CancellationPending) { e.Cancel = true; return; } } } //バックグラウンド処理の終了を判断する private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { if (e.Cancelled) { MessageBox.Show("キャンセルされました"); } else { MessageBox.Show("キャンセルされずに終了しました"); } }

  • C#でオブジェクトの有無を取得する

    オブジェクトが作られている時と作られていない時で処理をわけたいのですがうまくいきません。 この書き方の何がまずいのでしょうか? namespace オブジェクト検索 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { Control c = Controls["form2"]; if (c != null) { ((TextBox)c).Text += "*"; } } private void button2_Click(object sender, EventArgs e) { Form2 form2 = new Form2(); form2.Show(); Application.DoEvents(); } } } ボタン2を押した時に新しいフォームが立ち上がり、そのフォームがあるときは*が出るようにしたいです。

  • C#でテキストボックスをクリック→フォームが開く→フォームで設定した値

    C#でテキストボックスをクリック→フォームが開く→フォームで設定した値を元のテキストボックスに入力 こんな感じのことをC#でやりたいのですがよくわからないので教えてください。 1. 親をForm1、子をForm2とします 2. Form1のTextBox1をクリックするとForm2が開いきます 3. Form2で設定した値をForm2の「入力」ボタンを押すとForm2が閉じてTextBox1に値が入力されます というものです。 ここで、textBoxは「クリックしたコントロール名を取得してForm2に渡したい」のです。 テキストボックスがたくさんあるので、textBox1とか決まった名前ではありません。 この部分がわからないのです。 ------------------------------------------------------- Form1: private void textBox1_Click(object sender, EventArgs e) {  Form2 fromItem = new Form2(sender);  DialogResult deRet = fromItem.ShowDialog(); } Form2: public Form2(object sender) {  InitializeComponent();  object pSender = sender; } private void button_input_Click(object sender, EventArgs e) {  ((System.Windows.Forms.TextBox)pSender).text = "hoge"; } ------------------------------------------------------- この程度しかわかりません。 正しい方法を教えてください。よろしくお願いいたします。

  • BackgroundWorkerの処理

    マルチスレッド機能であるBackgroundWorkerを利用してプログラムを書いていたのですが DoWorkからコントロールへのアクセスが出来ません。 どうすればコントロールへアクセスできるのでしょうか? 誰かお詳しい方、ご教授願います。 Private Sub back1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles back1.DoWork     textbox1.text="処理中とか表示したい" End Sub

  • BackgroundWorkerについて(C#)

    C#でBackgroundWorkerを使ったプログラムを試しています。 Windows Form上にButtonとProgressBarを設置して以下のプログラムを 動作させると、プログレスバーが終了するまえに”終了”が出てしまいますが これはどうしてでしょうか? よろしくお願いいたします。 using System; using System.ComponentModel; using System.Threading; using System.Windows.Forms; namespace WindowsFormsApplication1 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { button1.Enabled = false; Random rand = new Random(); BackgroundWorker bgWorker = new BackgroundWorker(); bgWorker.DoWork += ((sender2, e2) => { for (int i = 0; i < 10; i++) { Thread.Sleep((int)(rand.NextDouble() * 500)); bgWorker.ReportProgress((i + 1) * 10); } }); bgWorker.RunWorkerCompleted += ((sender2, e2) => { button1.Enabled = true; bgWorker.Dispose(); MessageBox.Show("終了"); }); bgWorker.ProgressChanged += ((sender2, e2) => { progressBar1.Value = e2.ProgressPercentage; }); bgWorker.WorkerReportsProgress = true; bgWorker.RunWorkerAsync(); } } }

  • C# Formの操作

    Form2 form2 ; // 子 Form Form3 form3 ; // 子 Form private void button1_Click(object sender, System.EventArgs e) { form2 = new Form2() ; this.AddOwnedForm(form2) ; // 親 Form が form2 を所有する form2.Show() ; } private void button2_Click(object sender, System.EventArgs e) { form3 = new Form3() ; this.AddOwnedForm(form3) ; // 親 Form が form3 を所有する form3.Show() ; } 上記のようにボタン一つに対して1つのFormに対する処理をするのではなく,ボタン1つに対して 複数のFormを処理する.つまり 例えば,Form1,2,3とあったとして,Form1のボタンを押してFrom2を開き,そこで何らかの処理を したものをForm1に反映する.または,From3のボタンを押してForm2を開きそこで何らかの処理を 行いForm3に反映する. この場合,Form1とForm3のどちらのFormのボタンが押されたか判断しないといけないと思うのですが,ここの処理がうまくいきません. どちらかのFormのボタンを押してForm2を表示し,どちらかのFormに反映させる所までは出来たのですが,両方の判断材料を入れて,どちらのFormのボタンを押されたか判断する際に,押されなかった方が「null」となりエラーになります. 下記にこの判断部分を掲載いたします.どなたかご教授よろしくお願いいたします. Form1 public int but = 0; public int butt1; public string DB; public string HDB; public Form2 f2 = null; private void mybutton1_Click(object sender, EventArgs e) { butt1 = 1; ・ ・ ・ public int Ye=0; private void button43_Click(object sender, EventArgs e) { butt1 = 0; From2 public partial class Form2 : Form { Form1 f1; Form3 f3; public Form2(Form1 f) { f1 = f; // メイン・フォームへの参照を保存 InitializeComponent(); } public Form2(Form3 f) { f3 = f; // メイン・フォームへの参照を保存 InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { #region int Pin1 = f1.butt1; int Pin3 = f3.butt3; Form3 Form1と基本的には同じです.

  • c# イベントハンドラ 統一

    trackBarを複数配置しているのですが、そのtrackBarごとに private void trackBar1_Scroll(object sender, EventArgs e) { previewcolor(this.trackBar1.Value,'赤'); } private void trackBar2_Scroll(object sender, EventArgs e) { previewcolor(this.trackBar1.Value, '青'); } private void trackBar3_Scroll(object sender, EventArgs e) { previewcolor(this.trackBar1.Value, '緑'); } private void trackBar4_Scroll(object sender, EventArgs e) { previewcolor(this.trackBar1.Value, '透'); } こんな風に別々に記述する形になって非常にスッキリしないです。 これをジェネリクス?やデリゲート?などを使ってすっきりできないのでしょうか? visual stdio c# 2008を使ってます。 宜しくお願いします。

  • C# フォーム追加後、旧フォームを閉じたい

    前略 ・C#で教えてください。 ・Form1 からshowDiag()メソッドでForm2をつくります。Form2が表示されたら非アクティブとなったForm1を閉じたいのですが どのようにしたらよいのかおしえてください。  下記は、Close() メソッドで Form2側から閉じようとしたプログラムですがFrom1を閉じることができません。 よろしくお願いします。 //----------------------------------------------- namespace formClose { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { Form2 form2 = new Form2(); form2.ShowDialog(); } }   public partial class Form2 : Form { public Form2() { InitializeComponent(); } private void Form2_Load(object sender, EventArgs e) { Form1 form1 = new Form1(); form1.Close(); //Form1が閉じない!! } private void button1_Click(object sender, EventArgs e) { this.Close(); //Form2を閉じる }    } } 以上

  • Form2のテキストボックスを更新する方法(C#)

    namespace WindowsFormsApplication_test { public partial class Form1:Form { public Form1() { InitializeComponent(); } private void Form1_Load(object sender,EventArgs e) { Form2 Fm2 = new Form2(); Fm2.StartPosition = FormStartPosition.CenterScreen; Fm2.Show(); } private void button1_Click(object sender,EventArgs e) { Random a = new Random(); int x=a.Next(100); x=x+10; textBox1.Text=x+""; } } } 起動時にForm1とForm2を表示するプログラムを作ったのですが、 Form1のボタンをクリックした時、 Form2のtextBox1に、Form1のxの値を表示する方法が分かりません。 記述方法を教えて下さい。

専門家に質問してみよう