November 1, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

.NET Remoting with Events in Visual C++

  • April 13, 2004
  • By Kate Gregory
  • Send Email »
  • More Articles »

In my previous column, I introduced the basics of .NET remoting. I showed how to create a remoted class, host it in a server application, and call methods of the class from a client application.

The communication in that simple example was all client-driven. When the client wanted information from the server, it called methods of the remoted object such as Greet() or Records(). These methods don't take parameters, but there's no restriction on remoted objects. You could easily design a system where the client passes parameters to the server as arguments on a method call. For example, the client applications could be used to process orders, and the method calls could pass along information such as item codes, quantities, and ship dates. If the only new information that enters your system is from your client, the remoting model as presented will work perfectly for you.

But, in many systems there is input from several directions at once. For example, the central office might change the prices of the items for which orders are being taken. How can you be sure that all the clients have the most up-to-date prices at all times?

What you want is event handling: The server can raise an event (PriceChanged perhaps) and the client (or multiple clients) can all handle that event. Events are used throughout .NET when one piece of code needs to notify other pieces of code that something has happened. You write an event handler to deal with button clicks and other user interactions with a WinForm application, among other things.

In this sample, I'll add a button to the server application (a WinForm app with a big Listen button) and have the handler for the click event raise a custom event. I'll have the remoting client actually handle that event by displaying a message box. Your job is to run with that and create something useful in your own application.

Defining the Event and Delegate

The delegate represents the event handler. It must be known to both the client and the server. I added this line to the file that declares Greeting::Greeter, the remoted object:

public __delegate void RemoteAlert(String* str);

This means that any function that takes a string and returns void can be used as a RemoteAlert delegate. Delegates are type-safe function pointers: A function that takes an integer, or that takes a string and returns another string, cannot be used as a RemoteAlert delegate. The full signature and return type of the function are part of the definition of the delegate.

The event handler is a function that matches the signature of the delegate: In this example, it must be a void function that takes a String*. The client application implements a class to hold this function, and passes a reference to an instance of the class up to the server, to be added to the list of event handlers maintained there. The class must be known to the server code, either because the server code includes a header file that defines it or because the server code has a reference to an assembly that defines it.

This is a real problem for many developers: The handler class must be known to the server. They discover that events appear to work only when the server application and client application are in the same folder—in other words, when they are not really remoting. Many have discovered that copying the client application to the remote server machine also enables events to work properly over remoting. Copying a client application to the server machine feels awkward. It also sets you up for frustrating debugging or maintenance work because you might have to copy files again and again, and forgetting to copy might make the application fail even though there's nothing wrong with the code.

I have a solution to this issue that's a little more trouble at the beginning and then a lot less trouble afterwards. I define a base class (RemoteHandlerBase) with a pure virtual function (I called mine HandleAlert()) that matches the signature of the delegate. This class is in the same namespace and assembly as the remoted object, Greeter. Then, in the client, I define a derived class, RemoteHandler, that overrides and implements HandleAlert(). The instance that's passed up to the server is actually an instance of RemoteHandler, but upcasts are always allowed, so the server can think of it as an instance of RemoteHandlerBase. When the server calls HandleAlert(), thanks to good old polymorphism, the implementation in the derived class is actually called. It works like a charm and you never have to copy your client application to the server.

Changing the Server

Here is the base class, which is included in the project that defines the remoted object:

namespace Greeting
{
public __gc class RemoteHandlerBase: public MarshalByRefObject
   {
   public:
      void Alert(String* s)
      {
         //polymorphism rules :-)
         HandleAlert(s);
      }
   protected:
      virtual void HandleAlert(String* s)=0;
   };
}

Notice that the class must inherit from MarshalByRefObject: A reference will be passed to the server from the client, but the methods will execute on the client. Alert() is a non-virtual wrapper around the virtual function; it will be called from the UI code.

The remoted object needs to change as well: It needs to keep a list of event handlers and to provide a static Alert() function:

public __gc class Greeter: public MarshalByRefObject
   {
   public:
      __event RemoteAlert* RemoteAlertEvent;
      String* Greet(String* name);
      String* Greet();
      Data::DataSet* Records();
      static void Alert(String* s);
      Greeter();
   private:
      static Collections::ArrayList* greeters=0;
   };

RemoteAlertEvent is actually a list of events. It will take new items with += and maintains the list. I added the static Alert() because the server code actually has no access to Greeter instances: There is one Greeter object, created as a result of client calls, and the server code isn't using it. (The Greeter class is remoted as a server-activated singleton, so there's only one instance, but by changing only the configuration file I might make it client activated, and then there could be many instances at once, each associated with a different client application.) Adding a static method saved me from any instance management issues on the server side. It uses the greeters list to access all the instances that have been created. The constructor adds instances to the list. Here are the implementations of the constructor and Alert():

Greeter::Greeter()
{
   if (!greeters)
      greeters = new Collections::ArrayList();
   greeters->Add(this);
}
void Greeter::Alert(String* s)
{
   for (int i=0; i < greeters->Count; i++)
      (static_cast<Greeter*>(greeters->get_Item(i)))
         ->RemoteAlertEvent(s);
}




Page 1 of 2



Comment and Contribute

 


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

 

 


Sitemap | Contact Us

Rocket Fuel