This
example is a serveractivated sample, and the client will not be using the new
operator, only Activator.GetObject(). The reason for this is to demonstrate the
use of interfaces in remoting, and interfaces can't be created. The server has
access to the actual implementation, while the client references the interface.
Trying to use new or Activator.CreateInstance() on the client results in
compiler errors. Because of the use of properties, the server will register the
remote objects in Singleton mode. In that manner, the objects are able to
maintain state. The example consists of a class library (SimpleInterfaceLib) and
two console applications (ClientInterfaceExe and ServerInterfaceExe). Don't
forget to add references to System.Runtime.Remoting.dll and
SimpleInterfaceLib.dll to the console applications.
You have looked at remote objects only from a
marshal-by-reference perspective. Marshal-by-reference classes must inherit from MarsahlByRefObject. In this section we also look at another type of remotable
object, marshal by value. Marshal-by-value classes are declared by using the [Serializable]
attribute or implementing the System.Runtime.Serialization.ISerializable
interface. These objects are copied (serialized) across the wire and are
reconstituted at the other end (deserialized). The objects are independent of
each other, and modifying one doesn't change the other. The SimpleInterfaceLib
code is shown in Listing 25.7.
Listing 25.7: SimpleInterface.vb
Imports System
Imports
System.Collections
Namespace
SimpleInterfaceLib
Public Interface
ISimpleInterface
Default
ReadOnly Property Item(ByVal
index As Integer)
As String
WriteOnly Property Names() As
ArrayList
Function InvokeMethod(ByRef
param As SimpleParam)
As SimpleMarshal
End Interface
End Namespace
The areas of note here are the use of the this and the ref keywords. The this
keyword, in this context, is used as an indexer. The ref keyword means that any
changes to param in the InvokeMethod will be reflected in the original object.
There is also an out keyword, not used here, as a parameter modifier. If we were
using the out keyword, in this case, param would be passed in as a null, and the
object would be created on the server and assigned to param.
Listing 25.8: SimpleMarshal.vb and SimpleParam.vb
Public Class SimpleMarshal
Inherits MarshalByRefObject
Private m_name
As String =
Nothing
Public
Sub New()
Console.WriteLine("In SimpleMarshal
constructor")
End Sub
Public Property
Name() As String
Get
Console.WriteLine("SimpleMarshal.Name.get")
Return (m_name)
End Get
Set(ByVal
value As String)
Console.WriteLine("SimpleMarshal.Name.set")
m_name = value
End Set
End
Property
End Class
<Serializable()> _
Public Class
SimpleParam
Private m_arrayNames
As ArrayList =
Nothing
Public
Sub New()
End Sub
Public Property
ArrayNames() As ArrayList
Get
Console.WriteLine("SimpleParam.ArrayNames.get")
Return (m_arrayNames)
End Get
Set(ByVal
value As ArrayList)
Console.WriteLine("SimpleParam.ArrayNames.set")
m_arrayNames = value
End Set
End Property
In Listing 25.8 the SimpleMarshal class derives from MarshalByRefObject. The
SimpleParam class definition uses the [Serializable] attribute. The
ISerializable interface could have been used in place of the attribute. Running
this code on the server, results in the output shown in Figure 25.8.
Figure 25.8: ServerInterfaceExe Output
Listing 25.9: ServerInterfaceExe.vb
Namespace
ServerInterfaceExe
Class ServerInterface
Private
Shared Function Main(ByVal
args As String())
As Integer
Dim http
As HttpChannel =
Nothing
http = New HttpChannel(1234)
ChannelServices.RegisterChannel(http)
RemotingConfiguration.RegisterWellKnownServiceType(GetType(SimpleInterfaceImpl),
"InterfaceImpl",
WellKnownObjectMode.Singleton)
Console.WriteLine("Press <enter> to
exit.")
Console.ReadLine()
Return (0)
End Function
End Class
Public Class
SimpleInterfaceImpl
Inherits MarshalByRefObject
Implements ISimpleInterface
Private arrayNames
As New
ArrayList()
Public Sub New()
MyBase.New()
Console.WriteLine("SimpleInterfImpl
const")
End Sub
Default Public ReadOnly Property Item(ByVal
index As Integer)
As String
Get
Dim ret
As String =
Nothing
If arrayNames.Count = 0
Then
ret = "No names in list"
ElseIf index >=
arrayNames.Count Then
ret = "Index beyond end
of list."
Else
ret = DirectCast(arrayNames(index),
String)
End
If
Console.WriteLine("SimpleInterfImpl
indexer")
Return (ret)
End Get
End
Property
Public
WriteOnly Property Names()
As ArrayList
Set(ByVal
value As ArrayList)
arrayNames = value
End Set
End
Property
Public
Function InvokeMethod(ByRef param
As SimpleParam) As
SimpleMarshal
Dim arrayTmp
As New
ArrayList()
Dim arrayParam
As ArrayList =
Nothing
arrayParam = param.ArrayNames
For x As Integer = 0 To
arrayParam.Count - 1
arrayTmp.Add(arrayParam(x) + "
InvokeMethod")
Next
param.ArrayNames = arrayTmp
Console.WriteLine("SimpleInterfImpl.InvokeMethod()")
Dim paramNew
As New
SimpleMarshal()
paramNew.Name = "SimplMarshal from
MarshalByRefObj"
Return (paramNew)
End Function
End Class
End Namespace
In the main() in Listing 25.9, the code is essentially the same as in our first
example. The only changes have been the use of HttpChannel instead of
HttpServerChannel and the switch to Singleton mode. HttpChannel combines the
functionality of HttpServerChannel and HttpClientChannel. HttpServerChannel
receives messages, while HttpClientChannel sends messages. HttpChannel
transports messages in either direction.
Just below the main(), the SimpleInterfaceImpl class is defined. It inherits
from MarshalByRefObject and ISimpleInterface, by implementing the indexer and
Names properties and the InvokeMethod(). When designing remote objects, it is
worth considering generic interfaces for their potential reuse.
Figure 25.9: ClientInterfaceExe Output
Listing 25.10: ClientInterface.vb
Private Shared Function
Main(ByVal args As String()) As Integer
Dim http
As HttpChannel = Nothing
http = New HttpChannel()
ChannelServices.RegisterChannel(http)
Dim simple As
ISimpleInterface = Nothing
simple = DirectCast(Activator.GetObject(GetType(ISimpleInterface),
"http://localhost:1234/InterfaceImpl"),
ISimpleInterface)
Dim arrayParam As New ArrayList()
arrayParam.Add("Sam")
arrayParam.Add("Tom")
arrayParam.Add("Heidi")
arrayParam.Add("Rose")
arrayParam.Add("Babe")
simple.Names = arrayParam
Dim param1 As
SimpleParam = Nothing
param1 = New SimpleParam()
param1.ArrayNames = DirectCast(arrayParam.Clone(),
ArrayList)
Dim param2 As
SimpleMarshal = Nothing
param2 = simple.InvokeMethod(param1)
Dim arrayRet As
ArrayList = Nothing
arrayRet = param1.ArrayNames
For x As Integer = 0 To
arrayRet.Count - 1
Console.WriteLine("indexer {0} - ref {1}.",
simple(x), arrayRet(x))
Next
Console.WriteLine("SimpleMarshal
""{0}""", param2.Name)
Return (0)
End Function
In Listing 25.10, the param2 returned by InvokeMethod() is a transparent proxy. The client's output is captured in Figure 25.9.
Conclusion
Hope this article would have helped you in understanding
Using Objects as Parameters and Return Types in Remoting using VB.NET.