February 16, 2019
Hot Topics:

.NET Remoting and Event Handling in VB .NET

  • December 29, 2004
  • By Paul Kimmel
  • Send Email »
  • More Articles »

Writing the Shared Code

At this point your code will compile but not run. You need to define the Singleton remote service described in the App.config file, the Chatter.

There are a lot of ways to implement remotable objects. If serialization is used then you are basically mirroring what XML Web Services do. If you want a proxy reference to an object that resides on the server then you need a MarshalByRefObject. For the program you are creating, you want the latter. You want the clients to be able to get a handle to your remote service, store and address to your client's event handler, and invoke operations on that service. In general terms your remote service needs to:

  • Permit clients to begin listening for messages
  • Permit clients to send messages
  • And, then send that message to all listening clients

Collectively these elements are supported by a custom class that inherits from EventArgs. This custom EventArgs derivative is used to contain your message. You need a new delegate that accepts the custom event arguments, and you need the Chatter class. The Chatter class is the remotable object. You will put all of these elements in a new Class Library project because these definitions must be shared between client and server.

Defining the Custom Event Arguments

The custom event arguments include the sender and that sender's message. Because this object is sent between client and server it has to be remotable. To make the class remotable you can apply the SerializableAttribute to make the event arguments serializable, marshal—by—value objects, that is remotable. Listing 3 contains the implementation of the custom event arguments.

Listing 3: Our serializable event arguments class, ChatEventArgs.

<alizable()> _
Public Class ChatEventArgs
    Inherits System.EventArgs

    Private FSender As String
    Private FMessage As String

    Public Sub New()
    End Sub

    Public Sub New(ByVal sender As String, _
      ByVal message As String)
        FSender = sender
        FMessage = message
    End Sub

    Public ReadOnly Property Sender() As String
            Return FSender
        End Get
    End Property

    Public ReadOnly Property Message() As String
            Return FMessage
        End Get
    End Property
End Class

Next a delegate that accepts your new event arguments type needs to be defined.

Declaring the Delegate

.NET uses multicast delegates to manage events. A multicast delegate is really a list that can contain function pointers with a specific signature. What you are doing when you define a delegate is implicitly defining a class that can contain a list of function pointers that have the same number, order, and type of arguments, that is the addresses of event handlers. The delegate I defined for our new event arguments class is show here:

Public Delegate Sub MessageEventHandler(ByVal Sender As Object, _
  ByVal e As ChatEventArgs)

Implementing the Chatter Class

The Chatter is remotable. The basic behavior is that clients assign their message event handler to an instance of the Chatter's public event property. Then, each client can call a Send method and the server notifies clients of a message via their event handlers. I implemented Send to store a copy of the message and then raise the MessageEvent to send the message to all of the registered clients.

There are some interesting features in the Chatter class. I will go over those elements after the code in listing 4.

Listing 4: Our remotable Chatter class which tracks messages send and recipients using a delegate.

Imports System.Collections
Imports System.Runtime.Remoting
Imports System.Runtime.Remoting.Messaging
Imports System.Runtime.Remoting.Lifetime

Public Class Chatter
    Inherits MarshalByRefObject

    Private history As Queue = New Queue

    Public Event MessageEvent As MessageEventHandler

    Public Overrides Function InitializeLifetimeService() As Object
        Return Nothing
    End Function

    Public Sub New()

    End Sub

    <OneWay()> _
    Public Sub Send(ByVal sender As String, _
    ByVal message As String)

        Console.WriteLine(New String("—"c, 80))
        Console.WriteLine("{0} said: {1}", sender, message)
        Console.WriteLine(New String("—"c, 80))

        history.Enqueue(String.Format("At {0} {1} said: {2}", _
          DateTime.Now, sender, message))
        DoMessageEvent(sender, message)
    End Sub

    Private Sub DoMessageEvent(ByVal sender As String, _
        ByVal message As String)

        RaiseEvent MessageEvent(Me, _
            New ChatEventArgs(sender, message))
    End Sub

    <OneWay()> _
    Public Sub ShowHistory()
        Dim O As Object
        For Each O In history
            DoMessageEvent("server—history", O.ToString())
    End Sub
End Class

If you want clients to have a reference to an object that lives on the server, as opposed to a deserialized copy of the object, then you need to inherit from MarshalByRefObject.

The Queue is not relevant to this discussion other than it demonstrates an interesting way to keep a running log of all the messages received.

Next, declare a public event. When a client attaches an event handler to this event the server can talk back to the client by raising the event. Remember events are multicast—which means there might be many handlers for one event in .NET—so you can have an unlimited number of clients receiving this event.

By overriding the InitializeLifetimeService method inherited from MarshalByRefObject you can extended the life of the remotable object. By returning Nothing from InitializeLifetimeService the remotable object hangs around indefinitely.

The last step is to provide behaviors to consumers. In this example, two are offered: Send and ShowHistory. Both methods are implemented the same way, using the OneWayAttribute to describe how this method behaves. The OneWayAttribute means that no data is returned and only input parameters are passed to the method. In practice the OneWayAttribute means you are defining a subroutine with only ByVal parameters.

When ShowHistory is called the queue containing all of the previous messages is dumped. When Send is called, the MessageEvent is simply raised. All that is left to do is create a single client application that listens for MessageEvents. That piece will be tackled in the next part of this series of articles.


.NET Remoting requires a lot of knowledge about a lot of little pieces of the .NET framework. In this first part of a three, you learned how to configure a remote server, define remotable objects, and define custom event arguments and delegates.

In the next part, you will implement a client for your server and complete the chat application. The client is the other half of a distributed solution, so it has some special configuration needs too. In addition, because the server has to connect to the client to invoke the client's event handler, the client has to be remotable too and consequently has some special requirements. More on this later.

About the Author

Paul Kimmel is the VB Today columnist for www.codeguru.com and has written several books on object-oriented programming and .NET. Check out his book, Visual Basic .NET Power Coding, from Addison-Wesley and his upcoming book, UML DeMystified, from McGraw-Hill/Osborne (Spring 2005). Paul is also the founder and chief architect for Software Conceptions, Inc, founded 1990. He is available to help design and build software worldwide. You may contact him for consulting opportunities or technology questions at pkimmel@softconcepts.com.

If you are interested in joining or sponsoring a .NET Users Group, check out www.glugnet.org.

Copyright © 2004. All Rights Reserved.

Page 2 of 2

Comment and Contribute


(Maximum characters: 1200). You have characters left.



Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Thanks for your registration, follow us on our social networks to keep up-to-date