There are some on CodeProject. Here's a mini program that won't run as
I've cut out all the gui stuff and replaced with ..., but it should
give you the idea. There are four projects in the solution, Client,
Server, Interface and Shared, the salient code for each is below.
Client, Server and Shared reference Interface, Server also references
Shared.
Server creates a real object, Client uses an interface so it is never
exposed to the remote object directly, Interface contains that
interface and shared the real implemenation of it that allows the
server to do things.
There are a lot of comments though, I'd trim it down properly but I
must go now, sorry!
Server-------------------------------------------------------------------------------------
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting.Channels.Tcp;
namespace Ian.Remoting
{
public class ServerGUI : System.Windows.Forms.Form
{
private delegate void UpdateTextDelegate(string pText);
//ObjRef transfers an object reference across appdomain boundaries
private ObjRef _refCalcRequest;
//Our Remoted object Remoted is the class name, it's not some cunning
keyword.
private Remoted _calcRequest;
...
private void ServerGUI_Load(object sender, System.EventArgs e)
{
//Set up the channel.
ChannelServices.RegisterChannel(new TcpChannel(50050));
//Using singleton model to get our Remoted instance.
_calcRequest = Remoted.Instance;
//Get the ObjRef
_refCalcRequest = RemotingServices.Marshal(_calcRequest, "Remoted");
//Hook up to the event so we know when a client is trying to do a
calculation
_calcRequest.CalcRequestEvent+=new
Ian.Remoting.Remoted.CalcRequestEventHandler(_calc Request_CalcRequestEvent);
}
private void _calcRequest_CalcRequestEvent(object sender,
CalcRequestEventArgs e)
{
Random r = new Random();
//Invoke should always be required in this scenario, but this is
good practice.
if(this.InvokeRequired)
{
//Thread safely update the GUI
this.BeginInvoke(new UpdateTextDelegate(UpdateText), new object[]
{e.Name + " Submitted: " + e.Calculation});
}
//Populate the EventArgs results field so when we exit the Remoted
can return the results.
e.Results = new double[] {r.NextDouble() * 10 , r.NextDouble() *
10};
}
private void UpdateText(string pText)
{
textBox1.Text+="\r\n" + pText;
}
}
}
Shared------------------------------------------------------------------------------------------
namespace Ian.Remoting
{
public class Remoted : MarshalByRefObject , IRemoted
{
//The event and delegate used when the user calls calculate. This
notifies the server.
public delegate void CalcRequestEventHandler(object sender,
CalcRequestEventArgs e);
public event CalcRequestEventHandler CalcRequestEvent;
//Singleton
private static Remoted _instance;
private Remoted()
{
}
public static Remoted Instance
{
get
{
if(null==_instance)
{
_instance=new Remoted();
}
return _instance;
}
}
public override Object InitializeLifetimeService()
{
// Allow this object to live "forever". Even singletons are
collected after a default of 5 minutes normally.
return null;
}
double[] IRemoted.Calculate(string pCalculation, string pName)
{
//The user has access to IRemoted and has called Calculate on it. We
manage concurrency by creating a new EventArgs to hold
//our info then raise the event which the server consumes.
CalcRequestEventArgs calcRequest = new
CalcRequestEventArgs(pCalculation, pName);
if(null != CalcRequestEvent)
{
CalcRequestEvent(this,calcRequest);
//This would of been populated by the server.
return calcRequest.Results;
}
else
{
return new double[] {-1.0};
}
}
}
public class CalcRequestEventArgs : EventArgs
{
private string _calculation;
private double[] _results;
private string _name;
public string Name
{
get
{
return _name;
}
set
{
_name = value;
}
}
public string Calculation
{
get
{
return _calculation;
}
}
public double[] Results
{
get
{
return _results;
}
set
{
_results = value;
}
}
public CalcRequestEventArgs(string pCalculation, string pName)
{
_calculation = pCalculation;
_name = pName;
}
}
}
Interface
---------------------------------------------------------------------------------------------
using System;
namespace Ian.Remoting
{
public interface IRemoted
{
double[] Calculate(string pCalculation, string pName);
}
}
Client
-----------------------------------------------------------------------------------------------------------------
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting.Channels.Tcp;
namespace Ian.Remoting
{
/// <summary>
/// Summary description for Form1.
/// </summary>
public class ClientGUI : System.Windows.Forms.Form
{
private IRemoted _remoted; //Remoted implements IRemoted. We hold a
reference the the interface not the concrete object
private delegate void SetTextDelegate(string pText); //Used to
thread safe update the GUI as we're using Async callbacks
private delegate double[] DoCalculationDelegate(string pName, string
pCalculation); //Used to call the Calculate method on IRemoted
...
private void button1_Click(object sender, System.EventArgs e)
{
txtResults.Text = string.Empty;
//Our Async callback will call CalculationCallback on completion
AsyncCallback acb = new AsyncCallback(this.CalculationCallback);
//DoCalculationDelegate has the same signature as IRemoted.Calculate
DoCalculationDelegate d = new
DoCalculationDelegate(_remoted.Calculate);
//Invoke the Delegate with the parameters. the AsyncCallback and
null (not sure what object represents)
IAsyncResult ar = d.BeginInvoke(txtName.Text,txtCalculation.Text,
acb, null);
}
public void CalculationCallback(IAsyncResult ar)
{
//The call to the remote object has ended and called here to let us
know.
//Get our delegate out so we can end it.
DoCalculationDelegate del =
(DoCalculationDelegate)((AsyncResult)ar).AsyncDele gate;
try
{
//del's signature is the same as calculate, so we expect an array
of doubles out.
double[] results = (double[])del.EndInvoke(ar);
string output="";
foreach(double r in results)
{
output+=r.ToString() + " ";
}
//Thread safely call Updateoutput with our new output.
this.BeginInvoke finds this control's thread and fires the delegate on
it.
this.BeginInvoke(new SetTextDelegate(UpdateOutput),new
object[]{output});
}
catch(Exception e)
{
this.BeginInvoke(new SetTextDelegate(UpdateOutput),new
object[]{e.Message});
}
}
private void UpdateOutput(string pText)
{
txtResults.Text = pText;
}
private void ClientGUI_Load(object sender, System.EventArgs e)
{
//Register a channel
ChannelServices.RegisterChannel(new TcpChannel());
//retrieve the remote object. On the server side it will be a
concrete object of type Remoted
_remoted = (IRemoted)Activator.GetObject(typeof(IRemoted),
"tcp://localhost:50050/Remoted");
}
}
}
Rich wrote:
Can anyone suggest a good (current) tutorial on how to do basic
remoting with C# (2005 express edition)?