开发者

ObjectDisposedException from core .NET code

I'm having this issue with a live app.

(Unfortunately this is post-mortem debugging - I only have this stack trace. I've never seen this personally, nor am I able to reproduce).

I get this Exception:

message=Cannot access a disposed object.
Object name: 'Button'.
exceptionMessage=Cannot access a disposed object.
Object name: 'Button'.
exceptionDetails=System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'Button'.
   at System.Windows.Forms.Control.CreateHandle()
   at System.Windows.Forms.Control.get_Handle()
   at System.Windows.Forms.Control.PointToScreen(Point p)
   at System.Windows.Forms.Button.OnMouseUp(MouseEventArgs mevent)
   at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
   at System.Windows.Forms.Control.WndProc(Message& m)
   at System.Windows.Forms.ButtonBase.WndProc(Message& m)
   at System.Windows.Forms.Button.WndProc(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
   at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
exceptionSource=System.Windows.Forms
exceptionTargetSite=Void CreateHandle()

It looks like a mouse event is arriving at a form after the form has been disposed.

Note there is none of my code in this stack trace.

The only weird (?) thing I'm doing, is that I do tend to Dispose() Forms quite aggressively when I use them with ShowModal() (see "Aside" below).

EDIT: Just to clarify, I'm using C++-CLI so actually I don't call Dispose() I use the delete operator. This is the same as calling Dispose(), however.

But I only do this after ShowModal() has returned (that should be safe right?), and only when I'm done with the form.

I think I read that events might be queued up in the event queue, but I can't believe this would be the problem. I mean surely the framework must be tolerant to old mess开发者_高级运维ages? I can well imagine that under stress messages might back-log and surely the window might go away at any time?

Any ideas?

If you could even suggest ways of reproducing, that might be useful.

John


Aside:

TBH I've never quite understood whether calling Dispose() after Form.ShowDialog() is strictly necessary - the MSDN docs for ShowDialog() are to my mind a bit ambiguous.


This happens if you show a form after disposing it. (I tried it)

After calling ShowDialog, you should dispose the form, but only if you don't plan to do anything else with that instance.


This is a very strange call stack. The button got disposed, its PointToScreen() method is recreating the handle. But it shouldn't have been able to get the mouse-up message if it was disposed. Only threading can really explain this.

Furthermore, nothing should have been disposed yet by the time the mouse-up message arrives. Presumably this is a button on the dialog that closes it. Make sure you use the Click event, not the MouseDown event. Also make sure you close the dialog by assigning its DialogResult property, not by calling Close(). Awkward in C++/CLI because it doesn't keep separate separate symbol tables for types and variables.

Ask the user what kind of "enhancements" she's got running on that machine.


I found your question while diagnosing a similarly odd stack trace in an application of mine. Here's the stack trace that I had to work with:

System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'TextBox'.
  at System.Windows.Forms.Control.CreateHandle()
  at System.Windows.Forms.TextBoxBase.CreateHandle()
  at System.Windows.Forms.Control.get_Handle()
  at System.Windows.Forms.Control.set_CaptureInternal(Boolean value)
  at System.Windows.Forms.Control.WmMouseDown(Message& m, MouseButtons button, Int32 clicks)
  at System.Windows.Forms.Control.WndProc(Message& m)
  at System.Windows.Forms.TextBoxBase.WndProc(Message& m)
  at System.Windows.Forms.TextBox.WndProc(Message& m)
  at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
  at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
  at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)

It's not the same as yours, but it has some of the same key characteristics:

  • The handle exists at the beginning, but not at the end of the stack trace.
  • None of my code is visible in the stack trace.

I don't think my problem is the same is yours, but I thought I'd share what I discovered in the hopes that it can give you some insight. After some experimentation, I was able to reproduce my problem. I had hooked the LostFocus event on a different control and in certain situations the LostFocus event handler would delete certain controls that were no longer relevant.

However, if the LostFocus event was triggered because the user clicked on one of the controls to be deleted, I get the stack trace above. In my case, Control.WndProc calls Control.WmKillFocus which eventually calls my LostFocus event handler (of a different control), I dispose of the control that was clicked, and then Control.WmMouseDown gets called.

Might you have a similar situation occurring, where something is being triggered before WmMouseUp?

Using .NET Reflector to see which events might be called before WmMouseUp might help you track down the problem.


I was having this problem with a subclass of Button I had written. For me the solution turned out to be to check the button's IsDisposed property between my call to base.OnMouseDown and the rest of the code in the method.

Something like this:

protected override void OnMouseUp(System.Windows.Forms.MouseEventArgs mevent)
{
    base.OnMouseUp(mevent);

    if (this.IsDisposed) {
        return;
    }
}


Why don't you use your form instanciation inside a using statement ? This would avoid the need to call dispose, and ensure you it's done at the correct time.

e.g. (not tested, don't have access to a compiler right now)

using(FormX frm = new FormX())
{
   DialogResult res = frm.ShowDialog();
   // Do your other stuff after
}


I had the same problem when I called Close() method on button click for non-modal form. Debugging of winforms assembly led me to button's FlatStyle property. Did you have button.FlatSyle = FlatStyle.System?

For some reason sometimes button doesn't recive WM_KILLFOCUS (OnLostFocus) during form closing and disposing. And if you have button.FlatSyle = FlatStyle.System, it can cause to ObjectDisposedException with such callstack.


It looks like a mouse event is arriving at a form after the form has been disposed.

Yes indeed! The exception clearly says 'Cannot access a disposed object, object name: button' which means the button which was already disposed is again under the pressure of disposal, hence the exception. So based on the stack trace provided by you if you observe the below line it is evident that the second repetitive dispose on already disposed button occured during the OnMouseUp event of button.

  at System.Windows.Forms.Button.OnMouseUp(MouseEventArgs mevent)

So I suggest to create a custom button that inherits System.Windows.Forms.Button control and override the OnMouseUp event wherein you prevent the repetitive dispose based on IsDisposed property of Button as below.

public class FlatSylteSystemButton : System.Windows.Forms.Button
{
    public FlatStyleSystemButton()
    {
           this.FlatStyle =FlatStyle.System;
    }

    protected override void OnMouseUp(MouseEventArgs mevent)
    {
        if(!this.IsDisposed)
        {
           base.OnMouseUp(mevent);
        }
    }
}

Hope this helps!

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜