J
Jim H
I have a form that has a bunch of events triggered from worker threads. The
event handlers need to update the gui so if it's a worker thread I call
this.Invoke from inside my Form's event handler code. When I close the app
I get the following exception:
System.ObjectDisposedException was unhandled
Message="Cannot access a disposed object.\r\nObject name: 'MainForm'."
Source="System.Windows.Forms"
ObjectName="MainForm"
StackTrace:
at System.Windows.Forms.Control.MarshaledInvoke(Control caller,
Delegate method, Object[] args, Boolean synchronous)
at System.Windows.Forms.Control.Invoke(Delegate method, Object[]
args)
at SniperScope.MainForm.OnPidDataArrived(Object source,
DataLoggerEventArgs e) in F:\code\Logger VS8
**** more underlying calls in the stack but this is the meat****
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing ); <-- main thread is here when exception occurs
}
The offending code:
public void OnPidDataArrived(object source, DataLoggerEventArgs e)
{
object[] loaArgs = new object[1];
loaArgs[0] = e.PIDResults;
if (this.Disposing == false)
{
//check to see if this is the calling thread
//gui operations should only happen in the calling thread
if (this.InvokeRequired)
this.Invoke(m_UIGaugeDelegate, loaArgs); <-- BOOM!!!!!!
Exception when app is closing
else//this is the thread that owns the control handle
OnUpdateGauges(e.PIDResults);
}
}
m_UIGaugeDelegate is a delegate for the OnUpdateGauges method.
I am checking Disposing in the gui operation call that happens on the main
thread as well (OnUpdateGauges)
The problem appears to be that Disposed is called between the time that
OnPidDataArrived checks the value of Disposing and the call to
this.Invoke(). (Actually each time the debugger comes up after the
exception this.InvokeRequired is false) In the debugger the main thread is
currently executing base.Dispose() from my Dispose() code and the worker is
stopped at this.Invoke().
I can't figure a way around this. If I try to use a lock, or some kind of
thread sync object, in Dispose() and this method it dead locks because the
main thread is blocked on the lock in Dispose waiting for the worker to
release the lock, so the Invoke call never comes back in the worker thread.
I running VS.NET 2005 and .NET2.0 and ported the app over from .NET1.1. The
condition did not seem to happen in .NET1.1 and VS.NET 2003, but it was
mostly run on a single processor laptop. My workstation is a dual Xeon
processor machine. This might be exaggerating the issue. I can duplicate
the problem EVERY single time I try to close the app. I haven't run it on
my notebook. (Something in my code is broken and running a single processor
is not a fix.)
How can I synchronize this? I have been wrestling this for a while. Any
ideas would be greatly appreciated.
Thanks,
jim
event handlers need to update the gui so if it's a worker thread I call
this.Invoke from inside my Form's event handler code. When I close the app
I get the following exception:
System.ObjectDisposedException was unhandled
Message="Cannot access a disposed object.\r\nObject name: 'MainForm'."
Source="System.Windows.Forms"
ObjectName="MainForm"
StackTrace:
at System.Windows.Forms.Control.MarshaledInvoke(Control caller,
Delegate method, Object[] args, Boolean synchronous)
at System.Windows.Forms.Control.Invoke(Delegate method, Object[]
args)
at SniperScope.MainForm.OnPidDataArrived(Object source,
DataLoggerEventArgs e) in F:\code\Logger VS8
**** more underlying calls in the stack but this is the meat****
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing ); <-- main thread is here when exception occurs
}
The offending code:
public void OnPidDataArrived(object source, DataLoggerEventArgs e)
{
object[] loaArgs = new object[1];
loaArgs[0] = e.PIDResults;
if (this.Disposing == false)
{
//check to see if this is the calling thread
//gui operations should only happen in the calling thread
if (this.InvokeRequired)
this.Invoke(m_UIGaugeDelegate, loaArgs); <-- BOOM!!!!!!
Exception when app is closing
else//this is the thread that owns the control handle
OnUpdateGauges(e.PIDResults);
}
}
m_UIGaugeDelegate is a delegate for the OnUpdateGauges method.
I am checking Disposing in the gui operation call that happens on the main
thread as well (OnUpdateGauges)
The problem appears to be that Disposed is called between the time that
OnPidDataArrived checks the value of Disposing and the call to
this.Invoke(). (Actually each time the debugger comes up after the
exception this.InvokeRequired is false) In the debugger the main thread is
currently executing base.Dispose() from my Dispose() code and the worker is
stopped at this.Invoke().
I can't figure a way around this. If I try to use a lock, or some kind of
thread sync object, in Dispose() and this method it dead locks because the
main thread is blocked on the lock in Dispose waiting for the worker to
release the lock, so the Invoke call never comes back in the worker thread.
I running VS.NET 2005 and .NET2.0 and ported the app over from .NET1.1. The
condition did not seem to happen in .NET1.1 and VS.NET 2003, but it was
mostly run on a single processor laptop. My workstation is a dual Xeon
processor machine. This might be exaggerating the issue. I can duplicate
the problem EVERY single time I try to close the app. I haven't run it on
my notebook. (Something in my code is broken and running a single processor
is not a fix.)
How can I synchronize this? I have been wrestling this for a while. Any
ideas would be greatly appreciated.
Thanks,
jim