December 19, 2014
Hot Topics:

.NET Remoting with Events in Visual C++

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

I need the static_cast<> because ArrayList is a collection of Object references, so I need to cast them back to Greeter references as they come out of the collection. If you find this code ugly, you could use an STL collection; just remember the gcroot template that enables you to put managed types into an STL collection. (This is going to be so much neater in Whidbey.)

Notice how Alert() gets hold of each item in the collection, and after casting it, calls the RemoteAlertEvent() event as though it was a function. This will raise the event and invoke all the handlers that have been added to the event. There's no code here on the server to add handlers to the event, though. You'll see that on the client side.

So that the event can easily be raised, I added a button to the server form and had the event handler for the button click call the static Alert() method:

private: System::Void Alert_Click(System::Object *  sender,
                                  System::EventArgs *  e)
{
   Greeting::Greeter::Alert("The server Alert button was clicked");
}

The last step on the server side is to change the configuration file. This element needs to be expanded:

<channel ref="tcp" port="9555" />

It ends up looking like this:

<channel ref="tcp" port="9555">
   <serverProviders>
      <formatter ref="binary" typeFilterLevel="Full" />
   </serverProviders>
</channel>

This change is required in version 1.1 of the .NET Framework and up; older samples or articles that cover remoting will not mention it. Security settings in version 1.1 do not support callbacks by default because they might represent a vulnerability. Client code is letting server code decide to trigger the execution of client code. You have to deliberately turn the feature on.

Changing the Client

The first step on the client side is to define and implement the handler class, RemoteHandler:

namespace GreetingClient
{
public __gc class RemoteHandler: public Greeting::RemoteHandlerBase
   {
   protected:
      void HandleAlert(String* msg);
   };
}

Because RemoteHandler inherits from RemoteHandlerBase, which in turn inherits from MarshalByRefObject, instances of this class can be passed over remoting by reference. These references are used to invoke the HandleAlert() method when the event is raised.

The implementation of HandleAlert() is nice and simple:

void RemoteHandler::HandleAlert(String* msg)
{
   Windows::Forms::MessageBox::Show(msg,"Alert from server");
}

Just as the server configuration file needed to be changed to permit callbacks, so does the client. After the existing <client>...</client> element, I added a channel element:

<channel ref="tcp" port="0">
   <serverProviders>
      <formatter ref="binary" typeFilterLevel="Full" />
   </serverProviders>
</channel>

As in the server configuration file, this element takes care of the security restrictions, making it clear I am deliberately using callbacks over remoting and that I trust the server application to trigger execution of parts of the client application. The <channel> element specifies a port of 0, so any available port can be used.

The client constructor gains another line of remoting "plumbing." (It's still a lot less code than you would write with DCOM.) After the call to Configure(), I added this line to set up the callback channel:

ChannelServices::RegisterChannel(new Tcp::TcpServerChannel
                                 ("callback", 9556));

(This needs a using namespace Runtime::Remoting::Channels to find the classes.) Choose any port you like that's different from the one you're accessing the remoted object over, and that's unlikely to be in use by anyone else.

You may be wondering how event handlers get added to the list that the server is keeping. I just use the remoted instance. In the Form1 constructor, there is already a line to create the remote object:

greet = new Greeting::Greeter();

Right after that line, I added:

RemoteHandler* rh = new RemoteHandler();
Greeting::RemoteHandlerBase* rhb = static_cast
          <Greeting::RemoteHandlerBase*>(rh);
greet->RemoteAlertEvent += new RemoteAlert(rhb,
                                           &RemoteHandler::Alert);

This code creates an instance of the RemoteHandler class, defined in the client. It then casts that instance to a RemoteHandlerBase* because the server is only aware of the RemoteHandlerBase class. (RemoteHandlerBase is in the Greeting assembly, and the client has a reference to that assembly, so client code knows about both RemoteHandler and RemoteHandlerBase.) The final line of this code snippet creates a delegate using the special C++ syntax. The first parameter to the delegate constructor is a pointer to the object, and the second parameter uses the pointer-to-member syntax to create a function pointer. Once constructed, the delegate is added directly to the event handler list in the Greeting object by accessing the public variable and using the += operator.

That's it! The client has code to make an instance of the handler object and add it to the list on the server. It also has an implementation of the handler method. The server has the delegate definition, and code to maintain a list of event handlers then raise the event to them. The configuration files have been tweaked to allow events to pass over remoting.

Trying It Out

If you built the code for my previous column, and made the changes I've shown here, you can test it quite simply. Rebuild the entire solution and copy greetingserver.exe, greetingserver.exe.config, and greeting.dll to your second machine. Start the server and click Listen. Go to your first machine and start the client. If you want, make sure that Greet() and GetRecords() still work. Then, on the server, click the new button that raises the event. Nothing should appear to happen on the server. Go back to the client and you should see a message box. If you do, that means you raised an event on the server that was handled on the client. The possibilities for that are tremendous. This is a huge advantage of remoting over Web services and one I encourage you to explore a bit more.

About the Author

Kate Gregory is a founding partner of Gregory Consulting Limited (www.gregcons.com). In January 2002, she was appointed MSDN Regional Director for Toronto, Canada. Her experience with C++ stretches back to before Visual C++ existed. She is a well-known speaker and lecturer at colleges and Microsoft events on subjects such as .NET, Visual Studio, XML, UML, C++, Java, and the Internet. Kate and her colleagues at Gregory Consulting specialize in combining software develoment with Web site development to create active sites. They build quality custom and off-the-shelf software components for Web pages and other applications. Kate is the author of numerous books for Que, including Special Edition Using Visual C++ .NET.






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.

Sitemap | Contact Us

Rocket Fuel