Why is cross thread operation exception not thrown while running exe in bin\Debug
I was debugging an application and somewhere in the code, a thread tries to reach a listbox that was created by another thread. On attempt to access the listbox, the application throws a "Cross-thread operation not valid: Control 'listbox' accessed from a thread other than the thread it was created on" exception while debugging. However, when I run this application's output in bin\Debug folder, I do not get an exception dialog and I can see that the listbox is successfully accessed from the non-owner thread, so this makes me think that there is a behavioral difference here, not just a suppressed exception. I can get past this exception in debugging with the following line in form_load event
Control.CheckForIllegalCrossThreadCalls = false;
But what is the reason behind this different behavior?
Yes, this is only checked when a debugger is attached. This was necessary because there was lots of .NET 1.x code that violated this rule. It is not an obvious one.
The bigger problem is that such code got away with it. Either by luck, not thinking too much about the occasional painting problems or by thinking that aborting the app when it deadlocked and restarting it once a day was acceptable. Because the programmer had no real hope of discovering the problem without a diagnostic.
Microsoft cares a lot about backward compat, even if it is buggy compat. The fix is excellent, even though it is sometimes wrong (Show(owner) is checked when it shouldn't). And sometimes overlooks to check when it is code in the framework that violates the rule. Which happens when the thread dependency is indirect. Most common cases of that are updating the data source of a data-bound control in a worker thread (unbind first!) and using a control that listens for the SystemEvents.UserPreferenceChanged event (don't create UI on a second thread!)
For reference, the relevant code is present in the static constructor of the Control class:
static Control()
{
//...
checkForIllegalCrossThreadCalls = Debugger.IsAttached;
//...
}