The World’s Leading Microsoft .NET Magazine
   
 
The .NET Addict's Blog

Calendar

««Sep 2010»»
SMTWTFS
    1234
567891011
12131415161718
19202122232425
2627282930

Mailing List

My Top Tags

                                                           

My RSS Feeds








I heart FeedBurner

Computers Blogs - Blog Top Sites

Site Hits

Total: 5,627,442
since: 19 Jan 2005

NOTE that while I am still linking all new content, the source of all my blog content is now the Kotan Code blog. This site is being maintained only to avoid losing search engine links.

IPC Remoting "Real World" Example

posted Thu 27 Jan 05
I am constantly struck by the amount of simple "Hello World" style examples that you find on the Web, in books, and even in magazine articles. While there is nothing essentially wrong with those samples, most of them don't do us much good.
Case in point: I've seen some examples of how to do IPC-based Remoting where the server object is just an object that echoes some transmitted text to the Console. This is great to see the fundamentals of that Remoting technology, but it does nothing for showing you how to apply that technology to a real-world problem. In most cases, hosted server objects will not have direct access to the GUI, and if they do the code on the server is probably full of spaghetti and not something you want to model to begin with. I'm a strong believer in encapsulation and isolation: Objects should perform their given task and nothing else. Ever. A server object should not have to have a using statement so that it can reference a Windows Form directly. There are far more elegant approaches.
What happens if the server object you are connecting to needs to affect the GUI of a Windows Form? You'll need to rig up event handlers and delegates - and if you do that via Remoting in 1.1 and 2.0 - you'll need to modify the security settings of the formatter.
In the sample I was playing with, I wrote a Console application that sends a message to a Windows Forms application. The server object, SharedMessage has a method called SendMessage. The SendMessage method implementation on the server invokes an event handler that then informs the Windows GUI that a message has been received. I like this pattern because Windows Forms doesn't bleed into the otherwise pure server code, and you can attach any GUI to the server object - they are somewhat loosely coupled instead of mingled together in spaghetti.

Step 1 - Create the Shared Interface
#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

#endregion

namespace SharedLibrary
{
    public interface ISharedMessage
    {
        void SendMessage(string message, string sender);
    }
}

The code above creates a pretty simple shared interface. I placed this shared interface in a Class Library called SharedLibrary.

Step 2 - Create the Server Implementation of the Shared Interface
#region Using directives

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;

#endregion

namespace IpcServer
{
    public delegate void OnMessageReceivedDelegate(string message, string sender);

    public class SharedMessage : MarshalByRefObject, SharedLibrary.ISharedMessage
    {
        public event OnMessageReceivedDelegate OnMessageReceived;

        public SharedMessage()
        {

        }

        public void SendMessage(string message, string sender)
        {
            if ((OnMessageReceived != null) &&
                 (OnMessageReceived.GetInvocationList().Length > 0))
                OnMessageReceived(message, sender);
        }
    }
}

This class is fairly basic, and should be familiar to most people with Remoting experience. The class, as usual, not only implements the shared interface, but inherits from MarshalByRefObject. Something that is part of the server class that is not part of the shared interface is the event OnMessageReceived. This event is fired every time a message comes in from a Remoting client. Instead of having to bleed the line between the Remoting code and the WinForms code by passing instances of forms around in constructors, we can just use this event handling scenario to tell the WinForms GUI (or any other object, for that matter...that's the beauty of this pattern) that a message has arrived.

Step 3 - Host the Server Object
#region Using directives

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Ipc;

#endregion

namespace IpcServer
{
    partial class frmMain : Form
    {
        private SharedMessage sm;
        private delegate void ShowMessageDelegate(string message, string sender);
        private ShowMessageDelegate showMessage;

        public frmMain()
        {
            InitializeComponent();

            // need this or we can't use delegates and remoting.
            BinaryServerFormatterSinkProvider serverProv = new BinaryServerFormatterSinkProvider();
            serverProv.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;

            BinaryClientFormatterSinkProvider clientProv = new BinaryClientFormatterSinkProvider();

            System.Collections.Hashtable properties = new System.Collections.Hashtable();
            properties["portName"] = "MessageServer";
            IpcChannel ipc = new IpcChannel(properties, clientProv, serverProv);
            ChannelServices.RegisterChannel(ipc);

            RemotingConfiguration.RegisterWellKnownServiceType(
                typeof(SharedMessage), "MessageServer.rem", WellKnownObjectMode.Singleton);


            sm = (SharedMessage)Activator.GetObject(typeof(SharedMessage), "ipc://MessageServer/MessageServer.rem");
            showMessage = new ShowMessageDelegate(ShowReceivedMessage);
            sm.OnMessageReceived +=new OnMessageReceivedDelegate(sm_OnMessageReceived);
        }

        void sm_OnMessageReceived(string message, string sender)
        {
            this.Invoke(showMessage, new object[] { message, sender });
        }

        public void ShowReceivedMessage(string message, string sender)
        {
            ListViewItem lvi = new ListViewItem();
            lvi.Text = message;
            lvi.SubItems.Add(sender);
            lvMessages.Items.Add(lvi);
        }
    }
}

The first point of interest here is the sink providers. I had to create them so that I could tell the Remoting infrastructure that the type filter level is Full. In most "Hello World" examples, you see a simple string being passed and that's it. In this example, the server hosting the object is attaching to the Singleton object via Remoting, and then subscribing to an event that will be published using Remoting. In order to send events across a Remoting boundary in 1.1 or 2.0, you need to set the TypeFilterLevel to Full.

The second interesting thing is that instead of using new to create an instance of the Singleton server object, I'm using Activator.GetObject. This is because if I used new, then the instance I am using to listen for events would not be the same instance as the one receiving events. This is because the client is going to use Remoting to get the Singleton object, and if I don't use Remoting to get the same object on the server, the two objects will be different, and the event publisher will not be attached to the event listener.
Finally we get to the code where I am adding the message information to a ListView on the main form. You'll notice that I had to use Invoke. This is because when your code receives an event notification, whether from Remoting or from somewhere else, you can't guarantee that the event notification is coming in on the main GUI thread. To "push" your code from a background thread into the main UI thread (so your code will have permission to modify things like list views and create modal dialogs) you need to use Invoke.

Step 4 - Create the Client.
#region Using directives

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Ipc;

using SharedLibrary;

#endregion

namespace IpcClient
{
    class Program
    {
        static void Main(string[] args)
        {
            IpcChannel ipc = new IpcChannel("MessageClient");
            ChannelServices.RegisterChannel(ipc);

            ISharedMessage sm = (ISharedMessage)Activator.GetObject(typeof(ISharedMessage),
                "ipc://MessageServer/MessageServer.rem");
            if (sm == null)
            {
                Console.WriteLine("Failed to communicate with remote server.");
                return;
            }

            Console.WriteLine("Enter message to send to server:");
            string message = Console.ReadLine();
            Console.WriteLine("Enter your name (sender):");
            string sender = Console.ReadLine();

            sm.SendMessage(message, sender);
        }
    }
}
       

This is a pretty simple Console application. It uses Console.ReadLine() to obtain the message and sender information from the user. All of the important activity takes place in the Activator.GetObject call. A couple of important points:
  • The client never sees the server implementation, it only has access to ISharedMessage
  • The name of the client IPC channel doesn't have to match the name of the server IPC channel so long as the ipc URI uses the server channel properly

So that's my version of a "Hello World" sample in IPC Remoting 2.0. A Windows Forms application sits as a host for the server object (running as a Singleton in case you need to maintain state... which is often the case). The client can be anything, but in the sample I used a simple Console application. The uses for this kind of communication are countless. You can also secure your IPC channel by creating ACLs and much more thats beyond the scope of this particular blog.

I hope this entry gives you some idea of the power of Remoting, especially when combined with .NET fundamentals like delegates, event handling and multi-threaded UI.

Here is a screenshot of the IPC Server application after I accessed it using 2 different clients

Screenshot

links: digg this    del.icio.us    technorati    reddit

AddThis Social Bookmark Button




1. Subbu left...
Mon 22 Aug 05 9:32 am

Hi, I am trying to do remoting using the sample above in .net 1.1, however I get some runtime exception, can you send me some workspace that compiles and runs on v1.1? Thanks.


2. Michael left...
Mon 23 Jan 06 5:14 pm

Thanks a lot, this code is reall cool (was trying some others, but yours was the only one that worked straigt away)


3. hugo left...
Tue 14 Nov 06 4:11 pm

Response to: Hi, I am trying to do remoting using the sample above in .net 1.1, however I get some runtime exception, can you send me some workspace that compiles and runs on v1.1? Thanks.

This is new functionality available only in .NET 2.0


4. tito left...
Tue 05 Dec 06 9:54 am

This is .NET 2.0 only. IPC channels did not exists before.


5. Vidar left...
Sat 17 Mar 07 7:09 am

Hi, could you post the source code for the GUI example at the bottom?


6. marco left...
Fri 06 Apr 07 7:17 am

I tried the example. Everything works fine after starting the app. But when you wait a few minutes the remoting does still work, but the event OnMessageReceived of the shared object is NULL!!! It seems that the client gets another instance of the server object. How can I avoid this?


7. Maty left...
Wed 11 Apr 07 7:16 am

to Marco: You have to override method InitializeLifetimeService() with return null in MarshaledByRefObj Class derivative. It makes Share object useable for server lifetime.


8. Marc left...
Fri 20 Jul 07 8:48 pm

thanks a ton! that did it! :-)


9. David Kirkland left...
Tue 04 Sep 07 4:25 am

Thanks for this, it works very well! :-) Just wanted to share my experience with other readers.... I implemented the server part of this code in a seperate class, not in the main part of the form. I kept getting serialization errors!!! The solution was to make my IPC server class (equivalent to frmMain) inherit from MarshalByRefObject.

Hope this helps someone....


10. toprak left...
Wed 17 Oct 07 9:38 am

your example works fine. but i have implemented the server part to a windows service. Now, i have an build error: ...'Service....' does not containt a definition for "Invoke". -> void sm_OnMessageReceived(string message, string sender)

  • {

    • this.Invoke(showMessage, new object[] { message, sender });

  • }


11. Iain left...
Fri 09 Nov 07 8:45 am

toprack,

If you are using a windows service you probably arn't putting the data into a UI control and therefore you do not need the invoke.


12. Alex Birch left...
Wed 23 Jul 08 4:59 pm :: http://www.lifesabirch.org

Just out of curiosity, why did you put: (OnMessageReceived.GetInvocationList().Length > 0)

When I've done this in the past, I've just checked for the delegate not to be null. Have you ran into problems in the past for not checking that or is it a performance hit?


13. Janet left...
Sat 02 Aug 08 5:29 am

Works beautifully in C#, but if I convert to VB I get an error--Requested Service Not Found. If I remove this line in the Server main form constructor (C# included for reference) C#: sm.OnMessageReceived += new OnMessageReceivedDelegate(sm_OnMessageReceived); VB: AddHandler sm.OnMessageReceived, AddressOf sm_OnMessageReceived The error is gone but the event does not get raised. Do you possibly have a vb.net version of the code? I expect there is something I lost in the translation. It would be greatly appreciated.


14. Walter left...
Fri 02 Jan 09 5:02 pm

Thanks for the brilliant example. This is much better than the other average "Hello World" examples. I like the fact that it goes into the details of creating an interface and separating the implementation from the interface (something missing from other examples that said this was a preferred method of creating IPC). If anything, that's the one part I missed in this article--that separating the interface allows you to change the implementation without recompiling both client and server.


15. awpross left...
Fri 24 Apr 09 4:22 am

Does anyone know how i can modify this code to send messages from Server to Client ??


16. Simon left...
Fri 01 May 09 9:25 am

Kevin:

  • Any chance you can Email your Code to me? or May be post the "IPC Remoting "Real World" Example" in Zip file? I'm googling about Event and IPC for quite a while! Your code seems to be the BEST I ever come across! Let me learn bit by bit about your Full Code.

Thanks


17. eb left...
Fri 15 Jan 10 11:24 am

Thanks, great article! This worked for me fine, but when I moved the server code to a DLL, the line that wires the OnMessageReceivedDelegate is throwing an ArgumentException: "Error binding to target method" when invoked by another executable. I've tried using CreateDelegate and still no luck.


18. vijaya left...
Wed 03 Mar 10 5:13 am

"D:\Samples\SampleRemoting\SampleRemoting\IPCServer\obj\Debug\IPCServer.exe ' does not contain a static 'Main' method suitable for an entry point iPCServer " plz help me how to fix "


Tag Related Posts