简体   繁体   中英

Cannot access a disposed object C# (showdialog dispose)

I am new to c# and kind of winging it. using Microsoft Visual C# 2010

I have checked many similar posts and none of the suggestions seem to help

I am getting the following error: "Cannot access a disposed object" which references the main form here

private void btn_RunPkgs_Click(object sender, EventArgs e)
        {
            RunPackages rp = new RunPackages();    
            this.Hide();
            rp.ShowDialog();//The error points to this line
            this.Show();
        }

here is the code that blows up when the security check fails.

private void securityCheck()
        {
            if (MyGlobals.FormCheck("RUN_JOBS") == 1)
            {
                InitializeComponent();
            }
            else
            {
                //this.BeginInvoke(new MethodInvoker(this.Close));
                //this.DialogResult = System.Windows.Forms.DialogResult.Cancel;
                MessageBox.Show("You do not have permission to access this form!");
                //this.Close();
                this.Dispose();
            }            
        }

EDIT It looks like I am going to go with Adriano Repetti's idea of putting the security where I call the page, but I am a little nervous now having any security on the page.

   private void btn_RunPkgs_Click(object sender, EventArgs e)
        {
            if (MyGlobals.FormCheck("RUN_JOBS") == 1)
            {
                RunPackages rp = new RunPackages();
                this.Hide();
                rp.ShowDialog();
                this.Show();                
            }
            else
            {
                MessageBox.Show("Not for You!");             
            }
        }

        private void btn_ListUpdater_Click(object sender, EventArgs e)
        {
            if (MyGlobals.FormCheck("MDM") == 1)
            {
                ListUpdater lu = new ListUpdater();

                this.Hide();
                lu.ShowDialog();
                this.Show();
            }
            else
            {
                MessageBox.Show("Private!");
            }            
        }

EDIT2 Came up with the following possible solution but am nervous to use it because I am new at this and don't know what issues there might be. Any problems with just creating an event handler for form load?

namespace RunPackages
{
    public partial class ListUpdater : Form
    {
        public ListUpdater()
        {            
            InitializeComponent();
            this.Load += new EventHandler(securityCheck);
        }    

        private void securityCheck(object sender, EventArgs e)
        {
            if (MyGlobals.FormCheck("MDM1") == 0)
            {
                MessageBox.Show("Not Allowed!");
                this.Close();
            }    
        }

You can't dispose of the form within the form itself. The ShowDialog() method tries to access the form on exit for things such as DialogResult .

After a form has been disposed almost all of its methods can't be accessed (and most of its properties are invalid).

In your first line of btn_RunPkgs_Click() you create an object and you dispose it inside its constructor. Per se , even if pretty bad habit you may even call Dispose() from within constructor, it may even work but then you try to use such object ShowDialog() will generate ObjectDisposedException . Note that this code will also lead to same result (an exception):

RunPackages rp = new RunPackages();
rp.Dispose();

Yes you may check IsDisposed but that won't make code readable and problem (IMO) is you're mixing things. Constructor shouldn't contain such logic.

The point isn't just where you dispose your form. What's better is to don't even create such form (let me assume, because you call InitializeComponent() , that securityCheck() is called inside form constructor), for this you may use a factory static method:

public static bool TryShowDialog(Form currentForm)
{
    if (MyGlobals.FormCheck("RUN_JOBS") != 1)
        return false;

    if (currentForm != null)
        currentForm.Hide();

    RunPackages dlg = new RunPackages();
    dlg.ShowDialog();   

    if (currentForm != null)
        currentForm.Show();

    return true;
}

Your calling function will then be reduced to:

private void btn_RunPkgs_Click(object sender, EventArgs e)
{
    RunPackages.TryShowDialog(this);
}

Note that such function is highly eligible for some refactoring (for example to extract code to hide/show existing form). Something like this:

public static bool ShowDialog<T>(Form currentForm, string authorizationId)
    where T : Form, new()
{
    if (MyGlobals.FormCheck(authorizationId) != 1)
        return false;

    if (currentForm != null)
        currentForm.Hide();

    T dlg = new T();
    T.ShowDialog();   

    if (currentForm != null)
        currentForm.Show();

    return true;
}

Used like this (now code is reused everywhere):

    SecurityHelpers.ShowDialog<RunPackages>(this, "RUN_JOBS");

Please note that calling code may be simplified ( authorizationId may be an attribute on RunPackages , for example, and also currentForm can be deduced from current active form).

EDIT Calling Close() isn't better, if window handle has not been created (let's simplify little bit: it's created when window is shown) internally it'll call Dispose() (then above applies).

I would not try to disrupt the chaining of events that lead to the form creation.
The side effects are difficult to predict and what works today could not work in future versions.

Instead I would try a different approach

    private void securityCheck()
    {
        if (MyGlobals.FormCheck("RUN_JOBS") == 1)
        {
            InitializeComponent();
        }
        else
        {
            Label message = new Label();
            message.Dock = DockStile.Fill;
            message.Text("You do not have permission to access this form!.");
            message.TextAlign = ContentAlignment.MiddleCenter;
            this.Controls.Add(message);
        }      
    }

In this way I let the form show with just one label that covers the entire form surface with your message. The user could only close the form (provided that you have not removed the Control Box)

By the way, this has the advantage of avoiding dangerous oversights because it doesn't require any change on the calling code and the final effect is to effectively block the use of the form.

If you insist in closing the form during its constructor phase then you could get some advices from this question

I came up with the following, can anyone tell me if there are any issues with this?

namespace RunPackages
{
    public partial class ListUpdater : Form
    {

        public ListUpdater()
        {            
            InitializeComponent();
            this.Load += new EventHandler(securityCheck);
        }    

        private void securityCheck(object sender, EventArgs e)
        {
            if (MyGlobals.FormCheck("MDM1") == 0)
            {
                MessageBox.Show("Not allowed!");
                this.Close();
            }    
        }

etc...

Use a flag. For example change the code, like this:

    public bool IsDisposed;
    private void securityCheck()
            {
                if (MyGlobals.FormCheck("RUN_JOBS") == 1)
                {
                    InitializeComponent();
                }
                else
                {
                    //this.BeginInvoke(new MethodInvoker(this.Close));
                    //this.DialogResult = System.Windows.Forms.DialogResult.Cancel;
                    MessageBox.Show("You do not have permission to access this form!");
                    //this.Close();
                    this.Dispose();
                    this.IsDisposed = true;
                }      


        }

Then:

private void btn_RunPkgs_Click(object sender, EventArgs e)
        {
            RunPackages rp = new RunPackages();    
            if(rp.IsDisposed)
                return;
            this.Hide();
            rp.ShowDialog();//The error points to this line
            this.Show();
        }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM