Threading problem after migration from 2003 to 2005

J

Jeroen

We converted our decently large (13 projects, compiled about 12 mb)
VStudio 2003/.Net1.1 project to VStudio 2005/.Net2. My two colleagues
have no problem starting the new solution, but I get an exception
thrown at me. I only get the exception if I run in debug mode, in
release mode all works fine. The exception thrown somewhere along
startup sequence is:
[ InvalidOperationException ]
Cross-thread operation not valid: Control '' accessed from a thread other than the thread it was
created on.

The following snippet shows where the exception is thrown:

----------------------------------------------------------------------
public class PleaseWait : System.Windows.Forms.Form
{
private static PleaseWait fWindow = null;
public static void ShowWindow(string message)
{
if(fWindow == null)
{
fWindow = new PleaseWait();
fWindow.Owner = System.Windows.Forms.Form.ActiveForm;
}
// long i = 0;
// while (i++ < 40000000) ;

fWindow.Show();

// ...etcetera
}
}
--------------------------------------------------------------------------

The ShowWindow method is called from somewhere else in the application
during startup. If I uncomment the counter and let it wait for a
while, no exception is thrown. No exception is thrown either if I
break the code just before the Show instruction and wait a few secs.

I have found articles on this but the problem is I cannot distill a
resolution for the mystery the above poses to me. Can anyone provide
an explanation of what's happening with the above info?
 
A

Andy

We converted our decently large (13 projects, compiled about 12 mb)
VStudio 2003/.Net1.1 project to VStudio 2005/.Net2. My two colleagues
have no problem starting the new solution, but I get an exception
thrown at me. I only get the exception if I run in debug mode, in
release mode all works fine. The exception thrown somewhere along
startup sequence is:
[ InvalidOperationException ]
Cross-thread operation not valid: Control '' accessed from a thread other than the thread it was
created on.

The following snippet shows where the exception is thrown:

----------------------------------------------------------------------
public class PleaseWait : System.Windows.Forms.Form
{
private static PleaseWait fWindow = null;
public static void ShowWindow(string message)
{
if(fWindow == null)
{
fWindow = new PleaseWait();
fWindow.Owner = System.Windows.Forms.Form.ActiveForm;
}
// long i = 0;
// while (i++ < 40000000) ;

fWindow.Show();

// ...etcetera
}}

--------------------------------------------------------------------------

The ShowWindow method is called from somewhere else in the application
during startup. If I uncomment the counter and let it wait for a
while, no exception is thrown. No exception is thrown either if I
break the code just before the Show instruction and wait a few secs.

I have found articles on this but the problem is I cannot distill a
resolution for the mystery the above poses to me. Can anyone provide
an explanation of what's happening with the above info?

Its only fine in release mode because of luck. Controls have thread
affinity, and accessing ANY property or method on a control on a
thread other than the one it was created with will cause problems.

To resolve this problem, check out the Control.Invoke method and
Control.InvokeRequired. You'll see a pattern that will help you fix
the problems.

The reason your colleagues are not getting the exception is because
there's a setting in VS that will turn off that particular exception.
I recommend you have them turn that option ON, because you WILL have
odd problems if you don't fix the offending code.

I assume the code you posted is the one running in a new thread; I
don't see a way for you to NOT run that on the UI thread, especially
without seeing the other code.

HTH
Andy
 
I

Ignacio Machin \( .NET/ C# MVP \)

Hi,

First of all I see no threading in that piece of code, from where are you
calling this?
IS this beign created in another thread?

Also worth of mention is that declaring a form static and messing with the
owner is not something I advise to do.

Jeroen said:
We converted our decently large (13 projects, compiled about 12 mb)
VStudio 2003/.Net1.1 project to VStudio 2005/.Net2. My two colleagues
have no problem starting the new solution, but I get an exception
thrown at me. I only get the exception if I run in debug mode, in
release mode all works fine. The exception thrown somewhere along
startup sequence is:
[ InvalidOperationException ]
Cross-thread operation not valid: Control '' accessed from a thread
other than the thread it was
created on.

The following snippet shows where the exception is thrown:

----------------------------------------------------------------------
public class PleaseWait : System.Windows.Forms.Form
{
private static PleaseWait fWindow = null;
public static void ShowWindow(string message)
{
if(fWindow == null)
{
fWindow = new PleaseWait();
fWindow.Owner = System.Windows.Forms.Form.ActiveForm;
}
// long i = 0;
// while (i++ < 40000000) ;

fWindow.Show();

// ...etcetera
}
}
--------------------------------------------------------------------------

The ShowWindow method is called from somewhere else in the application
during startup. If I uncomment the counter and let it wait for a
while, no exception is thrown. No exception is thrown either if I
break the code just before the Show instruction and wait a few secs.

I have found articles on this but the problem is I cannot distill a
resolution for the mystery the above poses to me. Can anyone provide
an explanation of what's happening with the above info?
 
B

Brian Gideon

We converted our decently large (13 projects, compiled about 12 mb)
VStudio 2003/.Net1.1 project to VStudio 2005/.Net2. My two colleagues
have no problem starting the new solution, but I get an exception
thrown at me. I only get the exception if I run in debug mode, in
release mode all works fine. The exception thrown somewhere along
startup sequence is:
[ InvalidOperationException ]
Cross-thread operation not valid: Control '' accessed from a thread other than the thread it was
created on.

The following snippet shows where the exception is thrown:

----------------------------------------------------------------------
public class PleaseWait : System.Windows.Forms.Form
{
private static PleaseWait fWindow = null;
public static void ShowWindow(string message)
{
if(fWindow == null)
{
fWindow = new PleaseWait();
fWindow.Owner = System.Windows.Forms.Form.ActiveForm;
}
// long i = 0;
// while (i++ < 40000000) ;

fWindow.Show();

// ...etcetera
}}

--------------------------------------------------------------------------

The ShowWindow method is called from somewhere else in the application
during startup. If I uncomment the counter and let it wait for a
while, no exception is thrown. No exception is thrown either if I
break the code just before the Show instruction and wait a few secs.

I have found articles on this but the problem is I cannot distill a
resolution for the mystery the above poses to me. Can anyone provide
an explanation of what's happening with the above info?

Like Andy said it was working in 1.1 by accident. VS 2005 adds the
managed debugging assistant (MDA) to detect this common problem in
debug builds. Strategically placed calls to Control.Invoke or
Control.BeginInvoke may fix the problem. It's been my experience that
some threading problems require significant architectural changes to
fix so let's hope that's not the case here since you have a large
application.
 
J

Jeroen

Thanks a lot for the response. I'm a little bit further in
understanding this problem. However, it's still not all clear to me;
I'm especially having trouble with the story on "Invoke". The main
problem is that I didn't really get what it was for, until I read the
msdn:
Note: This method is new in the .NET Framework version 2.0.
...
Provides access to properties and methods exposed by an object.
...
Remarks
This method is for access to managed classes from unmanaged
code and should not be called from managed code. For more
information about IDispatch::Invoke, see the MSDN Library.

My first problem is that some folks here and on blogs explain the
exception I'm getting just as well could have happened on 2003, and
that it's coincidence it only occurs now. Still, "Invoke" wasn't added
until .Net2.0!?

Secondly, I'm only working in managed code, so the msdn remark even
seems to imply I don't need "Invoke".

Any thoughts?
 
M

Marc Gravell

Wrong Invoke... the correct Invoke here is Control.Invoke, not to be
confused with Delegate.Invoke or IDispatch.Invoke.

This is not new to 2.0, relates to managed code, and although you
*should* have been using it prior to 2.0, the system let you get away
with some [ab]use; from 2.0 it checks a bit more rigorously. This can
be disabled, but I *really* don't recommend it. It is doing this for a
very good reason.

It's purpose is to push a message onto the Form's message pump so that
a unit of work can be done by the UI thread (which has access to the
Controls).

Marc
 
B

Brian Gideon

Thanks a lot for the response. I'm a little bit further in
understanding this problem. However, it's still not all clear to me;
I'm especially having trouble with the story on "Invoke". The main
problem is that I didn't really get what it was for, until I read the
msdn:

I was talking about Control.Invoke which implements
ISynchronizeInvoke.Invoke and has been available since 1.0.
My first problem is that some folks here and on blogs explain the
exception I'm getting just as well could have happened on 2003, and
that it's coincidence it only occurs now. Still, "Invoke" wasn't added
until .Net2.0!?

No, the exact exception you're getting could not be thrown in 1.1
because it is generated from an MDA which is available starting with
2.0. Like Marc said you can disable the MDA, but I too do not
recommend that.

In versions prior to 2.0 you could see a variety of exceptions or no
exception at all. You might observe that the application runs
correctly 99% of time and crashes only intermittently. Different
hardware configuration could make the crash more or less likely. In
the past I've seen a big red X drawn in the place of a control as a
symptom. Either way the problems will be unpredicable.
Secondly, I'm only working in managed code, so the msdn remark even
seems to imply I don't need "Invoke".

One thing I can say for certain is that you absolutely cannot access a
Form or Control from a thread other than the main UI thread.
Control.Invoke is usually the best option if you want a worker thread
to initiate some kind of UI activity.
 
M

Marc Gravell

Control.Invoke is usually the best option if you want a worker thread
to initiate some kind of UI activity.

Just a minor point; for those unfamilair with cross-threading an UI,
the BackgroundWorker component can serve as a gentle introduction; the
background code calls ReportProgress, and *separately* your UI handles
the ProgressChanged event - usually updating a ProgressBar; this
handles all the thread-switching for you, similar to how
Windows.Forms.Timer works.

Of course, once you get the hang of it, direct use of Control.Invoke
offers a lot more flexibility ;-p

Marc
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top