There is a console server application that happened to need to support GUI layer one day. I created a class library that contains the gui form, referenced it from the console application project, and let the form get instantiated and run on separate thread. So the server object and form object lives in different threads.
The server is a game server. It maintains collection of game objects that is currently placed on the map. Of course, they grow or shrink in number over time. This form had to visualize all these objects in real time. So naturally I added event that server invokes when there was change in the list of objects for the form to listen on.
class Server {
event Action<IEnumerable> ObjectListChanged:
}
When the form gets attached to the server, it will add handler.
Server server = new Server ();
var form = new VisializerForm ();
server.ObjectListChanged += form.OnObjectListChanged;
And when form is closing, it will remove handler from the server.
form.Closing += (s,e) => {
server -= form.OnObjectListChanged:
};
As the event invocation occurs in server thread, The implementation of OnObjectListChanged will contain check for InvokeRequired property.
class VisualizerForm : Form {
void OnObjectListChanged(IEnumerable objects){
if( !Visible || IsDisposed || !IsHandleCreated ) return;
if( InvokeRequired ) {
Invoke ((Action)(()=>OnObjectListChanged (objects)));
return;
}
// ....
}
}
This works well until the point we close the form. Exception is thrown when the event is invocated after form is closed, which in fact shouldn't happen as the event handler is removed in Form.Closing event, That says 'Cannot access disposed object 'Visualizer'
Funny thing is, when I check it by debugger, the event is clearly devoid of any handler (displays null when listed on watch window of visual studio) yet the thread reached the line of Form.Invoke call inside the event handler no matter what. Most likely it implies that I messed up multithreading at some point. What am I doing wrong? What might be happening here?
Sorry for the bad formatting I'm posting it on a phone, not used to it.
Changed the code like below and still getting the same exception.
public void OnObjectListChanged(IEnumerable objects)
{
if (IsDisposed || Disposing || !Visible || !IsHandleCreated) return;
if (InvokeRequired) {
try {
Invoke((Action)(() => OnFieldObjectListChanged(zoneId, channel, removed, added)));
} catch (Exception e) {
Debugger.Break(); // <-- server thread enters here.
}
return;
}
// ....
}
And the following is the exception and stack trace that I get in that catch clause:
위치: System.Windows.Forms.Control.MarshaledInvoke(Control caller, Delegate method, Object[] args, Boolean synchronous)
위치: System.Windows.Forms.Control.Invoke(Delegate method, Object[] args)
위치: System.Windows.Forms.Control.Invoke(Delegate method)
위치: VisualZoneServerLib.Visualization.VisualZoneServerWindow.OnFieldObjectListChanged(Int32 zoneId, Int32 channel, IList`1 removed, IList`1 added) 파일 D:\Server\ZoneServerLib.Visualizer\Visualization\VisualZoneServerWindow.cs:줄 310
In the meantime, UI thread stopped in override of Dispose method.
protected override void Dispose(bool disposing)
{
if (disposing) {
components?.Dispose();
m_timeLineWindow?.Dispose();
m_iesSnapshots?.Dispose();
}
base.Dispose(disposing); // <- UI thread stopped here
}
Searched once again keyword 'winform' 'invoke' 'dispose' and found this thread. It seems like talking about the same problem that I'm going through.