user250078
user250078

Reputation:

Cannot access a disposed object

Cannot access a disposed object

Anyone know why I would get this error "Cannot access a disposed object" on this line of code

Invoke(new UpdateTimerDelegate(UpdateTimer), new object[] { null });

Hit start over then either accept / reject / or close the form -- seems to happen when closing the form

the error happens when you bring the form up again

using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace TimerTester
{
    public class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void StartOverBtn_Click(object sender, EventArgs e)
        {
            ShowForm2();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            ShowForm2();
        }

        private void ShowForm2()
        {
            Form2 f2;

            f2 = new Form2(10000);
            f2.Owner = this;
            switch (f2.ShowDialog())
            {
                case DialogResult.Yes:
                    break;
                case DialogResult.No:
                    break;
                default:
                    break;
            }
        }

        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.StartOverBtn = new System.Windows.Forms.Button();
            this.SuspendLayout();
            // 
            // StartOverBtn
            // 
            this.StartOverBtn.Location = new System.Drawing.Point(33, 33);
            this.StartOverBtn.Name = "StartOverBtn";
            this.StartOverBtn.Size = new System.Drawing.Size(75, 23);
            this.StartOverBtn.TabIndex = 0;
            this.StartOverBtn.Text = "Start Over";
            this.StartOverBtn.UseVisualStyleBackColor = true;
            this.StartOverBtn.Click += new System.EventHandler(this.StartOverBtn_Click);
            // 
            // Form1
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(292, 266);
            this.Controls.Add(this.StartOverBtn);
            this.Name = "Form1";
            this.Text = "Form1";
            this.Load += new System.EventHandler(this.Form1_Load);
            this.ResumeLayout(false);

        }

        #endregion

        private System.Windows.Forms.Button StartOverBtn;
    }
}

form2

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;

namespace TimerTester
{
    public class Form2 : Form
    {
        public Form2(int timeoutSecondsForAcceptCall)
        {
            this.timeoutSecondsForAcceptCall = timeoutSecondsForAcceptCall;
            InitializeComponent();
            tUpdateTimer = new System.Threading.Timer(new System.Threading.TimerCallback(UpdateTimer), null, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1));
        }

        DateTime dtCreate = DateTime.Now;
        int timeoutSecondsForAcceptCall = 99999;
        System.Threading.Timer tUpdateTimer;

        private delegate void UpdateTimerDelegate(object obj);
        void UpdateTimer(object obj)
        {
            if (InvokeRequired == true)
            {
                Invoke(new UpdateTimerDelegate(UpdateTimer), new object[] { null });
            }
            else
            {
                TimeSpan ts = DateTime.Now - dtCreate;
                if (ts.TotalSeconds > timeoutSecondsForAcceptCall)
                {
                    DialogResult = DialogResult.Cancel;
                    return;
                }
                TimeSpan timeleft = TimeSpan.FromSeconds(timeoutSecondsForAcceptCall) - ts;
                label1.Text = FormatTimeSpan(timeleft);
            }
        }

        private string FormatTimeSpan(TimeSpan ts)
        {
            return ((int)ts.TotalMinutes).ToString().PadLeft(2, '0') + ":" + ((int)ts.Seconds).ToString().PadLeft(2, '0');
        }


        private void btnAccept_Click(object sender, EventArgs e)
        {
            DialogResult = DialogResult.Yes;
        }

        private void btnReject_Click(object sender, EventArgs e)
        {
            DialogResult = DialogResult.No;
        }

        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.btnAccept = new System.Windows.Forms.Button();
            this.btnReject = new System.Windows.Forms.Button();
            this.label1 = new System.Windows.Forms.Label();
            this.SuspendLayout();
            // 
            // btnAccept
            // 
            this.btnAccept.Location = new System.Drawing.Point(31, 30);
            this.btnAccept.Name = "btnAccept";
            this.btnAccept.Size = new System.Drawing.Size(75, 23);
            this.btnAccept.TabIndex = 0;
            this.btnAccept.Text = "Accept";
            this.btnAccept.UseVisualStyleBackColor = true;
            this.btnAccept.Click += new System.EventHandler(this.btnAccept_Click);
            // 
            // btnReject
            // 
            this.btnReject.Location = new System.Drawing.Point(140, 29);
            this.btnReject.Name = "btnReject";
            this.btnReject.Size = new System.Drawing.Size(75, 23);
            this.btnReject.TabIndex = 1;
            this.btnReject.Text = "Reject";
            this.btnReject.UseVisualStyleBackColor = true;
            this.btnReject.Click += new System.EventHandler(this.btnReject_Click);
            // 
            // label1
            // 
            this.label1.AutoSize = true;
            this.label1.Location = new System.Drawing.Point(31, 80);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(35, 13);
            this.label1.TabIndex = 2;
            this.label1.Text = "label1";
            // 
            // Form2
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(240, 117);
            this.Controls.Add(this.label1);
            this.Controls.Add(this.btnReject);
            this.Controls.Add(this.btnAccept);
            this.Name = "Form2";
            this.Text = "Form2";
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        private System.Windows.Forms.Button btnAccept;
        private System.Windows.Forms.Button btnReject;
        private System.Windows.Forms.Label label1;
    }
}

Upvotes: 2

Views: 10941

Answers (3)

Sergey Berezovskiy
Sergey Berezovskiy

Reputation: 236268

You see this error, because your System.Threading.Timer is not component of form. Thus it doesn't get disposed when form is closed. I'd recommend you to use System.Windows.Forms.Timer instead. Because this is a component, which you can place on your Form and it will be disposed with other form's components (just drag it from toolbox in designer).

public class Form2 : Form
{
   DateTime dtCreate = DateTime.Now;
   int timeoutSecondsForAcceptCall = 99999;

   public Form2(int timeoutSecondsForAcceptCall)
   {
      this.timeoutSecondsForAcceptCall = timeoutSecondsForAcceptCall;
      InitializeComponent();
      timer1.Interval = 1000;
      timer1.Start();
   }

   private void timer1_Tick(object sender, EventArgs e)
   {
      TimeSpan ts = DateTime.Now - dtCreate;
      if (ts.TotalSeconds > timeoutSecondsForAcceptCall)
      {
         DialogResult = DialogResult.Cancel;
         return;
      }

     TimeSpan timeleft = TimeSpan.FromSeconds(timeoutSecondsForAcceptCall) - ts;
     label1.Text = FormatTimeSpan(timeleft);
   }

   // other code
}

BTW another benefit of using System.Windows.Forms.Timer is that it's Tick event handler executed on UI thread. Thus you don't need to verify if invoke required.

Upvotes: 1

Nate
Nate

Reputation: 5407

I've had this problem before. There are a couple things you should be doing.

1) When you close your program, you should be removing handlers and/or shutting down timers or other code that will run as the program is exiting. You are still left with the problem of how many timer handlers will still run after shutting down the timer. One possible solution is to set a "stop running" flag after you turn off the Timer. Check the flag in the handler and don't run the code if it is set. The flag could be a bool marked as volatile or a long using Interlocked to avoid concurrency issues, especially if your timer is really short.

2) Wrap a try/catch around your Invoke statement, also put one in the GUI editing code (the part where InvokeRequired == false).

Upvotes: 0

particle
particle

Reputation: 3350

Your form is disposed by the time invoke() message is processed by the message loop. I donot think so there is a way to avoid it. You can put a check e.g

  if(!yourForm.IsDisposed)
      yourForm.Invoke(...)

But that may not work sometime as the invoke is posted to UI thread and by the time it is process form may already be disposed and you will still get that exception. You have to fix your program in a way to avoid this situation by making sure invoke is not posted on a disposed or disposing form. This is a fatal error and your program will exit after this. I think suppressing it via try{}catch{} will not work as framework already start to unload when catch block is executed.

You can fix the problem in two ways. 1. Just drop a timer component from toolbox i.e. System.Windows.Forms.Timer and use that. Designer will automatically add it to component collection and dispose it before form is disposed. 2. Manually over OnDispose(bool disposing) in your form and perform

if(disposing)
    yourTimer.Dispose()

So now your timer is dispose before your form so no subsequent invoke() will be posted to your form after its disposed.

Upvotes: 1

Related Questions