Desaware Home
Products    Purchase    Publishing    Articles   Support    Company    Contact    



Contact Desaware and order today

Sign up for Desaware's Newsletter for the latest news and tech tips.

Raising Events from Remote DCOM Objects using the NT Service Toolkit

The NT Service toolkit documentation discusses two ways to expose objects from services that can be accessed from other applications on the local system and remote systems.

You can expose client objects using the ControlObject.RegisterClientObjectName method and providing the class name of the object in your service that you wish to expose. This object is created on the toolkit's internal thread pool. We'll refer to these as service client objects.

You can also expose an Application object by registering an existing object using the ControlObject.RegisterApplicationObject method. This object is created on the main service thread and is shared by all clients that obtain access to it. We'll refer to this as the application object.

You can also expose objects by adding a method to the application or client object that creates and returns a reference to another object. If you have a class named OtherClass in your service DLL, the following code can expose it to the clients:

Public Function GetOtherClass() As Object
	Set m_Other = New OtherClass
	Set GetOtherClass = m_Other
End Function

Classes exposed using this approach are created on the thread of the object that creates it. We'll refer to these objects as other class objects.

The preferred way for any of these objects to signal back to the client is using OLE callbacks (as described in the documentation). You do this by adding a method to the service object which allows it to store a reference to some object on the client. The service object can then call a specified method on the client object when needed. As with the case with OLE callbacks in general, you do have to tear down carefully, since an OLE callback does have the potential for creating circular references. You should always enable error checking when calling the client's method in case the client application has terminated (this will result in a trappable client-disconnected error).

The use of events in objects exposed by the NT Service Toolkit is problematic for a number of reasons. The main reason has to do with binding, a secondary one relates to security.

The Early Binding Problem

You are probably familiar with the fact that when you attempt to create an object in a Visual Basic DLL, you always get a new instance of an object. VB does not provide a way to expose a "running object". This was clearly a problem with regards to the NT Service toolkit, where even in the case of service client objects, the goal was to expose objects from an already running service.

To address this problem, we used the framework (which is written in C++ and can thus easily expose existing objects) to expose an object called "RunningService" any time you register a service client or application object. So when you request a service client object, you are really getting a RunningService object created by the framework. So how are your service client object methods callable? Well, we pull a fast one - every time you call a method on the RunningService object, we check to see if it is one of the built in methods (like GetAppObject) - if not, we forward the call to the service client object that was created by the framework on your behalf. One of the consequences of this approach is that you can only call methods on your service client object using the IDispatch interface - i.e., late bound calls using the Object type.

The problem that arises here is that the COM event mechanism must use early binding - you not only have to have a reference to the actual object, you must also have access to the type information for that object (so that the type library can be queried for the event interface GUID's and templates).

When you use the RunningService.GetAppObject method to obtain a reference to an application object, or another method to obtain other class objects, you do obtain a reference to the actual object. However, you still don't have the type information for that object. That's because the dwNTServ.tlb file that is distributed with your service does not contain any type information for your internal objects - only for the RunningService object defined by the toolkit.

The NT Service toolkit was designed under the assumption that you would obtain objects as described here, and access them in a late bound manner, using OLE callbacks if you need to pass notifications back to the client.

It is possible to use early binding with the application object and other class objects, and thus to use true COM events - though that has other problems as you will soon see. You cannot use early binding with service client objects because you aren't actually accessing the object - only the Running Service object.

Please note that the following approach, while technically valid, is not one supported by Desaware.

Events, the application and other class objects.

Because you actually have a reference to the actual application and other class objects, you can early bind to them. To do so, you need type information for the object. To obtain this you will need to create and distribute the actual type library for your service DLL. To create this type library you must check the "Remote Server Files" check box in the Component tab section of the Project settings for your service DLL file. Be sure that your service DLL does not have the same name as your service EXE though!

Checking the "Remote Server Files" check box will create a type library for the service DLL. It will also create a VBR file. 

You'll then need to do the following:

  • In your client application, you'll need to add a reference to the type library.
  • You'll need to distribute the type library.
  • You'll need to register the type library on the client system.

Registering the type library is a bit tricky. An easy way is to simply add a reference to it from VB, but this won't work on clients. The VBR file that was created by VB (not the one created by the NT service toolkit configuration wizard) does a lot more than register the type library - and if you use CliReg32 to register that VBR file, you may find yourself accessing the service DLL directly rather than through the service. If you edit the VBR file an only keep the following entries, you should be able to register the type library for early binding:

  • The VB5SERVERINFO and version header
  • All keys beginning with HKEY_CLASSES_ROOT\Typelib\
  • All keys beginning with HKEY_CLASSES_ROOT\INTERFACE that correspond to the objects that you wish to early bind.

You should also be able to add these Typelib and INTERFACE entries to the VBR file created by the toolkit, and thus have the registration of the other type library occur at the same time as that of dwNTServ.tlb.

But before you get too excited about this approach, it does have one major limitation. The event mechanism does require the DCOM system to be able to access the client object through the event mechanism - and thus requires adequate security to perform this operation. You must use dcomcnfg.exe to assign Access permission to the NETWORK group as a default permission in order for this approach to work. This will allow the server application and other class objects to raise events to the client as long as the client is a compiled application or component. You will still get permission denied errors when the client is running in the VB environment (The VB environment does not seem to allow calls in from DCOM - as mentioned in the toolkit documentation).

Additional Issues

One of the issues to be concerned with is what happens if the client (to which the server objects are raising events) suddenly terminates - say through a crash? With OLE callbacks, you can easily trap for this situation using standard VB error trapping. You will see a "client disconnected" error when this occurs. When using the event mechanism, you must be sure to use error trapping any time you raise events.

One of the advantages of late binding is that you never have to worry about interface compatibility of your service DLL. In fact, you'll note that we don't stress any of the traditional versioning and binary compatibility issues with regards to service DLL's for exactly that reason. However, as soon as you allow early binding (required for events), it is ESSENTIAL that you follow all of the versioning rules of VB6 and maintain binary compatibility for your service DLL. If you find that you did not maintain binary compatibility on an update, be sure to use Clireg32 /u to uninstall the type libraries. 

We hope this helps you address the problem of calling back into the client. Most of the challenges that arise in this area, like most, relate to issues having to do with the interactions of services with DCOM and the Windows security system - and are not actually related to the implementation of the NT Service toolkit itself.

For notification when new articles are available, sign up for Desaware's Newsletter.

Related Products:
Products    Purchase    Articles    Support    Company    Contact
Copyright© 2012 Desaware, Inc. All Rights Reserved.    Privacy Policy