Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Using the Remoting Callbacks in .Net Applications.

0.00/5 (No votes)
1 Dec 2001 6  
This article describes how to implement an asynchronous remoting callbacks from different remote objects such as .Net Service (COM+), Web Service and classic .Net object using the C# language.

Contents

Introduction
Concept and Design
      Remoting Interface
      Callback Custom Attribute
      Remoting Method
Implementation
      RemoteCallbackLib
      RemoteObject (A , X and WS)
      RemoteObjectX
      RemoteObjectWS
      HostServer
      HostServer.exe.config
      RemoteWindowsForm
            Initialization
            Callback object
            Invoking the Remote Method
      RemoteWindowsForm.exe.config
Test
Conclusion

Introduction

The object remoting is an integral part of the .Net Framework. It allows to call an object located on the Enterprise network using the same programming techniques like access to object located within the application domain. The remoting is a peer-to-peer communication mechanism between the remote object (server) and its consumer on the client side. The objects can be hosted in different application domains such as Web Server, Web Service, COM+ Service, etc.  The remoting call is processing in a synchronously manner with the option of the asynchronously end notification on the client side. This design pattern is knew as a BeginInvoke/EndInvoke technique. The client will receive a notification of the end remoting with the return value of the called method. Note that the client has no state of the remote method during this remoting process. (only finally as a returned value). This article will describe a mechanism of the remoting callback which can be used to notify client about the remote method state. This solution is suitable for any cases where "real-time" processing is required. In my simple sample I will demonstrate how to make a Windows Form more sensitive and pre-emptive during the remoting time using the Remote Callbacks.

Concept and Design

The concept of the remoting call-back is based on the remoting delegate, where a callback object is delegated to the remote object within the invoked method.  The following picture shows the remoting callback "plumbing" mechanism: 

The Remoting Callbacks model

The remoting part is sitting between the client and server objects. From the application point of view it represents by the proxy/stub of the remoting object, communication channel and message format protocol. The client/server  connectivity is started at the remote object with publishing its metadata (url object) and registering the stub object in the specified listener such as tcp or http channel and port. This process can be done either programmatically or administratively using a config file. The client side needs to know where the remote object metadata located and which channel and port is dedicated for this connectivity (listener channel). Based on this information, the client will create a proxy object of the remote object within its application domain. Also like a server side, this process can be completed by either the hard-coding or using the config file. Using config files to establish a "wiring" between the client/server is more flexible solution than hard-coding.

The above picture shows two remoting objects between the client and sever side:

  • Remote object - is calling by the client (Windows Form) to invoke any public member of the remote  object. The object can be setup for any mode such as Singleton, SingleCall or Client Activated. Note that remoting callback mechanism doesn't need to keep an object state (for instance: event class), it's a stateless - loosely coupled design pattern. The connectivity are setup explicitly and transparently (the remote object has to be published).
  • Callback object - is calling by the remote object within the method scope. Here is a situation little bit different. The remote callback is a private client-server contract and it is configured implicitly. The major different is that the client creating the callback proxy and then it is delegated to the remote object as a method argument. The callback object is running in the Singleton mode with unlimited lease time.

Based on the physical connectivity with the remoting objects, the client can create one common callback object for the same channel and formatter for different remote objects. This sample using a common callback object, where Windows Form calls concurrently three different remote objects and passing delegate of the same callback proxy. Note that each remote object will create own remote callback proxy based on the delegate metadata and using the Reflection design pattern. 

The following picture shows a situation on the client side using multiple remoting objects:

The Remoting Callbacks model

This is a generic model for any number of the remoting callback objects. The callback object is receiving a state from the remote object and passing it to the thread pool in the "fire&forget" fashion. The worker thread then dispatching a call to the Windows Control handler based on the cookie and senderId value.

Note that every object is serialized/de-serialized between the proxy/stub, therefore it has to be derived from the ISerializable (or attributed).

Let's look at closely for some issues which are related with the design implementation:

Remoting Interface

Interface is a contract between the object and its consumer. The interfaces allow to publish an object based on their abstract definitions. This encapsulation is giving a more flexibility in the design implementation. The major advantage using interfaces (or abstract class) is their loosely design pattern, which it may play an important role in the remoting issue. It is a good design technique to put all common abstract definitions  into one assembly. In my sample I created separate project called a ContractObject for that issue.

The following is an abstract definition of the remote object contract:

namespace RKiss.RemoteObject
{
   // stateless delegate (it can be any generic signature)

   public delegate bool RemoteCallback(string sender, object e);

	
   // interface 

   [Guid("B19A2AD2-31F2-4c6e-B5A6-24495670BE02")]
   public interface IRmObject 
   {
      string GiveMeCallback(int timeinsec, string ticketId, object wire);
      string Echo(string msg);
   }

	
   // Callback EventArgs class

   [Serializable]
   public class CallbackEventArgs : EventArgs 
   {
      private string _sender;
      private string _ticketId;
      private object _state;
      public string Sender
      {
         get { return _sender; }
         set { _sender = value;  }
      }
      public string TicketId
      {
         get { return _ticketId; }
         set { _ticketId = value;  }
      }
      public object State 
      {
         get { return _state;  }
         set { _state = value; }
      }
   }
}

There are three parts of the metadata in its assembly: delegator, interface and callback's EventArg class. They can be modified based on needs of the application. Note that interface has been attributed by Guid value to keep its ID the same (The .Net Service will be accepted this Guid each time when object is going to be re-registered into the COM+ catalog)

Callback Custom Attribute

For setup a config of the Remote Callback object on the client side is suitable to use a custom attribute technique. It will allow to hide and reuse all implementation for this private remoting object. I created a separate project - RemoteCallbackLib to handle this solution.  There is a RemoteCallbackAttribute  to config any attributed object (of course derived from the MarshalByRefObject class) for the Remoting purpose.

The following code snippet shows its usage:


 [RemoteCallback("tcp", desc="Callbacks Test")]
 public CallbackClass cb = null;
 // callback Admin

 private RemoteCallbackSubscriber sub = null;  

The RemoteCallback attribute is activated by its subscriber - RemoteCallbackSubscriber, which has to be constructed during the client's initialization:

 sub = new RemoteCallbackSubscriber(this);
 cb.Parent = this;	// we need an access to the parent properties

 

Remoting Method

The method signature of the Remoting object which wants to use the Remoting Callback mechanism includes less two additional arguments such as the  ticketId and objwire as it is shown in the following snippet: 

 public string GiveMeCallback(int timeinsec, string ticketId, object objwire) 

The ticketId represents a cookie value to handle multiple callbacks on the client side. The objwire is a delegate object of the client's callback proxy.

Implementation

The sample solution of the Remote Callbacks implemenation is divided into several projects:

  • ContractObject - common abstract definitions (see the above)
  • RemoteCallbackLib - RemoteCallback Custom Attribute (see the above)
  • RemoteObjectA - classic .Net object hosted in the HostServer
  • RemoteObjectX - .Net Service (COM+) hosted in the HostServer
  • RemoteObjectWS - Web Service hosted in the IIS
  • HostServer - console server program
  • RemoteWindowsForm - Windows Form client program

also the solution included the following config files:

  • HostServer.exe.config - the Remoting config info for objects hosted by HostServer
  • RemoteWindowsForm.exe.config - the config info for Remoting objects using by this client

RemoteCallbackLib

The following code snippet shows the implementation of the RemoteCallbackAttribute. Its design is based on the Refection of the "parent" assembly, where located all metadata of the Callback object. The RemoteCallbackLib is built into the separate assembly which it can be reused by another remoting clients.

namespace RKiss.RemoteCallbackLib
{
   [AttributeUsage(AttributeTargets.Field)]
   public class RemoteCallbackAttribute : Attribute 
   {
      private string _desc;
      private string _protocol;
      private int _port;
      public string desc 
      { 
         get { return _desc; } 
         set {_desc = value; }
      }
      public string protocol 
      { 
         get { return _protocol; } 
         set {_protocol = value; }
      }
      public int port 
      { 
         get { return _port; } 
         set {_port = value; }
      }
      public RemoteCallbackAttribute() : this("tcp", 0) {}
      public RemoteCallbackAttribute(string sProtocol) : this(sProtocol, 0) {}
      public RemoteCallbackAttribute(string sProtocol, int iPort) 
      {
         protocol = sProtocol;
         port = iPort;
      }
   }

   // Creating remoting stuff based on the properties of the CallbackAttribute

   // Note that all callback class' have to located in the same (parent) assembly!

   public class RemoteCallbackSubscriber 
   {
      private Hashtable HT = Hashtable.Synchronized(new Hashtable());
      private string site  = "localhost";
		
      public RemoteCallbackSubscriber(object parent) 
      {
         Type typeParent = parent.GetType();
						
         foreach(FieldInfo fi in typeParent.GetFields()) 
         {
            foreach(Attribute attr in fi.GetCustomAttributes(true)) 
            {
               if(attr is RemoteCallbackAttribute) 
               {
                  RemoteCallbackAttribute rca = attr as RemoteCallbackAttribute;

                  // open in/out channel

                  int port = rca.port;
                  if(port == 0) 
                  {
                     Random rdmPort = new Random(~unchecked((int)DateTime.Now.Ticks)); 
                     port = rdmPort.Next(1, ushort.MaxValue);
                  }

                  // create channel

                  IChannel channel = null;				
                  if(rca.protocol == "tcp")	
                     channel = new TcpChannel(port);
                  else
                  if(rca.protocol == "http")
                     channel = new HttpChannel(port);
                  else
                     throw new Exception(string.Format("The '{0}' is not recognize protocol.", rca.protocol)); 

                  // register channel

                  ChannelServices.RegisterChannel(channel);

                  // register a Callback Object 

                  string nameofCallbackObject = fi.FieldType.FullName;
                  Type typeofCallbackObject = typeParent.Assembly.GetType(nameofCallbackObject);
                  RemotingConfiguration.RegisterWellKnownServiceType(
                              typeofCallbackObject,
                              nameofCallbackObject, 
                              WellKnownObjectMode.Singleton);

                  // create proxy

                  string urlofCallbackObject = string.Format(@"{0}://{1}:{2}/{3}", 
                              rca.protocol, site, port, nameofCallbackObject);
                  object proxyCB = Activator.GetObject(typeofCallbackObject, urlofCallbackObject);
                  fi.SetValue(parent, proxyCB);
                  //

                  //store into the HT

                  HT[fi.Name] = channel;
               }
            }
         }
      }
      public IChannel this [string nameofCallback] 
      {
         get { return HT[nameofCallback] as IChannel; }
      }
   }
}

RemoteObject (A , X and WS)

The design pattern of the Remoting methods is the same for any of these remoting objects. The following code snippet shows the RemoteObjectA class derived from the MarshalByRefObject class and IRmObject interface.

using RKiss.RemoteObject;	// abstract definitions


namespace RKiss.RemoteObjectA
{	
   public class RmObjectA : MarshalByRefObject, IRmObject
   {
      public RmObjectA()
      {
         Trace.WriteLine(string.Format("[{0}]RmObjectA activated", GetHashCode()));	
      }
      public string Echo(string msg)
      {
         Trace.WriteLine(string.Format("[{0}]RmObjectA.Echo({1})", GetHashCode(), msg));
         return msg;
      }
      // stateless callbacks

      public string GiveMeCallback(int timeinsec, string ticketId, object objwire) 
      {
         RemoteCallback wire = objwire as RemoteCallback;
         bool bProgress = true;
         CallbackEventArgs cea = new CallbackEventArgs();
         cea.TicketId = ticketId;
         cea.State = "running ...";
         cea.Sender = GetType().ToString();

         wire(cea.Sender, cea);
         while(timeinsec-- > 0 && bProgress) 
         {
            Thread.Sleep(1000);
            cea.State = timeinsec;
            bProgress = wire(cea.Sender, cea);
         }
         cea.State = bProgress?"done":string.Format("aborted at {0}", ++timeinsec);
         wire(cea.Sender, cea);
         //

        Trace.WriteLine(string.Format("[{0}]RmObjectA.GiveMeCallback({1}, {2}, {3})", 
                        GetHashCode(), timeinsec, ticketId, wire.GetType().FullName));
        return ticketId;
      }
   }
}

There are implementation of two methods of the IRmObject interface in the object. The first one - Echo has a test purpose, the other one - GiveMeCallback simulated some time consuming work with notification of the method state using the Remoting Callback mechanism. The method state is wrapped and serialized by the CallbackEventArgs object.

Now I will show you only differencies in the following remote objects:

RemoteObjectX

This is a .Net Service - transactional and poolable object register into the COM+ catalog and config as a remoting object. There are attributes for assembly and class necessary for this object configuration in the COM+ catalog. The other change, the remote class is derived from the ServicedComponent. This is a standard stuff for the .Net Service configuration.

using RKiss.RemoteObject;	// abstract definitions


[assembly: ApplicationName("RemoteObjectX")]
[assembly: ApplicationID("026B9E80-6B07-45f0-8EBF-BD35B5D3BB77")]
[assembly: Description("Remoting COM+ test")]
[assembly: ApplicationActivation(ActivationOption.Library)]		// accepted by Beta2

[assembly: ApplicationAccessControl(Value = false, Authentication = AuthenticationOption.None)]

namespace RKiss.RemoteObjectX
{
   [Guid("B19A2AD2-31F2-4c6e-B5A6-24495670BE02")]
   [Description("Remoting Object")]
   [ObjectPooling(Enabled=true,MinPoolSize=3,MaxPoolSize=64)]
   [Transaction(TransactionOption.Required)]
   [ConstructionEnabled(Default="server=ATZ-ROMAN;uid=sa;pwd=;database=Logger")]
   [EventTrackingEnabled]
   public class RmObjectX : ServicedComponent, IRmObject
   { 
	...
   }
}

Note that object has to run in the COM+ library only, that's the limitation of the Beta2 and it will be fix it in the RTM version.

RemoteObjectWS

This is a Web Service object generated by wizard and modify for the remoting purpose. The following code snippet shows its boilerplate:

using RKiss.RemoteObject;	// abstract definitions


namespace RemoteObjectWS
{
   public class RmObjectWS : MarshalByRefObject, IRmObject
   {
      public RmObjectWS()
      {
		...
      }

      #region Component Designer generated code
		...
      #endregion

      [WebMethod]
      public string Echo(string msg)
      {
		...
      }
      // stateless callbacks

      [WebMethod]
      public string GiveMeCallback(int timeinsec, string ticketId, object objwire) 
      {
		...
      }
   }
}

Note that this project has to be unzip and move it into the localhost\RemoteObjectWS directory in prior of opening the solution.

HostServer

This is a server program to host a remote object. It's a very simply console program to perform a configuration of the remote object(s). There are two kinds of options for this configuration as I mentioned earlier. The option 1 is commented.

namespace RKiss.ServerActivation 
{
   public class Server 
   {
     public static int Main(string [] args) 
     {
        try
        {
           /* option 1
           TcpChannel chan = new TcpChannel(12345);
           ChannelServices.RegisterChannel(chan);
					
           RemotingConfiguration.RegisterWellKnownServiceType(
                        Type.GetType("RKiss.RemoteObjectA.RmObjectA, RemoteObjectA"), 
                        @"RemoteTest/ObjectA", 
                        WellKnownObjectMode.SingleCall);
					
           RemotingConfiguration.RegisterWellKnownServiceType(
                        Type.GetType("RKiss.RemoteObjectX.RmObjectX, RemoteObjectX"), 
                        @"RemoteTest/ObjectX", 
                        WellKnownObjectMode.Singleton);
				
           */
           // option 2

           RemotingConfiguration.Configure(@"..\..\HostServer.exe.config");
           }
           catch(Exception ex) 
           {
             System.Console.WriteLine(ex.Message);
           }
            
        System.Console.WriteLine("Hit <enter> to exit...");
        System.Console.ReadLine();
        return 0;
     }
   }
}

HostServer.exe.config

This is a configuration file to config the Remoting ObjectA and ObjectX on the Tcp channel port# 12345. Both objects have been choose for server activatation running as a wellknown object in the SingleCall mode. This config file can be modified during the deploying process to match an application environment.

<configuration>
 <system.runtime.remoting>
  <application name="RemoteTest">
   <service>
    <wellknown mode="SingleCall" 
               type="RKiss.RemoteObjectA.RmObjectA, RemoteObjectA" 
               objectUri="ObjectA" />
   </service>
   <service>
    <wellknown mode="SingleCall" 
               type="RKiss.RemoteObjectX.RmObjectX, RemoteObjectX" 
               objectUri="ObjectX" />
   </service>
   <channels>
    <channel type="System.Runtime.Remoting.Channels.Tcp.TcpChannel, System.Runtime.Remoting" 
             port="12345" />
   </channels>
  </application>
 </system.runtime.remoting>
</configuration>

RemoteWindowsForm

This is a client side - consumer of the Remoting objects. The Remoting client can be any object on the Enterprise Network. This sample is using a Windows Form to demonstrate an asynchronously invoking remote objects with callbacks to the Windows Controls.

There are 3 major parts on the client side related to the remoting callbacks:

Initialization

The Initialization part has the following responsibility for the remoting process and connectivity:

  • subscribing  (config) a callback object as a remoting object
  • creating proxy objects of the requested remote objects based on the configuration file
  • making an Echo test on the remoting objects

The following code snippet shows that:

public Form1()
{
   InitializeComponent();

   try 
   {
      sub = new RemoteCallbackSubscriber(this);
      cb.Parent = this;	// we need an access to the parent properties


      // get the remoting uriAddress

      NameValueCollection uriAddr = (NameValueCollection)ConfigurationSettings.GetConfig("Client/urlAddress");
      string uriObjectA = (string)uriAddr["objectA"];
      string uriObjectX = (string)uriAddr["objectX"];
      string uriObjectWS = (string)uriAddr["objectWS"];

      // type of the Remote Interface

      Type typeofRI = typeof(RKiss.RemoteObject.IRmObject);

      // create a proxy of the remote objects

      roA = (IRmObject)Activator.GetObject(typeofRI, uriObjectA);
      roX = (IRmObject)Activator.GetObject(typeofRI, uriObjectX);
      roWS = (IRmObject)Activator.GetObject(typeofRI, uriObjectWS); 
      //

      // (sub["cb"] as TcpChannel).StartListening(0);	// sample how to control a Tcp channel


      // echo test

      textBoxStatus.Text = roWS.Echo(roA.Echo(roX.Echo("This is an ECHO Message")));
   }
   catch(Exception ex)
   {
      textBoxStatus.Text = ex.Message;
      buttonRM.Hide();
   }
}

Callback object

The callback object is the remoting object for the client's remoting objects, that's why it is derived from the MarshalByRefObject class. The callback object has been initiated for infinity lease time (actually its life time is depended from the client's life time). There is one callback method - Progress with the arguments (state) passed from the remote object. Based on this state, the DispatchEvent helper calls a particular Windows Control. Note that callback method is running in the "fire&forget" fashion to isolated processes and minimize its respond time. The callback object holding a state of the progressing and passing back to the remote object which it will allow to abort the remoting call. This a a great feature of the remoting callbacks to make the remote call pre-emptive.

// -----------< Callback class >-------------------------------------------

public class CallbackClass : MarshalByRefObject
{	
   private Form1 _parent = null;
   private bool  _state = false;
   public Form1 Parent	{ set { _parent = value; }}
   public bool State   { set { lock(this) { _state = value; }}}
   // set a leaseing time for infinity

   public override Object InitializeLifetimeService()
   {
     ILease lease = (ILease)base.InitializeLifetimeService();
     if(lease.CurrentState == LeaseState.Initial)  
     {
        lease.InitialLeaseTime = TimeSpan.FromMinutes(0);
     }
     return lease;
   }
   //

   // CallbackMethod

   public bool Progress(string sender, object e) 
   {
      // fire&forget

      ThreadPool.QueueUserWorkItem(new WaitCallback(DispatchEvent), e);
      return _state;
   }
   // helper

   private void DispatchEvent(object e) 
   {
      CallbackEventArgs cea = e as CallbackEventArgs;
      // a simple work with the parent's control

      lock(this) 
      {
         if(cea.TicketId == "ProgressBar") 
         {
            if(cea.State is int)
            {
               _parent.TextBoxStatus.Text = cea.State.ToString();
               _parent.ProgressBarStatus.PerformStep();
            }
            else
            if(cea.State is string)
               _parent.TextBoxStatus.Text = cea.State.ToString();
         }
         else
         if(cea.TicketId == "TextBox") 
         {
            if(_parent.TextBoxStatus.BackColor == Color.Yellow)
               _parent.TextBoxStatus.BackColor = Color.White;
            else
               _parent.TextBoxStatus.BackColor = Color.Yellow;
         }
         else
         if(cea.TicketId == "Button") 
         {
         if(_parent.ButtonRM.ForeColor == Color.Magenta)
            _parent.ButtonRM.ForeColor = Color.Black;
         else
            _parent.ButtonRM.ForeColor = Color.Magenta;
         }
      }
   }
} // end of the CallbackClass

Invoking the Remote Method

This code snippet shows invoking a method on the Remoting objects using the asynchronous design pattern. Clicking on the button Run/Abort the following code snippet is going to be perform:

private void buttonRM_Click(object sender, System.EventArgs e)
{
   try 
   {
      if(buttonRM.Text == "Run") 
      {
         // client state

         buttonRM.Text = "Busy";
         progressBarStatus.Value = 0;
         textBoxStatus.Text = string.Empty;
         cb.State = true;
         //

         // arguments

         int timeinsec = 10;
         string ticketId = "ProgressBar";
         RemoteCallback wire = new RemoteCallback(cb.Progress);
         //

         // invoking method on the objectA

         AsyncCallback acbA = new AsyncCallback(asyncCallBackA);
         DelegateGiveMeCallback dA = new DelegateGiveMeCallback(roA.GiveMeCallback);
         IAsyncResult arA = dA.BeginInvoke(timeinsec, ticketId, wire, acbA, null);
         //

         // invoking method on the objectX

         AsyncCallback acbX = new AsyncCallback(asyncCallBackX);
         DelegateGiveMeCallback dX = new DelegateGiveMeCallback(roX.GiveMeCallback);
         IAsyncResult arX = dX.BeginInvoke(timeinsec, "TextBox", wire, acbX, null);
         //

         // invoking method on the objectWS

         AsyncCallback acbWS = new AsyncCallback(asyncCallBackWS);
         DelegateGiveMeCallback dWS = new DelegateGiveMeCallback(roWS.GiveMeCallback);
         IAsyncResult arWS = dWS.BeginInvoke(timeinsec, "Button", wire, acbWS, null);
         //

         buttonRM.Text = "Abort";
      }
      else
      if(buttonRM.Text == "Abort")
      {
         cb.State = false;
      }
   }
   catch(Exception ex)
   {
      textBoxStatus.Text = ex.Message;
      buttonRM.Text = "Run";
   }
}
// async result from the remoting objects

private void asyncCallBackA(IAsyncResult ar)
{
   DelegateGiveMeCallback d = (DelegateGiveMeCallback)((AsyncResult)ar).AsyncDelegate;
   object o = d.EndInvoke(ar);
   buttonRM.Text = "Run";
}
private void asyncCallBackX(IAsyncResult ar)
{
   DelegateGiveMeCallback d = (DelegateGiveMeCallback)((AsyncResult)ar).AsyncDelegate;
   object o = d.EndInvoke(ar);
   buttonRM.Text = "Run";
}
private void asyncCallBackWS(IAsyncResult ar)
{
   DelegateGiveMeCallback d = (DelegateGiveMeCallback)((AsyncResult)ar).AsyncDelegate;
   object o = d.EndInvoke(ar);
   buttonRM.Text = "Run";
}

The design implementation is the same for any remoting object. It's starting to create a callback delegator - RemoteCallback for the specified callback method (Progress). Secondly, the AsyncCallBack is initiated for the particular handler function (for instance; asyncCallBackA). This function is going to be called at the end of the remoting call. Next step is to create a delegator of the Remoting method for the asynchronous call. As a last step is invoking a delegator method using the BeginInvoke function.

Note that all calls are processing without any blocking, so the Windows Control thread can be yielded and be ready to process any event such as callbacks, user interface, asyncCallBacks, etc. When the remoting is done, the post-call function (handler) is called to finish and resulting the remoting method.

RemoteWindowsForm.exe.config

This is a client configuration file. As you can see it's a different from the server side and also from the standard one. There is no section for the system.runtime.remoting. I created a custom section Client\urlAddress where located  an objecturi address of the remote object which client wants to talk. 

<configuration>
 <configSections>
  <sectionGroup name="Client">
   <section name="urlAddress" type="System.Configuration.NameValueSectionHandler,System" />
  </sectionGroup>
 </configSections>

 <Client>
  <urlAddress>
   <add key="objectA" value="tcp://localhost:12345/RemoteTest/ObjectA" />
   <add key="objectX" value="tcp://localhost:12345/RemoteTest/ObjectX" />
   <add key="objectWS" value="http://localhost/RemoteObjectWS/RemoteObjectWS.soap" />
  </urlAddress>
 </Client>
</configuration>

Test

Testing of the Remoting Callbacks solution needs to install properly Web Service and .Net Service projects. The package has included a batch files to make their installation easy. Please, look at them an change their pathname related to your setup environment if it's necessary. After that, launch the client program (RemoteWindowsForm) and you should see the following form:

The Echo Message has been nested trough all of three remote objects. This is an indication that your connectivity with the remote objects are valid. Now click the button Run to process the remoting callbacks.

As you can see, each remote object will notify its control on the Window Form such as the progress bar, text and button colors. Pressing the button Abort the remoting process will be aborted on the all remoting objects. After these simple test, lunch more clients (for instance five) and repeat the above test simultaneously for all of them. I hope everything is working well like on my machine.

Conclusion

Using the Remoting Callback in the .Net Application enhancing its event driven model, making more pre-emptive and realistic. As typically example is downloading a large file or query database. In this article I described the capability of the .Net Remoting and Reflexion which they play very significant role in the .Net Enterprise Solutions.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here