来自核心 .NET 代码的 ObjectDisposeException
我在使用实时应用程序时遇到了这个问题。
(不幸的是,这是事后调试 - 我只有这个堆栈跟踪。我个人从未见过这个,也无法重现)。
我收到此异常:
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()
看起来鼠标事件在表单被处理后到达表单。
请注意,此堆栈跟踪中没有我的代码。
我正在做的唯一奇怪(?)的事情是,当我将 Dispose() 表单与 ShowModal() 一起使用时,我确实倾向于非常积极地使用它们(请参阅下面的“旁白”)。
编辑:只是为了澄清一下,我正在使用 C++-CLI,所以实际上我不调用 Dispose() 我使用删除运算符。不过,这与调用 Dispose() 相同。
但我只在 ShowModal() 返回后才执行此操作(这应该是安全的,对吧?),并且仅当我完成表单时才执行此操作。
我想我读到事件可能会在事件队列中排队,但我不敢相信这会是问题所在。我的意思是框架肯定必须能够容忍旧消息?我完全可以想象,在压力下,消息可能会积压,并且窗口肯定会随时消失?
有什么想法吗?
如果您甚至可以建议复制方法,那可能会有用。
John
Aside:
说实话,我一直不太明白在 Form.ShowDialog() 之后调用 Dispose() 是否是绝对必要的 - 在我看来,ShowDialog() 的 MSDN 文档有点含糊。
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 messages? 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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
如果您在处理表单后显示该表单,则会发生这种情况。 (我尝试过)
调用
ShowDialog
后,您应该处理该表单,但前提是您不打算对该实例执行任何其他操作。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.这是一个非常奇怪的调用堆栈。按钮已被释放,其 PointToScreen() 方法正在重新创建句柄。但如果它被释放,它不应该能够获得鼠标释放消息。只有线程才能真正解释这一点。
此外,当鼠标松开消息到达时,应该还没有处理任何内容。据推测,这是对话框上用于关闭对话框的按钮。确保使用 Click 事件,而不是 MouseDown 事件。还要确保通过分配对话框的 DialogResult 属性而不是调用 Close() 来关闭对话框。在 C++/CLI 中很尴尬,因为它没有为类型和变量保留单独的符号表。
询问用户她在那台机器上运行了什么样的“增强功能”。
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.
我在我的应用程序中诊断类似奇怪的堆栈跟踪时发现了您的问题。这是我必须使用的堆栈跟踪:
它与您的不同,但它具有一些相同的关键特征:
我不认为我的问题和你的一样,但我想我会分享我的发现,希望它能给你一些见解。经过一些实验,我能够重现我的问题。我已将 LostFocus 事件挂接到不同的控件上,在某些情况下,LostFocus 事件处理程序会删除不再相关的某些控件。
但是,如果由于用户单击要删除的控件之一而触发 LostFocus 事件,我会得到上面的堆栈跟踪。就我而言,Control.WndProc 调用 Control.WmKillFocus,后者最终调用我的 LostFocus 事件处理程序(不同控件的),我处理被单击的控件,然后调用 Control.WmMouseDown。
您是否也遇到过类似的情况,即在 WmMouseUp 之前触发某些内容?
使用 .NET Reflector 查看在 WmMouseUp 之前可能调用哪些事件可能会帮助您找到问题。
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:
It's not the same as yours, but it has some of the same key characteristics:
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.
我编写的 Button 子类遇到了这个问题。对我来说,解决方案是在调用 base.OnMouseDown 和方法中的其余代码之间检查按钮的 IsDispose 属性。
像这样的事情:
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:
为什么不在
using
语句中使用表单实例?这将避免调用 dispose 的需要,并确保它在正确的时间完成。例如(未经测试,现在无法访问编译器)
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)
当我在非模态表单的按钮单击上调用
Close()
方法时,我遇到了同样的问题。 winforms 程序集的调试使我找到了按钮的FlatStyle
属性。您有button.FlatSyle = FlatStyle.System
吗?由于某种原因,有时按钮在表单关闭和处理期间不会接收到
WM_KILLFOCUS
(OnLostFocus)。如果您有button.FlatSyle = FlatStyle.System
,它可能会导致使用此类调用堆栈的ObjectDisposeException
。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'sFlatStyle
property. Did you havebutton.FlatSyle = FlatStyle.System
?For some reason sometimes button doesn't recive
WM_KILLFOCUS
(OnLostFocus) during form closing and disposing. And if you havebutton.FlatSyle = FlatStyle.System
, it can cause toObjectDisposedException
with such callstack.确实是的!该异常清楚地表明“无法访问已处置的对象,对象名称:按钮”,这意味着已处置的按钮再次受到处置的压力,因此出现异常。因此,根据您提供的堆栈跟踪,如果您观察以下行,很明显,在按钮的 OnMouseUp 事件期间发生了对已处置按钮的第二次重复处置。
因此,我建议创建一个继承 System.Windows.Forms.Button 控件的自定义按钮并重写 OnMouseUp 事件,其中您可以根据 Button 的 IsDispose 属性防止重复处置,如下所示。
希望这有帮助!
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.
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.
Hope this helps!