Imagine this: Your CEO, or reasonable facsimile thereof, just returned from lunch with his counterpart at .Net Early Adopter Inc. His mind is full of the wonders of Web services, and he thinks, maybe, he might have promised this other company the use of some functionality in one of your apps as a Web service in exchange for a large sum of money. The hitch is that your team is still firmly entrenched in the VB6 world, with no immediate plans to migrate.

This might sound completely outrageous, but I’ve known enough companies that operate in just such a “sell it first, make it happen later” fashion to be fairly solid in my conviction that something similar to this very scenario will happen to someone, somewhere, on this very day. What to do? Remain calm and check out Microsoft’s SOAP toolkit, which, with a few exceptions, makes it exceedingly easy to run existing COM functionality as a bona fide Web service.

SOAP 101
If you keep thinking “shampoo” when you hear about SOAP, you should know that SOAP stands for Simple Object Activation Protocol, and it specifies message formats that are useful when a client application wishes to activate and work with an object on a remote server. SOAP only specifies the format of the messages sent to and from a server; it doesn’t specify how these messages should get to wherever they’re going. Most of the time, HTTP is the transport protocol for a SOAP message, which means that a Web server is usually involved in hosting the remote objects or forwarding requests to the actual host.

Someone got SOAP in the keyboard
The SOAP Toolkit provides two application programming interfaces (APIs) for clients to work with Web services: a high-level API and low-level API. The high-level API provides a proxy object to give the client application the illusion that it’s actually invoking methods on a locally stored object. The low-level API requires a programmer to manually construct and decipher the SOAP messages sent to and received from the server. Obviously, the high-level API is more convenient, but it doesn’t offer as much direct control over the process as you have with the low-level API. However, it’s useful when reworking existing client code to access a Web service instead of a COM component.

Are those bubbles on the server?
From the server component’s point of view, the toolkit is virtually transparent. It provides a complete wrapper for COM services, and in some instances you’ll be able to simply answer a few questions from a wizard to generate the Web Services Description Language (WSDL) and Web Services Meta Language (WSML) files you’ll need to expose a component, assuming you’re running IIS.

DL vs. ML

What’s the difference between WSDL and WSML? It’s pretty simple, actually: WSDL is an XML dialect that defines the operations (object methods) and messages (parameters) available on a Web service. WSML is a specification created by Microsoft that maps the operations and messages in a WSDL document to their COM counterparts.

For a guided tour of an WSDL document, see “Defining Web services in WSDL: A primer.”

A quick example
The Toolkit includes several example applications, but the DocSample1 example has the most relevance to the scenario I laid out above: it takes a simple VB6 COM DLL and walks you through exposing it as a Web service. Unfortunately, the client-side code is written in VBScript, of course using late binding, which probably isn’t the way you’d create a VB6 client. So let’s run through a quick example here.

Suppose you have a component that contains the Invoice class in Listing A. For simplicity’s sake, Invoice  has only one method, GetInvoiceAmount, which is responsible for looking up an invoice number (just pretend it’s pulling this data from a database) and returning the amount. To expose GetInvoiceAmount as a Web service, you’d follow the following steps:

  1. ·        Create an IIS virtual directory to house the DLL.
  2. ·        Copy the DLL and its type library into the virtual directory you created in Step 1.
  3. ·        Run the WSDL Generator wizard on the DLL, specifying “Invoice” as the service name, selecting the Invoice class and providing a sensible URI (e.g., http://localhost/Invoice if you plan to run this example from the server) for the listener.

You’ll wind up with new Invoice.WSDL and Invoice.WSML files in the same directory that houses the DLL.

For a test client, you’d use the code in Listing B  in a VB6 project that references the Microsoft SOAP Type Library (MSSOAP1.dll).

Bursting the bubble
That was pretty simple, wasn’t it? Sure it was, but ask yourself this question: What happens if I want to return an object, such as the Invoice object itself? That’s the fly in the ointment (or in the suds if you’d rather). Although it’s quite capable of converting most nonobject types into WSDL equivalents, the WSDL Generator can’t automatically convert object types, such as a Collection, into their equivalent complexType WSDL elements.

No big deal, right? You’ll just need to hack together some WSDL; how hard can that be? Well, in order to send an object type as a SOAP message, you’ll also need to serialize it at the sending end and deserialize it at the receiving end.

If you happen to find yourself in a situation like this—and realistically, who wouldn’t—the SOAP Toolkit provides a set of type mappers that can take care of the serialization issues for you:

  • ·        The UDT Mapper is used with user-defined types (structures created using the VB6 Type…End Type keywords).
  • ·        The Generic Type Mapper is capable of serializing and deserializing COM objects, and it works adequately in many instances. The big advantage of the generic mapper is that it doesn’t require much code to use.
  • ·        You also have the option of creating a custom-type mapper for situations where the generic mapper won’t be adequate. For example, when the complexType definitions in WSDL don’t map directly to members of an object type.
  • ·        Finally, you also have the option of receiving the object’s data as a set of Document Object Model (DOM) nodes in an IXMLDOMNodeList object. Of course, that means you’ll need to write a lot of tree-traversal code to get at the data you’ve been sent.