Programming an application that needs to use .NET remoting sounds intimidating at first with all the channel and sink terminology that gets thrown around, but it's really a pretty straight forward process. In this post I'll demonstrate a simple example of using remoting to have one Windows Form application send text message data to another. From this you can see the basics of how remoting works which can then be architected into more robust systems to perform service to service communication, web to service communication, Windows Form to service communication, and any other combination that meets your architecture needs.
The code samples used in this article are from VS 2005, but the code can be backwards ported to VS 2003 with little changes.
1. Creating the Remoting Listener
The first application we'll create will be the listener which will receive remoting messages from one or more other applications. This project needs to define the remoting object that all the other applications will reference and also register the remoting channel to listen for incoming message. For this example, we'll use TCP as the channel protocol meaning that all communications should be sent over a TCP socket to the port we designate. This is similar to http communications where your web browser opens a TCP connection on port 80 to a web server and then transmits an http message. In this example, we'll be sending a text string across port 8888 (just a random port I selected to use) to our remoting listener Windows Forms application.
To create the listener start a new Windows Forms project. Add a multi-line textbox called txtReceived to the form. This is where our messages will appear when we receive them from another application. This example works by exposing a static method on our listening form that our remotable object can call when it is invoked and the static method will write the text data into the txtReceived textbox. Below is the code for the listener form (not including the windows designer code that is generated to handle form display). My example sets the remoting configuration (port, URI, object type) at run-time, but you can also load this information from a configuration file if desired.
using
System;
using
System.Collections.Generic;
using
System.ComponentModel;
using
System.Data;
using
System.Drawing;
using
System.Text;
using
System.Windows.Forms;
using
System.Runtime.Remoting;
using
System.Runtime.Remoting.Channels;
using
System.Runtime.Remoting.Channels.Tcp;
namespace
RemotingListener {
public partial class Form1 : Form {
private TcpChannel _channel = null;
//this is the static reference to the host service form, this allows the remoting object to call our public static method and have the data displayed.
private static Form1 thisForm = null;
public Form1() {
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e) {
try {
// Set the static reference to this form, our remotable object will call into this whenever a message is received..
thisForm = this;
// Create the server channel, this is where we designate the port the application will listen on - make sure you pick a port that is not already in use or an exception will be thrown.
_channel =
new TcpChannel(8888);
// Register the server channel.
System.Runtime.Remoting.Channels.
ChannelServices.RegisterChannel(_channel, false);
// Expose an object for remote calls. This exposes for any clients that connect what object we allow to be remoted and gives it a unique URI name ("AppMessageListener"). You could register multiple objects as remotable on the same channel within the same application as long as you give them each a unique URI for the client to call.
RemotingConfiguration.RegisterWellKnownServiceType(typeof(AppMessage), "AppMessageListener", WellKnownObjectMode.Singleton);
}
catch (Exception ex) {
MessageBox.Show("Exception: " + ex.Message);
}
}
public static void DisplayData(string data) {
//display the received text in the form. This static method gives the remotable object a way to talk with our form so that we can see the messages that arrive.
//You'd normally want to ensure this is done by the UI thread to handle multi threading appropriately but that has been ommitted from this sample.
thisForm.txtReceived.Text += data +
"\r\n";
}
}
// This class is the remotable object that can have any public method called by any client applications over the TCP port.
public class AppMessage : MarshalByRefObject {
private string _data = string.Empty;
public void SendData(string data) {
//send the data to the host form, this is where you could do many types of things to hand off the request
//to a centralized processing thread, singleton class, etc.
Form1.DisplayData(data);
}
}
}
2. Creating the Remoting Client
Now we can create a client application that is capable of calling our remoting listener. Our client form simply needs to open the TCP channel, register the client type, create a message sink and then execute the desired method on the remotable object. For the remoting client create a new Windows Form application and on the main form add a text box called txtData and a button named cmdSendData. The cmdSendData button will perform the remoting call and pass the txtData.Text as our message to the remoting listener. Below is the code for our client form:
using
RemotingListener;
using
System.Runtime.Remoting;
using
System.Runtime.Remoting.Channels;
using
System.Runtime.Remoting.Channels.Tcp;
namespace
RemotingClient {
public partial class Form1 : Form {
private bool _channelAlreadyRegistered = false;
public Form1() {
InitializeComponent();
}
private void cmdSendData_Click(object sender, EventArgs e) {
try {
//if we haven't already registered the channel, then registery it. We only need to do this once per
//application instance - if you do it twice you'll get an exception saying the channel is already registered.
if (!_channelAlreadyRegistered) {
// Create the channel.
TcpChannel clientChannel = new TcpChannel();
// Register the channel.
ChannelServices.RegisterChannel(clientChannel, false);
// Register the host where the remoting calls will connect. We do this by specifying a path to the remoting listener of: protocol://ipaddress:port/URI
WellKnownClientTypeEntry remoteType = new WellKnownClientTypeEntry(typeof(AppMessage), "tcp://localhost:8888/AppMessageListener");
RemotingConfiguration.RegisterWellKnownClientType(remoteType);
// Create the message sink to the remoting listener for the object we want to use. Note the URI value should match the URI defined when we register the service type on the listener.
string objectUri;
System.Runtime.Remoting.Messaging.
IMessageSink messageSink =
clientChannel.CreateMessageSink(
"tcp://localhost:8888/AppMessageListener", null, out objectUri);
_channelAlreadyRegistered = true;
}
object[] args=null;
AppMessage remoteMsg =null;
//create an instance of the remote object, you must do this with the Activator so it knows to make a remote connection for the object type.
remoteMsg = (
AppMessage)Activator.CreateInstance(typeof(AppMessage), args);
remoteMsg.SendData(txtData.Text);
}
catch (Exception ex) {
MessageBox.Show("Exception: " + ex.Message);
}
}}
}
3. Run and you're done!
Now run the listener application. Once it has started up run the client application. Enter your message in the client and press the send button and it will appear on your listener. You are now remotable! You can even start up a second instance of the client application and send messages from client 1 and 2 and both sets of messages will appear in the single listener form. As you can see, creating the remoting connection is simpler than it sounds. The real trick is fitting this efficeintly into your architecture and leveraging the power it can provide if you need .NET to .NET communications between different processes on the same machine or across different machines and even through firewalls. If you're trying to decide between remoting and web services as a communication mechanisms you can check for advice on MSDN (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconchoosingcommunicationoptionsinnet.asp) to learn which scenarios are best suited for each mechanism.
As most of you know, Visual Studio 2005 comes pre-loaded with some useful code snippets you can utilize. Snippets are configurable so you can make your own as well as distribute them to other developers for their use. There are some more useful snippets from Microsoft up on MSDN (http://msdn.microsoft.com/vstudio/downloads/codesnippets/default.aspx) ranging across a wide array of tasks - Collections and Arrays, File System, OS, and Windows Forms to name just a few.
These download as vsi files which will self-install when you run them on a workstation that has VS 2005 installed (similar to how starter kits will self-install from this file format).