2

I often find it's not really specified what exact collection is causing this type of exception. Is that true or should it be obvious? Perhaps I just don't understand how to interpret the exception message properly..

Specifically I wonder about this one. What is the collection it refers to?

The parameter of the event delegate is simply (object sender), and the events raised passes null parameter. Though the class itself that raises the event inherits a list:

public class TimeSerie : List<BarData>

Is it clear here if the "collection" refers to the object that raises the event, or can it be another object? Can it be, say a collection of event handlers to a method that is being dynamically changed? Or would that create a different exception?

    ************** Exception Text **************
System.InvalidOperationException: 
Collection was modified; enumeration operation may not execute.
   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 System.Windows.Forms.Control.Invoke(Delegate method)
   at SomeNameSpace.SomeUserControl.InvokeOnUpdateHistory(Object sender) in D:\SomePath\SomeUserControl.cs:line 5179
   at OtherNameSpace.OtherClass.TimeSerie.HistoryUpdateEventHandler.Invoke(Object sender)

Exception occurs in UserControl:

    public class SomeUserControl 

    private void InvokeOnUpdate(object sender)
    {
    this.Invoke(new GenericInvoker(Method));   // << Exception here!
    }

    private void Method() {...}

EDIT: Added some code. A bit simplified, but think it includes relevant bits.

private void Method() 
{
            if (this.instrument == null) return;  
            UnRegisterTimeSerieHandlers(this.ts);

            this.ts = instrument.DataSeries.GetTimeSerieByInterval(interval);
            if (ts != null)
            { 
                RegisterTimeseriesHandlers(ts);
                ClearAndLoadAllHistory();
            }
}

    private void UnRegisterTimeSerieHandlers(TimeSerie ts)
    {
        if (ts != null)
        {
            ts.TickUpdate -= InvokeUpdateCurrentBar;
            ts.NewBarUpdate -= InvokeUpdateNewBar;
            ts.HistoryUpdate -= InvokeOnUpdateHistory;
            this.ts = null;
        }
    }

    private void RegisterTimeseriesHandlers(TimeSerie ts)
    {
        ts.TickUpdate += InvokeUpdateCurrentBar;
        ts.NewBarUpdate += InvokeUpdateNewBar;
        ts.HistoryUpdate += InvokeOnUpdateHistory;
    }
bretddog
  • 5,411
  • 11
  • 63
  • 111
  • 3
    Show us the definition of `Method`. The root cause of the exception is not occurring on that line. – Ed S. Jan 22 '13 at 23:47
  • Ok, added what I think is relevant. – bretddog Jan 22 '13 at 23:58
  • There is still no code here referencing any collections. `instrument.DataSeries.GetTimeSerieByInterval` is not shown, nor is `ClearAndLoadAllHistory`, both of which sound like methods that would interact with a collection. – RJ Lohan Jan 23 '13 at 00:02
  • I'm not convinced you know what is and is not relevant (don't mean that to be rude, just saying; you have no idea where the problem is). Look at the InnerException property of the top level exception and see if that leads you anywhere. – Ed S. Jan 23 '13 at 00:02
  • @Ed S, I understand, it's just a lot of code so trying to be practical. Thought it could be answered with the info given. The main question is really to understand if it's obvious what is the collection, from the collection message and the immediate code. If I know the source I will find the bug, but the bug is not really the main question. But I'll try to provide more details. – bretddog Jan 23 '13 at 00:09
  • @Ed S; InnerException I can not look at, as this exception is from a popup of debug executable, not from VS. – bretddog Jan 23 '13 at 00:19
  • You can't run this code in the debugger? Why not? – Ed S. Jan 23 '13 at 00:36
  • 1
    @Ed S; I can but that's not where I got the exception. Perhaps it can be reproduced but it's not a frequent exception. But please read my question again, I'm not really asking about the error, but **how and if** the exception message can be interpreted to identify the collection. It's that understanding I wish to have before anything else. – bretddog Jan 23 '13 at 00:41

2 Answers2

5

Yes, the cause of an exception can be quite hard to diagnose when you use Control.Invoke(). The problem is that it catches the exception when it occurs on the UI thread and re-throws it in your worker thread. Necessarily so, your worker thread needs to know that the return value of Invoke() is not usable. The inevitable side-effect is that you lose the Holy Stack Trace that tells you where it blew up and how it got there.

If you can repro the problem when you have a debugger attached then use Debug + Exceptions, tick the Thrown checkbox for CLR Exceptions. The debugger stops when the exception is thrown, giving you a good statement location and a call stack to look at.

If not then do consider using Control.BeginInvoke() instead. This is a fire-and-forget version of Invoke() so if the invoked method throws then that exception will be raised on the UI thread and you'll get an accurate stack trace with it.

In general you always want to favor BeginInvoke(). It doesn't cause the worker thread to stall, it avoids many deadlock scenarios and give good exception feedback. Using Invoke() is generally a mistake.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • Thanks! Was also not aware of BeginInvoke. If I may follow up; What happens if two worker threads call BeginInvoke "at about the same time". Are we ensured the first to initiate will complete fully before starting the next? – bretddog Jan 23 '13 at 01:52
  • 1
    The UI thread can run only one method at the time. These invokes are automatically serialized, first-come first-serve. – Hans Passant Jan 23 '13 at 02:03
0

It seems you're using timers. Maybe you're changing some collection (more likely, the collection is a property of UI element) from the timer callback (another thread) without using Control.Invoke() call?

  • I'm not using timers. But the TimeSeries collection receives update from socket on other thread, which triggers the HistoryUpdateEvent. And the user may scroll with the mouse, each scroll changing TimeSeries object from a list of TimeSeries objects. It's during this scroll the exception occured. – bretddog Jan 23 '13 at 00:14
  • @bretddog, so, the socket thread changes the TimeSeries collection using `Control.Invoke()`? If not, then this is why you get the exception: when you scroll, UI thread detects changes from another (non-UI) thread. UI-related changes must be made using `Control.Invoke()` or `Control.BeginInvoke()`. – Sergey Vyacheslavovich Brunov Jan 23 '13 at 00:18
  • yes TimeSeries collection is changed, and fires event HistoryUpdate, which triggers InvokeOnUpdate, which calls Method through Invoke. But at the same time I'm scrolling, changing the UserControl TimeSerie object. Probably I should make a lock object to lock when changing that TimeSerie object, and also lock on that inside Method(), called by other thread. I guess that is the cause/solution. But my main question was really if I can see from the exception message what is the collection causing it.. Because I'm also unregistering and registering handlers, so could be a collection of handlers. – bretddog Jan 23 '13 at 00:23