来自核心.NET代码的ObjectDisposedException

时间:2010-05-10 15:27:33

标签: .net forms c++-cli idisposable

我在使用实时应用时出现此问题。

(不幸的是,这是事后调试 - 我只有这个堆栈跟踪。我从来没有见过这个,我也没有能够重现。)

我得到了这个例外:

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()

在表单处理完毕后,看起来鼠标事件正在到达表单。

请注意,此堆栈跟踪中没有任何代码。

我正在做的唯一奇怪的(?)事情是,当我将它与ShowModal()一起使用时,我确实倾向于使用Dispose()表格(参见下面的“Aside”)。

编辑:为了澄清,我正在使用C ++ - CLI,所以实际上我没有调用Dispose()我使用了delete运算符。这与调用Dispose()相同。

但我只在ShowModal()返回后才这样做(这应该是安全的吗?),并且只有当我完成表单时才会这样做。

我想我读过可能在事件队列中排队的事件,但我不敢相信这会是问题所在。我的意思是框架必须能够容忍旧消息吗?我可以想象,在压力下,消息可能会重新记录,当然窗口可能会随时消失?

有什么想法吗?

如果您甚至可以建议复制方法,那可能会有用。

约翰


除了:

TBH我从来没有完全理解在Form.ShowDialog()之后调用Dispose()是否是绝对必要的 - 对于ShowDialog()的MSDN文档在我看来有点含糊不清。

7 个答案:

答案 0 :(得分:1)

如果您在处置后显示表单,则会发生这种情况。 (我试过了)

调用ShowDialog后,您应该处理该表单,但前提是您不打算对该实例执行任何其他操作。

答案 1 :(得分:1)

这是一个非常奇怪的调用堆栈。按钮被丢弃,其PointToScreen()方法正在重新创建句柄。但如果它被处理掉它应该不能得到鼠标消息。只有线程才能真正解释这一点。

此外,在鼠标悬停消息到达时尚未处理任何内容。据推测,这是关闭它的对话框上的一个按钮。确保使用Click事件,而不是MouseDown事件。还要确保通过分配DialogResult属性来关闭对话框,而不是通过调用Close()来关闭对话框。在C ++ / CLI中很尴尬,因为它没有为类型和变量保留单独的单独符号表。

询问用户在该计算机上运行了哪种“增强功能”。

答案 2 :(得分:1)

我在我的应用程序中诊断出类似的奇怪堆栈跟踪时发现了你的问题。这是我必须使用的堆栈跟踪:

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)

它与你的不一样,但它有一些相同的关键特征:

  • 句柄存在于开头,但不存在于堆栈跟踪的末尾。
  • 我的代码在堆栈跟踪中都不可见。

我认为我的问题与你的问题不一样,但我认为我会分享我发现的东西,希望能给你一些见解。经过一些实验,我能够重现我的问题。我已将LostFocus事件挂钩到不同的控件上,在某些情况下,LostFocus事件处理程序会删除不再相关的某些控件。

但是,如果由于用户点击了要删除的其中一个控件而触发了LostFocus事件,那么我将获得上面的堆栈跟踪。在我的例子中,Control.WndProc调用Control.WmKillFocus,它最终调用我的LostFocus事件处理程序(不同的控件),我处理被单击的控件,然后调用Control.WmMouseDown。

你可能会发生类似情况,在WmMouseUp之前触发某些事情吗?

使用.NET Reflector查看在WmMouseUp之前可能会调用哪些事件可能会帮助您追踪问题。

答案 3 :(得分:1)

我遇到了一个我写过的Button子类的问题。对我来说,解决方案原来是检查我对base.OnMouseDown的调用与方法中其余代码之间的按钮的IsDisposed属性。

这样的事情:

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

    if (this.IsDisposed) {
        return;
    }
}

答案 4 :(得分:0)

为什么不在using声明中使用表单实例?这样可以避免调用dispose,并确保在正确的时间完成。

e.g。 (未经测试,目前无法访问编译器)

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

答案 5 :(得分:0)

当我在按钮点击时为非模态表单调用Close()方法时,我遇到了同样的问题。调试winforms程序集使我进入了按钮的FlatStyle属性。你有button.FlatSyle = FlatStyle.System吗?

由于某些原因,有时按钮在表单关闭和处理期间不会重新显示WM_KILLFOCUS(OnLostFocus)。如果您有button.FlatSyle = FlatStyle.System,则可能导致ObjectDisposedException使用此类调用。

答案 6 :(得分:0)

  

在表单处理完毕后,看起来鼠标事件正在到达表单。

确实是的!例外情况清楚地表明“无法访问已处置的物体,物体名称:按钮”这意味着已经处置的按钮再次处于处置压力之下,因此例外。因此,如果您观察到下面的行,基于您提供的堆栈跟踪,很明显在按钮的OnMouseUp事件期间发生了已经处置按钮的第二次重复处理。

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

所以我建议创建一个继承System.Windows.Forms.Button控件的自定义按钮,并覆盖OnMouseUp事件,其中根据Button的IsDisposed属性阻止重复处理,如下所示。

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);
        }
    }
}

希望这有帮助!