0

I have a .NET remoting client activated object type client-server application. I'm using Visual Studio 2010 targeting the .NET Framework 4. When I run without debugging, the program works fine - the clients can connect no problem. But when I try to debug, on the line in the client code where I have the 'new' operator, the server (I think) throws an exception.

What I want to do is keep references to the remote objects created in the server code. So in my Remote Object's constructor I have this line:

Cache.GetInstance().addFireFighter(this);

When I debug, this code runs fine too. But when it goes back to the remote object's line to call the addFireFighter method, that's when it crashes. Here's the addFireFighter method:

public static IServer _server;

public void addFireFighter(FireFighter ff)
{
    _server.addFireFighter(ff);
}

And the _server.addFireFighter method:

public void addFireFighter(FireFighterResponder.FireFighter ff)
{
    _ffList.Add(ff); // -> works fine :S
    Console.WriteLine("FireFighterResponder addFireFighter added");
    lstBox.Items.Add(ff); //-> CRASH!!
}

Observation: When I run the server without debugging but run the client in debug mode, it still works fine.

This is a school project and I'm new to C# .NET remoting. I implemented the same in Java and had no issue. So I can give my whole project if somebody wants to look at it. Maybe I have a design flaw.

Here's the stack trace:

System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.InvalidOperationException: Cross-thread operation not valid: Control 'lstBox' accessed from a thread other than the thread it was created on.
   at System.Windows.Forms.Control.get_Handle()
   at System.Windows.Forms.ListBox.NativeAdd(Object item)
   at System.Windows.Forms.ListBox.ObjectCollection.AddInternal(Object item)
   at System.Windows.Forms.ListBox.ObjectCollection.Add(Object item)
   at FirefighterMonitorSystem.BaseStation.addFireFighter(FireFighter ff) in C:\Users\Dula\Documents\My Dropbox\Firefighter\453\FirefighterMonitorSystem\FirefighterMonitorSystem\BaseStation.cs:line 35
   at FireFighterResponder.Cache.addFireFighter(FireFighter ff) in C:\Users\Dula\Documents\My Dropbox\Firefighter\453\FirefighterMonitorSystem\FireFighterResponder\Cache.cs:line 33
   at FireFighterResponder.FireFighter..ctor() in C:\Users\Dula\Documents\My Dropbox\Firefighter\453\FirefighterMonitorSystem\FireFighterResponder\FireFighter.cs:line 20
   --- End of inner exception stack trace ---

The lstBox is a .NET ListBox in my server code where I add each Remote object. But what still confuses me is why it works when not in debug mode.

Jeff Mercado
  • 129,526
  • 32
  • 251
  • 272
Dula
  • 1,404
  • 1
  • 14
  • 29

2 Answers2

1

You have to look at the exception's InnerException property to know what went really wrong.

You are accessing a control from a thread other than the main thread (aka UI thread). This is illegal, windows are not thread-safe. You must use Control.Begin/Invoke(). You don't get the exception without the debugger because this thread checking is only enabled by default when a debugger is attached. That doesn't mean it is safe to run code without a debugger, it will randomly cause paint problems or deadlock, although you may have to wait a day or a week for that to happen.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • hey Hans, I edited the question with the InnerException. Thanks! – Dula Mar 29 '11 at 18:49
  • makes perfect sense Hans, but then why would the _ffList.Add(ff); line work. That list was created by the UI thread too isn't it. Unless I cannot access only controls from a different thread? – Dula Mar 29 '11 at 19:16
  • I don't know what _ffList is, probably not a control. You won't get an exception from accessing, say, a List<> from a thread. That code will fail randomly and silently unless you protect it with the lock statement. Be careful with threads, they'll give you a very hard time when you don't get this stuff right. – Hans Passant Mar 29 '11 at 19:24
  • _ffList is indeed a List. So i edited the addFireFighter method as follows: lock (countLock) { _ffList.Add(ff); } Console.WriteLine("FireFighterResponder addFireFighter added"); this.addFirefighterToListBox(ff); } – Dula Mar 31 '11 at 15:06
  • and addFirefighterToListBox: 'public void addFirefighterToList(FireFighterResponder.FireFighter ff) { if (this.lstBox.InvokeRequired) { UpdateList updatelist = new UpdateList(addFirefighterToList); this.Invoke(updatelist, new object[] { ff }); } else { lstBox.Items.Add(ff); } }' does that sound right? – Dula Mar 31 '11 at 15:09
  • Sounds good, be sure to use the same lock in any other code that accesses that List<>. Please close your question by marking it answered. – Hans Passant Mar 31 '11 at 15:14
0

The exception that is being thrown is a "Managed Debug Assistant"
It only happens when a debugger is attached and is trying to warn you that you are doing something wrong that will work most of the time.
In this case, what you are doing wrong is to access the control lstBox from a thread other than the one that created it. (Windows Forms are not thread safe)

I can't follow your question very well, but I think you have a method in a server side object that is calling an object on the client. This means the code running on the client is running on a thread pool thread that is servicing the remoting infrastructure, not your application's main thread, hence the error.

the question about this error and the Invoke method (that you will use to fix it) has been asked many times before, here is one Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on take a look, I don't want to do all your homework for you. :)

One warning. If your application thread is still waiting for the server to respond and you try and use Invoke then your application will probably hang. Example, you can't do this Client Application calls Server method foo server method foo calls client method bar client method bar uses Invoke to call client method hang

The call to Invoke is now waiting on the main thread, which is waiting for the call to Foo to finish which is waiting for the call to bar to finish which is waiting for the Invoke to finish which is waiting for the main thread to be free which means waiting for the call to Foo to finish, etc...

Community
  • 1
  • 1
pipTheGeek
  • 2,703
  • 17
  • 16