Hi Paul,
It's highly likely that we're doing this also - it's just a matter of
finding out where.
I'm sceptical regarding the Finalizer doing work on the UI thread, that
would truly be a horrendous oversight. It could happen if Dispose was
overriden and the wrong value was passed to the base method (ie true when
"disposing" was false), but I doubt this could be the case in any of the
framework classes.
The bug looks very similar to another we've experienced regarding a race
condition in the dispose of the .NET ToolTip class, so it certainly is
possible that other race conditions exist in the framework regarding
disposing without it necessarily being the Finalizer doing work on the UI
thread. It's just a matter of tracking them down.
Regards,
Matt
> Matt,
>
[quoted text clipped - 113 lines]
> > > }
> > > m.Result =
System.Windows.Forms.UnsafeNativeMethods.CallWindowProc(this.defWindowProc,
> > > m.HWnd, m.Msg, m.WParam, m.LParam);
> > > return;
[quoted text clipped - 43 lines]
> > >
> > > 00000000()
system.windows.forms.dll!System.Windows.Forms.NativeWindow.DefWndProc(System
> > ..Windows.Forms.Message m) + 0xde bytes
system.windows.forms.dll!System.Windows.Forms.Control.DefWndProc(System.Wind
> > ows.Forms.Message m) + 0xb bytes
system.windows.forms.dll!System.Windows.Forms.Control.WmUpdateUIState(System
> > ..Windows.Forms.Message m) + 0x3b bytes
system.windows.forms.dll!System.Windows.Forms.Control.WndProc(System.Windows
> > ..Forms.Message m) + 0x718 bytes
system.windows.forms.dll!System.Windows.Forms.GroupBox.WndProc(System.Window
> > s.Forms.Message m) + 0x45 bytes
system.windows.forms.dll!ControlNativeWindow.OnMessage(System.Windows.Forms.
> > Message m) + 0xb bytes
system.windows.forms.dll!ControlNativeWindow.WndProc(System.Windows.Forms.Me
> > ssage m) + 0xbc bytes
system.windows.forms.dll!System.Windows.Forms.NativeWindow.Callback(int
> > > hWnd, int msg, int wparam, int lparam) + 0x30 bytes
system.windows.forms.dll!System.Windows.Forms.NativeWindow.DefWndProc(System
> > ..Windows.Forms.Message m) + 0xde bytes
system.windows.forms.dll!System.Windows.Forms.Control.DefWndProc(System.Wind
> > ows.Forms.Message m) + 0xb bytes
system.windows.forms.dll!System.Windows.Forms.Control.WmUpdateUIState(System
> > ..Windows.Forms.Message m) + 0x3b bytes
system.windows.forms.dll!System.Windows.Forms.Control.WndProc(System.Windows
> > ..Forms.Message m) + 0x718 bytes
system.windows.forms.dll!System.Windows.Forms.ScrollableControl.WndProc(Syst
> > em.Windows.Forms.Message m) + 0x36 bytes
system.windows.forms.dll!System.Windows.Forms.ContainerControl.WndProc(Syste
> > m.Windows.Forms.Message m) + 0x17 bytes
system.windows.forms.dll!System.Windows.Forms.Application.ParkingWindow.WndP
> > roc(System.Windows.Forms.Message m) + 0xe bytes
system.windows.forms.dll!ControlNativeWindow.OnMessage(System.Windows.Forms.
> > Message m) + 0xb bytes
system.windows.forms.dll!ControlNativeWindow.WndProc(System.Windows.Forms.Me
> > ssage m) + 0xbc bytes
system.windows.forms.dll!System.Windows.Forms.NativeWindow.Callback(int
> > > hWnd, int msg, int wparam, int lparam) + 0x30 bytes
system.windows.forms.dll!System.Windows.Forms.NativeWindow.DefWndProc(System
> > ..Windows.Forms.Message m) + 0xde bytes
system.windows.forms.dll!System.Windows.Forms.Control.DefWndProc(System.Wind
> > ows.Forms.Message m) + 0xb bytes
system.windows.forms.dll!System.Windows.Forms.Control.WndProc(System.Windows
> > ..Forms.Message m) + 0x960 bytes
system.windows.forms.dll!System.Windows.Forms.ScrollableControl.WndProc(Syst
> > em.Windows.Forms.Message m) + 0x36 bytes
system.windows.forms.dll!System.Windows.Forms.ContainerControl.WndProc(Syste
> > m.Windows.Forms.Message m) + 0x17 bytes
system.windows.forms.dll!System.Windows.Forms.Application.ParkingWindow.WndP
> > roc(System.Windows.Forms.Message m) + 0xe bytes
system.windows.forms.dll!ControlNativeWindow.OnMessage(System.Windows.Forms.
> > Message m) + 0xb bytes
system.windows.forms.dll!ControlNativeWindow.WndProc(System.Windows.Forms.Me
> > ssage m) + 0xbc bytes
system.windows.forms.dll!System.Windows.Forms.NativeWindow.Callback(int
> > > hWnd, int msg, int wparam, int lparam) + 0x30 bytes
system.windows.forms.dll!System.Windows.Forms.NativeWindow.DefWndProc(System
> > ..Windows.Forms.Message m) + 0xde bytes
system.windows.forms.dll!System.Windows.Forms.Control.DefWndProc(System.Wind
> > ows.Forms.Message m) + 0xb bytes
system.windows.forms.dll!System.Windows.Forms.Control.WndProc(System.Windows
> > ..Forms.Message m) + 0x960 bytes
system.windows.forms.dll!System.Windows.Forms.Label.WndProc(System.Windows.F
> > orms.Message m) + 0xca bytes
system.windows.forms.dll!ControlNativeWindow.OnMessage(System.Windows.Forms.
> > Message m) + 0xb bytes
system.windows.forms.dll!ControlNativeWindow.WndProc(System.Windows.Forms.Me
> > ssage m) + 0xbc bytes
system.windows.forms.dll!System.Windows.Forms.NativeWindow.Callback(int
> > > hWnd, int msg, int wparam, int lparam) + 0x30 bytes
> > > 008d8f32()
> > > user32.dll!GetDC() + 0x72
> > > user32.dll!GetDC() + 0x154
> > > user32.dll!GetParent() + 0x16c
> > > user32.dll!SendMessageW() + 0x49
system.windows.forms.dll!System.Windows.Forms.Control.SendMessage(int
> > msg,
> > > int wparam, int lparam) + 0x42 bytes
system.windows.forms.dll!System.Windows.Forms.Control.WmCreate(System.Window
> > s.Forms.Message m) + 0x45 bytes
system.windows.forms.dll!System.Windows.Forms.Control.WndProc(System.Windows
> > ..Forms.Message m) + 0x387 bytes
system.windows.forms.dll!System.Windows.Forms.Label.WndProc(System.Windows.F
> > orms.Message m) + 0xca bytes
system.windows.forms.dll!ControlNativeWindow.OnMessage(System.Windows.Forms.
> > Message m) + 0xb bytes
system.windows.forms.dll!ControlNativeWindow.WndProc(System.Windows.Forms.Me
> > ssage m) + 0xbc bytes
system.windows.forms.dll!System.Windows.Forms.NativeWindow.Callback(int
> > > hWnd, int msg, int wparam, int lparam) + 0x30 bytes
> > > 008d8f32()
[quoted text clipped - 5 lines]
> > > user32.dll!UserClientDllInitialize() + 0x9eb
> > > user32.dll!CreateWindowExW() + 0x33
system.windows.forms.dll!System.Windows.Forms.UnsafeNativeMethods.CreateWind
> > owEx(int
> > > dwExStyle, string lpszClassName, string lpszWindowName, int style, int x,
[quoted text clipped - 4 lines]
> > 0x36
> > > bytes
system.windows.forms.dll!System.Windows.Forms.NativeWindow.CreateHandle(Syst
> > em.Windows.Forms.CreateParams cp) + 0x247 bytes
> > > system.windows.forms.dll!System.Windows.Forms.Control.CreateHandle() +
> > > 0xd9 bytes
> > > system.windows.forms.dll!System.Windows.Forms.Control.get_Handle() +
> > 0x31
> > > bytes
system.windows.forms.dll!System.Windows.Forms.Control.CreateGraphicsInternal
> > () + 0x8 bytes
system.windows.forms.dll!System.Windows.Forms.Label.get_PreferredWidth()
> > +
> > > 0x57 bytes
> > > system.windows.forms.dll!System.Windows.Forms.Label.AdjustSize() + 0x85
> > > bytes
system.windows.forms.dll!System.Windows.Forms.Label.OnTextChanged(System.Eve
> > ntArgs e) + 0x1b bytes
system.windows.forms.dll!System.Windows.Forms.Control.set_Text(string
> > > value) + 0x6d bytes
> > > myassembly.dll!my.namespace.MyControl.InitializeComponent() Line 647 C#
[quoted text clipped - 53 lines]
> > > > > For me, disposing of the cached controls in my form's Dispose method
> > > > > fixed the problem. Hopefully, it will help you, too.
Paul F. Williams - 01 Feb 2005 16:09 GMT
These cached controls did not get Disposed. Some of the controls still have
handles, especially labels, which create their handles when you set their
Text property. The garbage collector tries to destroy these controls from
the GC thread-- probably at the same time the UI thread is initializing some
other opened Form.
Take the .NET Reflector and look at the implementation of Control.Dispose.
The control was not Disposed properly, so it still has a handle. The garbage
collector will call Dispose(false). Dispose(false) does all sorts of
interesting things if the control has a handle, including posting a WM_CLOSE
message, SetWindowLong, GetWindowLong, GC.SupressFinalize, etc. (through
NativeWindow.ReleaseHandle). From what I recall from my Win32 days, it's a
very bad thing to be doing UI stuff in a non-UI thread.
Once, I did get a Dispose(true) in the GC thread. In fact, it appeared that
the same control was being created on the UI thread and Disposed on the GC
thread at the same time! The error message I got was "Cannot call Dispose
while doing CreateHandle()." This message led me down the path of Disposing
of these cached controls in the UI thread. I've not been able to reproduce
this particular condition.
> Hi Paul,
>
[quoted text clipped - 470 lines]
> method
> > > > > > fixed the problem. Hopefully, it will help you, too.
Matt Garven - 02 Feb 2005 07:13 GMT
Posting a message is ok though - that's not strictly a UI thing, as the
posted message will be handled by the message loop of the UI thread, not the
Finalizer thread.
The next call in Dispose, window.ReleaseHandle() eventually makes its way
into NativeWindow.UnSubclass, which makes the GetWindowLong and
SetWindowLong as you have correctly pointed out. The nIndex parameter is
passed in as -4, which is GWL_WNDPROC. When ReleaseHandle is called by the
control, it will either call SetWindowLong with the value of
previousWindow.windowProc or if there is no previous window (ie
previousWindow is null) it sets it to defWindowProc which is the
DefWindowProc function from user32.dll.
I don't know if this strictly counts as changing the UI from a non-UI
thread, however it does make me slightly suspicious when the errors we're
receiving look like this:
at System.Windows.Forms.UnsafeNativeMethods.CallWindowProc(IntPtr wndProc,
IntPtr hWnd, Int32 msg, IntPtr wParam, IntPtr lParam)
at System.Windows.Forms.NativeWindow.DefWndProc(Message& m)
at System.Windows.Forms.Control.DefWndProc(Message& m)
A NullReferenceException would be what you expect from a bad delegate being
passed into unmanaged code at some time in the past, I suppose.
Also, the errors seem to have the ParkingWindow in the callstack, which
would confirm what you've said regarding controls with no parent, like
labels auto-sizing before the parent has been assigned. The exceptions seem
to often occur during the InitializeComponent method of forms that have
controls that create their handle before the parent has been assigned. I'm
not sure what the implications of this are - does it mean the
ParkingWindow's NativeWindow has at some point been corrupted?
The GC.SuppressFinalize is valid because it is the NativeWindow instance
that is having it's finalization suppressed - this is valid because the
control has explicitly cleaned up the NativeWindow in the controls
finalizer, so there is no need for the finalizer to clean up the
NativeWindow instance.
Another NullReferenceException we've had has a callstack like this:
at System.Windows.Forms.UnsafeNativeMethods.IntDestroyWindow(HandleRef hWnd)
at System.Windows.Forms.UnsafeNativeMethods.DestroyWindow(HandleRef hWnd)
at System.Windows.Forms.NativeWindow.DestroyHandle()
at System.Windows.Forms.Control.DestroyHandle()
at System.Windows.Forms.Control.Dispose(Boolean disposing)
at System.Windows.Forms.ContainerControl.Dispose(Boolean disposing)
at System.Windows.Forms.Form.Dispose(Boolean disposing)
at System.ComponentModel.Component.Dispose()
at System.Windows.Forms.Form.WmClose(Message& m)
at System.Windows.Forms.Form.WndProc(Message& m)
at System.Windows.Forms.ControlNativeWindow.OnMessage(Message& m)
at System.Windows.Forms.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr
wparam, IntPtr lparam)
And I've got another few slightly different ones. I'm reasonably sure
they're related.
Again Paul, thanks for your thoughts on this matter (and excuse my thinking
aloud) - I'll spend more effort looking for places where controls are not
being disposed properly, and see if we're using a cache anywhere in our
application. Other than that, I don't know where to look or how to debug the
situation further (especially as it only appears at client sites).
Regards,
Matt
> These cached controls did not get Disposed. Some of the controls still have
> handles, especially labels, which create their handles when you set their
[quoted text clipped - 16 lines]
> of these cached controls in the UI thread. I've not been able to reproduce
> this particular condition.