Remote procedure call (RPC), the Component Object Model (COM), the Distributed
Component Object Model (DCOM), Common Object Request Broker Architecture
(CORBA), and remote method invocation (RMI) are technologies that provide a
means to transfer data across boundaries, in what has come to be called
distributed applications. A boundary can be another process, a different
machine, a remote network, or, in the case of VB.NET, an application domain.
Generally, distributed applications can be simply illustrated as shown in Figure
25.1.
Figure 25.1: Typical Distributed Application

The VB.NET Remoting Framework provides an extensive array of classes enabling
developers to create distributed applications with minimal effort and
complexity. The framework is also highly extensible, presenting developers with
a wide range of customization options that extend to Internet solutions.
The .NET Framework Developer Specifications, published by Microsoft, describes
VB.NET Remoting as enabling Web Services Anywhere and CLR (Common Language
Runtime) Object Remoting. Web Services Anywhere means that remoting
functionality and services can be used by any application over any transport and
can employ any encoding scheme. CLR Object Remoting rides on top of Web Services
Anywhere. It provides all the language semantics normally associated with a
local object, such as constructors, delegates, the new operator, and overloaded
methods. Activation, lease-based lifetime, distributed identity, and call
context are also dealt with in CLR Object Remoting.
The remoting framework comprises a large segment of the System.Runtime
namespace. Remoting encompasses 11 namespaces containing in excess of 50 classes
and interfaces. The wealth of material in the remoting framework deserves an
entire book. Unfortunately, the time and space allotted to this article permit
only a survey of a few of those namespaces, classes, and interfaces.
This article explores VB.NET Remoting and relies heavily on examples to
demonstrate various topics. Among the subjects highlighted are these:
- Remoting components
- A simple client/server sample
- Lifetime management
- Passing objects and collections as method parameters and return types
- Remoting events and delegates
- Call contexts
- Configuration files in remoting
- Remoting over the Web
- Creating a custom sink
VB.NET Remoting Components
At its core the VB.Net Remoting Framework communicates between server and client
objects via object references. To qualify as a remote object, the class must
derive from System.MarshalByRefObject. Much in the manner of COM, the remoting
system uses proxies in the client's application domain. A proxy is an abstract
representation of the actual remote object on the server. In the case of .NET,
two proxies are created-a transparent proxy and a real proxy. The transparent
proxy is the remote object reference returned to the client. The real proxy
handles the forwarding of messages to the channel. Sound confusing? The diagram
in Figure 25.2 will, hopefully, make the sequencing clearer.
The transparent proxy is returned when the client activates a remote object
using the new operator or a System.Activator method. All method calls on the
proxy are intercepted by the runtime to ensure the call is a valid method or
property on the remote object. When a remote object's method is called, the
transparent proxy calls System.Runtime.Remoting.Proxies.RealProxy.Invoke() with
a System.Runtime.Remoting.Messaging.IMessage interface, which contains the
called method's parameter types and their values. The actual implementation is
the private Message class in mscorlib.dll.
A real proxy is created along with the transparent proxy. To create a customized
proxy, a developer derives the class from the RealProxy class and overrides
Invoke().The real proxy's primary responsibility is to forward messages to the
channel. It does this by first creating a message sink on the channel with the
call System.Runtime.Remoting.Channels.IChannelSender.CreateMessageSink and then
storing the returned IMessageSink object. Then in the Invoke method, it calls
either the message sink's SyncProcessMessage() or AsyncProcessMessage().
Figure 25.2: Remoting Sequence

The Channel Sink and Formatter Sink boxes in the diagram are somewhat misleading
in two ways. First, neither is an independent entity but rather a member of the
channel. Second, sinks are linked together in a chain. Each member in the chain
retains a reference to the next sink in the chain. In the client's application
domain, the first sink is a formatter and the last sink is the transport sink.
In the server's domain the order is reversed. Between the formatter and
transport sinks, customized user sinks may be dropped into the chain. Each sink
in the chain implements the ProcessMessage() and AsyncProcessMessage() methods
and is responsible for calling the respective method on the next sink in the
chain.
The function of formatter sinks is to serialize and deserialize IMessage to and
from a stream. The client must implement IClientChannelSink, while the server
implements IServerChannelSink.
The transport sink is responsible for transporting the serialized message.
A channel can receive messages, send messages, or both. Every channel derives
from IChannel. Depending on the channel's purpose, it must also inherit
IChannelReceiver or IChannelSender interfaces, or both.
One question you may have is how one can, as we asserted at the outset, "create
distributed applications with minimal effort and complexity," given the number
of namespaces, classes, and interfaces in the preceding description. Why have
the .NET designers taken something simple like COM and made it complicated? The
answer is, they haven't. .NET ships with two remoting types that support HTTP
and TCP/IP protocols. Each provides a client channel, a server channel, and a
combined sender-receiver channel. The default formatters support binary and
Simple Object Access Protocol (SOAP) serialization.
At this point, it would be beneficial to demonstrate these concepts with an
example.
A Server-Activated Example
The projects are in the Sample1 folder and consist of a class library (SimpleObjectLib)
and two console applications (ServerActivatedServerExe and
ServerActivatedClientExe). The console projects should add a reference to
System.Runtime.Remoting.dll and SimpleObjectLib.dll. This section presents the
code with few remarks.
Listing 25.1: The SimpleObject Code
Namespace
SimpleObjectLib
Public Class
SimpleObject
Inherits MarshalByRefObject
Public Sub New()
MyBase.New()
Console.WriteLine("In SimpleObject
constructor.")
End Sub
Public Function
ConCatString(ByVal first
As String,
ByVal second As String) As String
Dim concat
As String =
Nothing
Console.WriteLine("In
SimpleObject.ConCatString method.")
concat = first & " " & second
Return (concat)
End Function
End Class
End Namespace
Notice in Listing 25.1 that SimpleObject derives from MarshalByRefObject. Any
class that is a candidate for remoting must derive from MarshalByRefObject. The
output for the example is shown below in Figure 25.3.
Figure 25.3: The ServerActivatedServerExe Output

Listing 25.2: The ServerActivatedServerExe Code
Imports
System.Runtime.Remoting
Imports
System.Runtime.Remoting.Channels
Imports
System.Runtime.Remoting.Channels.Http
Imports
SimpleObjectLib
Namespace
ServerActivatedServerExe
Class ServerActivatedServer
Private
Shared Sub Main(ByVal
args As String())
Dim http As
HttpServerChannel
=
Nothing
http = New
HttpServerChannel(1234)
ChannelServices.RegisterChannel(http)
RemotingConfiguration.RegisterWellKnownServiceType(GetType(SimpleObject),
"Simple",
WellKnownObjectMode.SingleCall)
Console.WriteLine("Press <enter> to
exit.")
Console.ReadLine()
End Sub
End Class
End Namespace
The server code in Listing 25.2 is simple and straightforward. An instance of a
System.Runtime.Remoting.Channels.Http.HttpServerChannel is created, specifying
the port it will listen on. Once the channel has been instantiated, it registers
itself with the static method ChannelServices.RegisterChannel(). Finally, the
remote object is registered with the static method
RemotingConfiguration.RegisterWellKnownServiceType(). The parameters specify the
remote object, the URI (Uniform Resource Identifier), and the calling mode
enumeration. The Console.ReadLine() keeps the server up and running. When the
process ends, all channels and registered objects are dropped by remoting
services.
Figure 25.4: The ServerActivatedClientExe Output

Listing 25.3: The ServerActivatedClientExe Code
Imports
System.Runtime.Remoting
Imports
System.Runtime.Remoting.Channels
Imports
System.Runtime.Remoting.Channels.Http
Imports
SimpleObjectLib
Namespace
ServerActivatedClientExe
Class ServerActivatedClient
Private
Shared Function Main(ByVal
args As String())
As Integer
Dim http
As HttpClientChannel =
Nothing
http = New HttpClientChannel()
ChannelServices.RegisterChannel(http)
Dim simple1
As SimpleObject =
Nothing
simple1 = DirectCast(Activator.GetObject(GetType(SimpleObject),
"http://localhost:1234/Simple"), SimpleObject)
Dim ret
As String =
Nothing
ret = simple1.ConCatString("using",
"Activator.GetObject")
Console.WriteLine(ret)
Dim simple2
As SimpleObject =
Nothing
RemotingConfiguration.RegisterWellKnownClientType(GetType(SimpleObject),
"http://localhost:1234/Simple")
simple2 = New SimpleObject()
ret = simple2.ConCatString("using the
""new""", "operator")
Console.WriteLine(ret)
Return (0)
End Function
End Class
End Namespace
In the client example shown in Listing 25.3 and it's corresponding output in
Figure 25.4, two ways of obtaining a remote reference are demonstrated: using
the Activator and the new operator. Also available to developers is a
System.Runtime.Remoting.RemotingServices overloaded method, Connect(). A channel
is created and registered. This time, it is an HttpClientChannel. The default
constructor attaches to any available port. In fact, if you look at the
HttpClientChannel constructors in the .NET class library, a port number
specification isn't an option. The static method Activator.GetObject() returns a
transparent proxy for SimpleObject. The parameters specify the remote object
type and the Uniform Resource Locator (URL). To use the new operator to activate
remote objects, you must register the object by calling
RemotingConfiguration.RegisterWellKnownClientType(). The parameters are the
object type and the URL. Calling new on SimpleObject returns a transparent
proxy. It should be noted, that calling the new operator without registering
with RemoteConfiguration will work, but a local reference is returned, not a
transparent proxy.
As is evident from the preceding example, .NET designers have abstracted much of
the remoting complexity and hidden many of the implementation details. The
preceding example and a clientactivated example, encountered later in the next
article, probably offer a solution for 60 to 70 percent of problem domains.
While it isn't essential to understand what is actually going on "under the
covers," for those situations where portions of the framework must be adapted or
extended, knowledge of the underlying architecture is required.
The server starts up, creates a channel and registers itself, then registers a
"well-known type" with remoting configuration. Looking at the server's initial
output, Figure 25.3, nothing has been accomplished except the Console.WriteLine()
Press <enter> to exit. What has happened? The channel starts listening for
client connection requests on construction. This is established by looking into
the _tcpListener member, an instance of System.Net.Sockets.TcpListener, of the
HTTP channel, and determining that the Active property is true. It also can be
determined that the remote object has been registered with the configuration
services by calling the method
System.Runtime.Remoting.RemotingConfiguration.GetRegisteredWellKnownServiceTypes(),
which returns a WellKnownServiceTypeEntry[] with a list of registered types.
SimpleObject is in the list but not yet instantiated.
Start the client. Channels are created and registered, and the
Activator.GetObject() is called. No communication has taken place yet between
the client and the server. Activator.GetObject() has simply returned a local
instance of the transparent proxy. Only when simple1.ConCatString() is called,
does the server's output console begin to show signs of life.
Figure 25.5a: The ServerActivatedServerExe Output

Notice that SimpleObject's constructor (see Figure 25.5a) is called three times,
whereas the ConCatString() is called twice. There are a number of things to do
in discovering what is actually taking place, but this is a good point to
digress for a moment.
If you look at the server code, the call RemotingConfiguration.RegisterWellKnown
had as a parameter WellKnownObjectMode.SingleCall. It is an enumeration with two
values, SingleCall and Singleton. Using SingleCall creates a new instance of the
object for every method call. Singleton mode services all object requests with
the same instance. If the mode in the server's code were changed, the server
output would look like that shown in Figure 25.5b.
Figure 25.5b: Server Output with Mode Change

Now, back to tracing remote object activation, by doing the following:
- Override MarshalByRefObject's CreateObjRef() in the SimpleObject to aid in tracking the sequence of events.
- MyTrackingHandler in SimpleObjectLib is derived from System.Runtime.Remoting.Services.ITrackingHandler, a utility interface to trace remote object marshaling.
- In the server code, register the tracking handler using System.Runtime.Remoting.Services.TrackingServices.RegisterTrackingHandler().
As the client code calls
simple1.ConCatString(), the server creates an instance of SimpleObject(),
followed by a call to SimpleObject.CreateObjRef(). By registering a
MyTrackingHandler instance, you can see that the remoting subsystem then
marshals SimpleObject to obtain a System.Runtime.Remoting.ObjRef.
This ObjRef is a representation of the remote object, including the class's
strong name, hierarchy, implemented interfaces, and URI. It also contains
details of all the available registered channels. The server's remoting
framework stores this reference in a table to track registered objects. With
server-activated remote objects, the ObjRef is created only once during the
lifetime of the server. This is in contrast to client-activated objects, which
we'll encounter next.
Once the server has created and stored the ObjRef, the first instance of
SimpleObject is ready for the Garbage Collector (GC). At this point, the server
turns to servicing the client's request with a call to SimpleObject(), and then
ConCatString(). What has been established? Although this is explained in
Microsoft's .NET Remoting: A Technical Overview, we have used a number of the
remoting helper classes and interfaces to confirm a number of points:
- No actual communication takes place between the client and the server until a method or property is called on the remote object.
- Server-activated remote objects registered in SingleCall mode must remain stateless, as there is a new instance with every method call. Singleton mode remote objects could maintain state, but only one instance services all clients, and therefore they should also remain stateless, because of probable corruption.
- An ObjRef is created only once during a server's lifetime and that is used for all subsequent method calls.
While the preceding holds true for
server-activated remote objects, this is not the case for clientactivated
objects. But before proceeding, it was stated earlier that a real proxy could be
customized.
A Client-Activated Example - remoting
The projects are in the Sample1 folder. The two console applications are
ClientActivatedServerExe and ClientActivatedClientExe. Again, add a reference to
System.Runtime.Remoting.dll and SimpleObjectLib.dll. SimpleObject has remained
unchanged, so we need not bother with the code. Notice that the protocol has
been changed to TcpServerChannel and TcpClientChannel. The server's output is
shown in Figure 25.6.
Figure 25.6: The ClientActivatedServerExe Output

Listing 25.4: The ClientActivatedServerExe Code
Imports
System.Runtime.Remoting.Channels.Tcp
Namespace
ClientActivatedServerExe
Class ClientActivatedServer
Private
Shared Sub Main(ByVal
args As String())
Dim tcp
As TcpServerChannel = Nothing
tcp = New
TcpServerChannel(1234)
ChannelServices.RegisterChannel(tcp)
RemotingConfiguration.ApplicationName =
"Simple"
RemotingConfiguration.RegisterActivatedServiceType(GetType(SimpleObject))
Console.WriteLine("Press <enter> to
exit.")
Console.ReadLine()
End Sub
End Class
End Namespace
This time, a System.Runtime.Remoting.Channels.Tcp.TcpServerChannel is created
and registered (see Listing 25.4). The RemotingConfiguration.ApplicationName is
Simple, which specifies the URI. In this scenario, server registration of a
client-activated object using RemotingConfiguration employs the
RegisterActivatedServiceType() method. Then the server waits for a client to
connect.
Figure 25.7: The ClientActivatedClientExe

Listing 25.5: The ClientActivatedClientExe Code
Shared Sub Main(ByVal
args As String())
Dim tcp As
TcpClientChannel = Nothing
tcp = New TcpClientChannel()
ChannelServices.RegisterChannel(tcp)
Dim urls As
UrlAttribute() = New UrlAttribute(0) {}
Dim url As New UrlAttribute("tcp://localhost:1234/Simple")
Dim handle As
ObjectHandle = Nothing
urls(0) = url
handle = Activator.CreateInstance("SimpleObjectLib",
"SimpleObjectLib.SimpleObject", urls)
Dim simple1 As
SimpleObject = Nothing
Dim ret As String = Nothing
simple1 = DirectCast(handle.Unwrap(),
SimpleObject)
ret = simple1.ConCatString("using",
"Activator.CreateInstance")
Console.WriteLine(ret)
Dim simple2 As
SimpleObject = Nothing
RemotingConfiguration.RegisterActivatedClientType(GetType(SimpleObject),
"tcp://localhost:1234/Simple")
simple2 = New SimpleObject()
ret = simple2.ConCatString("using new",
"operator")
Console.WriteLine(ret)
Return (0)
End Sub
The client code in Listing 25.5 uses Activator.CreateInstance() and the new
operator to create instances of SimpleObject. Notice that CreateInstance()
returns a System.Runtime.Remoting.ObjectHandle rather than a transparent proxy.
To obtain the proxy, Unwrap() must be called before accessing SimpleObject's
method. The second instance of the SimpleObject proxy is created after calling
RemotingConfiguration's RegisterActivatedClientType() method. The client's
output can be seen in Figure 25.7.
As in the first example, Listing 25.2, the server creates and registers its
channels. When it registers the remote object with remoting configuration, no
mode is specified, only the type. This is because the client controls the
lifetime of each instance through leasing, discussed later in this chapter. This
also means that the client-activated remote objects can have state.
Upon calling RegisterActivatedServiceType(), with the aid of ITrackingHandler,
an ObjRef of ActivationListener type is marshaled. This class isn't listed in
the .NET class library under the System.Runtime.Remoting.Activation namespace,
but it can be found, using the MSIL Disassembler, in the mscorlib.dll in the
Activation namespace. It implements the System.Runtime.Remoting.IActivator
interface and contains channel and SimpleObject information.
Once the client has started up, compare the server output of Figures 25.3 and
25.6 (duplicated in Figures 25.8 and 25.9).
Figure 25.8: Server Output Shown Again


Figure 25.9: Server Output Shown Again


Notice on the server's console screen that the SimpleObject constructor for
client-activated objects is called for each creation request. This is also true
for the CreateObjRef() method. Remember that server-activated objects require a
single ObjRef during the lifetime of the server. Because a client controls the
lifetime of the remote object, each instance of the object requires an ObjRef.
Using ITrackingHandler, we discover that an ObjRef is marshaled by the server
and streamed to the client, where it is unmarshaled to create a transparent
proxy. This happens each time the client activates SimpleObject. Also, unlike
server-activated objects, which activate the remote objects only when method
calls are made, client-activated objects are activated when
Activator.CreateInstance() or new is called.
Conclusion
Hope this article would have helped you in understanding the
VB.NET Remoting.