1. Introduction
In this article I explain how to use delegate with the functions exposed by the remote object. Also I will explain how to call those remote object functions synchronously and asynchronously. I do not want to explain threading here. But the following is a very short note about synchronous and asynchronous function calls:
-
The client function that makes a call to the remote function will wait for the completion of the remote function. This is a synchronous call to the function exposed by the remote object.
-
The client function that makes a call to the remote function will not wait for the completion of the remote function. This is an asynchronous call the function.
2. About the example
The server in this example exposes a function that prints a running count taking some time. You can think of this function as a long running task on the server. The function is just to simulate the long running process situation.
The client has two buttons and one function is calling the remote function in synchronously and the other does the same asynchronously. The client uses the delegate of the same type to make a call to the remote functions.
When you click the Start Sync button, the count will be running on the server and once it is finished, the count on the form starts. The server shows the running count in the console window and the client shows it in the text box. So, here the client will wait for the server to complete the counts.
When you click the Start Async button, the count will run in parallel between server and the calling client. That means that after the call the client will not wait for the server to complete its task.
3. Codes for The Server
The code for the server is similar to previous examples. So you will not see much explanation here repeated again. If you need much explanation on the server, please have a look at the first remoting article here: Basic Article
1. In the server, after creating the project a class called Counter is added and it is derived from MarshalByRef object. In the counter.cs file a required namespace is included. This Counter class acts as the Remote class.
//RemSrv 01: Include required assemblies
using System.Runtime.Remoting;
2. The class has a constructor and a method PerformCount. This method will be called from the client using delegates. We will see about that in detail when we are moving to the client side coding. The code for this class is given below:
//RemSrv 02: Initialize the remoting object
public Counter()
{
Console.WriteLine("Remote Object Created. " + Environment.NewLine);
}
//RemSrv 03: Perform the counting operation. This will take sometime and is useful to explain
// How async call to this method is useful from the claent end.
public void PerformCount()
{
int x;
for (x = 1; x < 10000; x++)
Console.WriteLine("Current Count : " + x.ToString());
Console.WriteLine("Counting is finished");
return;
}
3. In the application entry, we are hosting the remote object under the name Counter. For more detail look at the basic article (the first one).
//RemSrv 04 : Required Assemblies
using System.Runtime;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
namespace RemotingDelegate
{
class Program
{
static void Main(string[] args)
{
//RemSrv 05 : Create a communication channel (Server) and register it
TcpServerChannel SrvrChnl = new TcpServerChannel(13340);
ChannelServices.RegisterChannel(SrvrChnl, false);
//RemSrv 06 : Register the Remote Class so that the Object can go
//and sit on the Remote pool
RemotingConfiguration.RegisterWellKnownServiceType(typeof(RemotingDelegate.Counter),
"Counter", WellKnownObjectMode.SingleCall);
//RemSrv 07 : Halt the server so that Remote client can access the object
Console.WriteLine("Server is Running...");
Console.WriteLine("Press Any key to halt the Server");
Console.ReadKey();
}
}
}
4. Codes for The Client
The client is the Windows application and the form details and what each UI is is explained in section 2 of this article.
1. The following namespaces are included in the form to access the Remoting as well as very basic thread function Thread.Sleep. Also note that the project reference for the server is also included. Once the application is built properly then you can split the exes into server and client machines for testing purposes.
//Client 01: Include the required namespace
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
using System.Threading;
using RemotingDelegate;
2. Once we are ready with the required namespaces, two delegates of the same are declared in the class level. Actually one delegate is sufficient, I kept two just to differentiate the way I am going to use it.
//Client 03: Declare delegates for Sync Call and Async Call
public delegate void SyncCall();
public delegate void AsyncCall();
3. The LocalCounter function here does the same job of the function PerformCount on the server. So there are two long running tasks, one at the server side and the other one is at the client side.
//Client 06: Start the Local Counter. Assume that It is a long running task.
private void LocalCounter()
{
long x;
lblDisplay.Text = "Starting the Local Count...";
for (x = 1; x < 10000; x++)
{
txtCount.Text = x.ToString();
Application.DoEvents();
}
lblDisplay.Text = "Local Count is Done.";
}
4. The click event handler for the button Start Sync first creates the proxy for the remote object and stores that in the variable cntObj. Then a delegate object of type SyncCall is created and it is pointing the remote function PerformCount. The function PerformCount is passed to the delegate object by using the proxy cntObj. Once the delegate fnCounter is ready, a call to the remote function PerformCount is made using the delegate. And after that a call to the local task (LocalCounter) is also made. The following is the code:
private void btnSync_Click(object sender, EventArgs e)
{
//Client 02: Get the Proxy for remote object
Counter cntObj = (Counter)Activator.GetObject(typeof(Counter),
"tcp://localhost:13340/Counter");
//Client 04: Call the remote method through the delegate. This call is Synchronous.
SyncCall fnCounter = new SyncCall(cntObj.PerformCount);
fnCounter();
//Client 05: Call the Local Counter
LocalCounter();
}
Note that after making a call to the remote object (by the statement fnCounter), the execution will pause till the remote function finishes its task. Once the task is completed on the server, the execution resumes here on the client and the function LocalCounter starts executing. You can observe this by running the sample, the count on the server is displayed in the console window, once the count is completed, you will see increment in the counter on the textbox of the form.
5. For making the asynchronous call, the delegate is created is in the same way as we did in the previous step. Below is the code:
//Client 07: Create the proxy for remote Object and set the delegate to the required
// remote function.
Counter cntObj = (Counter)Activator.GetObject(typeof(Counter), "tcp://localhost:13340/Counter");
AsyncCall FnCounter = new AsyncCall(cntObj.PerformCount);
6. Once the delegate is created, instead of directly calling the function, we are using the BeginInvoke method on the delegate. The first parameter is actually a call back that a server will call once it completes the operation. That is not covered here and I am leaving it to you to explore it yourself. I am passing null for both parameters. The return value is stored in the IAsyncResult. This is to do a check on the Server operation to make a safe call on the EndInvoke.
//Client 08: Call the remote method through the delegate. This is an Asynchronous call.
IAsyncResult AR = FnCounter.BeginInvoke(null , null);
7. After the above call we are making a call to the local counter function. But here the client, after making a call to the PerfomCount using the BeginInvoke method on the delegate, immediately moves to the next statement, which is a function-call for local counting. So there is no waiting for the server to complete its task.
//Client 09: Call the Local Counter. The Local Counter also run in parallel now, and we no need to
// wait for the remote call completion. The remote counting method, Once Done, calls our
// call back method CallBackHandler.
LocalCounter();
8. Finally, after making both functions run simultaneously, we are waiting at the end of the routine to make a call to the EndInvoke, which is the pair of its corresponding BeginInvoke. The IsCompleted property of the return value of the BeginInvoke method is used to test whether server function tied to the delegate is finished or not. Once we know that the server is done with the operation, we can make a call to EndInvoke by passing the value returned from the BeginInvoke function call.
//Client 10: Test the Remote counting is finsihed or Not before invoking the EndInvoke method on
// the delegate
while (!AR.IsCompleted)
Thread.Sleep(500);
FnCounter.EndInvoke(AR);
9. The entire event handling routine for the Start Async button is shown below:
private void btnAsync_Click(object sender, EventArgs e)
{
//Client 07: Create the proxy for remote Object and set the delegate to the required
// remote function.
Counter cntObj = (Counter)Activator.GetObject(typeof(Counter), "tcp://localhost:13340/Counter");
AsyncCall FnCounter = new AsyncCall(cntObj.PerformCount);
//Client 08: Call the remote method through the delegate. This is an Asynchronous call.
IAsyncResult AR = FnCounter.BeginInvoke(null , null);
//Client 09: Call the Local Counter. The Local Counter also run in parallel now, and we no need to
// wait for the remote call completion. The remote counting method, Once Done, calls our
// call back method CallBackHandler.
LocalCounter();
//Client 10: Test the Remote counting is finsihed or Not before invoking the EndInvoke method on
// the delegate
while (!AR.IsCompleted)
Thread.Sleep(500);
FnCounter.EndInvoke(AR);
}
5. Screen Shot of Sync and ASync Call
Sync Call:
Note: The Server finished the count and the Client has not yet started.
ASync Call:

Note: When the server is at the count 32 and the client is at 4533, that shows that both functions are running in parallel.
The above app is created in VS2005. If you have advanced IDE, say yes to the conversion UI displayed.