The new forums will be named Coin Return (based on the most recent vote)! You can check on the status and timeline of the transition to the new forums here.
The Guiding Principles and New Rules document is now in effect.

Was there a better way to do this? (C# Remote Events)

cyphrcyphr Registered User regular
edited February 2008 in Help / Advice Forum
So I'm grading a C# class this quarter and the prof asked me to write a little example application that the students could use in lecture to play with events, delegates, and remoting in C#. I coded a solution that has several clients that submit messages to a server, and when the server receives a message all the clients are notified, and they can refresh their displays with the new message (basically a simple chat client).

After reading through lots of examples on the net I finally got it working, and it was much more complicated than expected. I'm hoping that maybe a handful of people on here will know what I'm talking about, and if I'm lucky tell me that I made things way too complicated.

Basically:

I have a class library that holds the Message class, which consists of a sender string and a text string; an AbstractServer class which has a prototype for the function that fires an event when it receives a new message - this event is also declared as abstract in AbstractServer; and an AbstractEventSink class that has a callback prototype and an event to update the client form (more on this later); and two global delegates for the "message received" and "update form" events. The abstract classes MarshalByRefObj, and this class library is referenced by both the client and server implementations, so that they know about the types that are getting remoted.

Now, the reason I need an event sink is because a Windows Form isn't marshall-able, to my knowledge. So I need a callback to receive the events from the server, and that callback can in turn fire the event that calls the form-updating function.

I implement AbstractServer as ServerClass. I also make a ServerHost class in the same namespace, that has the following code:
BinaryServerFormatterSinkProvider serverProv = new BinaryServerFormatterSinkProvider();
             serverProv.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;
             BinaryClientFormatterSinkProvider clientProv = new BinaryClientFormatterSinkProvider();

             IDictionary props = new Hashtable();
             props["port"] = 8085;

             ChannelServices.RegisterChannel(new TcpChannel(props, clientProv, serverProv), true);
             

            RemotingConfiguration.RegisterWellKnownServiceType(typeof(ServerClass), "BulletinBoard", WellKnownObjectMode.Singleton);

            System.Console.ReadLine();

In the client namespace, I implement AbstractEventSink as EventSink. This namespace also has my Windows Form class, which is the client GUI. Here's the cute part: when I tried to run it at first, I kept getting a cross-threaded error, because apparently if you add a function that updates form controls to a delegate, and that delegate gets called by an event fired from the server, those will be in different threads even though you have a reference to the server object in your client. So I had to make two delegates to update the controls, which I then sent to the BeginInvoke method on the respective controls. This is what that looks like:
private delegate void UpdateRichText(string message);
        private delegate void UpdateLabel(string message);

        private void UpdateRT(string message)
        {
            this.richTextBox2.Rtf = message;
            
        }

        private void UpdateLB(string message)
        {
            this.label1.Text = "From " + message;
        }

        public void NewPost(BBMessage.BBMessage message)
        {
            UpdateRichText urt = new UpdateRichText(this.UpdateRT);
            UpdateLabel ulb = new UpdateLabel(this.UpdateLB);
            
            this.richTextBox2.BeginInvoke(urt, message.messageText);
            this.label1.BeginInvoke(ulb, message.sender);
        }

The two delegates live outside the Form class, in that snippet. The actual Form code is pretty straightforward remoting:
            BinaryServerFormatterSinkProvider serverProv = new BinaryServerFormatterSinkProvider();
            serverProv.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;
            BinaryClientFormatterSinkProvider clientProv = new BinaryClientFormatterSinkProvider();

            IDictionary props = new Hashtable();
            props["port"] = 8086;

            ChannelServices.RegisterChannel(new TcpChannel(props, clientProv, serverProv), true);

            theBoard = (BBClass.AbstractServer)Activator.GetObject(typeof(BBClass.AbstractServer),
                "tcp://localhost:8085/BulletinBoard");

            RemotingConfiguration.RegisterWellKnownServiceType(typeof(EventSink), "Events", WellKnownObjectMode.Singleton);

            EventSink sink = new EventSink();

            sink.UpdateForm += new BBClass.FormHandler(NewPost);

            try
            {
                theBoard.BoardUpdated += new BBClass.BoardUpdatedEventHandler(sink.callback);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }

So my question, if you've somehow managed to make it through this ridiculousness, is did I make it too complicated? Or is it really necessary to have event sinks, delegates invoked by form controls, abstract classes for both the client and the server, and four different types of delegates (with an additional type for each form control I want to update), just to be able to handle remote events?

EDIT: I forgot to mention those BinarySinkFormatters there. Without them, it appears that delegates can't be sent across a remote. Awesome.

steam_sig.png
cyphr on
Sign In or Register to comment.