Adding New Web Services
Go Up to Writing Servers that Support Web Services
To add a new Web Service interface to your server application, choose File > New > Other , and on the WebServices page double-click on the icon labeled SOAP Server Interface.
The Add New Web Service wizard lets you specify the name of the invokable interface you want to expose to clients, and generates the code to declare and register the interface and its implementation class. By default, the wizard also generates comments that show sample methods and additional type definitions, to help you get started in editing the generated files.
Editing the generated code
The interface definitions appear in the interface section of the generated unit. This generated unit has the name you specified using the wizard. You will want to change the interface declaration, replacing the sample methods with the methods you are making available to clients.
The wizard generates an implementation class that descends from Soap.InvokeRegistry.TInvokableClass and that supports the invokable interface). If you are defining an invokable interface from scratch, you must edit the declaration of the implementation class to match any edits you made to the generated invokable interface.
When adding methods to the invokable interface and implementation class, remember that the methods must only use remotable types. For information on remotable types and invokable interfaces, see Using Nonscalar Types in Invokable Interfaces.
Using a different base class
The Add New WebService wizard generates implementation classes that descend from TInvokableClass. This is the easiest way to create a new class to implement a Web Service. You can, however, replace this generated class with an implementation class that has a different base class (for example, you may want to use an existing class as a base class.) There are a number of considerations to take into account when you replace the generated implementation class:
- Your new implementation class must support the invokable interface directly. The invocation registry, with which you register invokable interfaces and their implementation classes, keeps track of what class implements each registered interface and makes it available to the invoker component when the invoker needs to call the interface. It can only detect that a class implements an interface if the interface is directly included in the class declaration. It does not detect support an interface if it is inherited along with a base class.
- Your new implementation class must include support for the IInterface methods that are part of any interface. This point may seem obvious, but it is an easy one to overlook.
- You must change the generated code that registers the implementation class to include a factory method to create instances of your implementation class.
This last point takes a bit of explanation. When the implementation class descends from TInvokableClass and does not replace the inherited constructor with a new constructor that includes one or more parameters, the invocation registry knows how to create instances of the class when it needs them. When you write an implementation class that does not descend from TInvokableClass, or when you change the constructor, you must tell the invocation registry how to obtain instances of your implementation class.
You can tell the invocation registry how to obtain instances of your implementation class by supplying it with a factory procedure. Even if you have an implementation class that descends from TInvokableClass and that uses the inherited constructor, you may want to supply a factory procedure anyway. For example, you can use a single global instance of your implementation class rather than requiring the invocation registry to create a new instance every time your application receives a call to the invokable interface.
The factory procedure must be of type TCreateInstanceProc. It returns an instance of your implementation class. If the procedure creates a new instance, the implementation object should free itself when the reference count on its interface drops to zero, as the invocation registry does not explicitly free object instances. The following code illustrates another approach, where the factory procedure returns a single global instance of the implementation class:
Delphi:
procedure CreateEncodeDecode(out obj: TObject);
begin
if FEncodeDecode = nil then
begin
FEncodeDecode := TEncodeDecode.Create;
{save a reference to the interface so that the global instance doesn't free itself }
FEncodeDecodeInterface := FEncodeDecode as IEncodeDecode;
end;
obj := FEncodeDecode; { return global instance }
end;
C++:
void __fastcall CreateEncodeDecode(System::TObject* &obj)
{
if (!FEncodeDecode)
{
FEncodeDecode = new TEncodeDecodeImpl();
// save a reference to the interface so that the global instance doesn't free itself
TEncodeDecodeImpl->QueryInterface(FEncodeDecodeInterface);
}
obj = FEncodeDecode;
}
Note: In this example, FEncodeDecodeInterface is a variable of type IEncodeDecode.
You register the factory procedure with an implementation class by supplying it as a second parameter to the call that registers the class with the invocation registry. First, locate the call the wizard generated to register the implementation class. This appears in initialization section of the unit that defines the class. It looks something like the following:
Delphi:
InvRegistry.RegisterInvokableClass(TEncodeDecode);
C++:
InvRegistry()->RegisterInvokableClass(__classid(TEncodeDecodeImpl));
Add a second parameter to this call that specifies the factory procedure:
Delphi:
InvRegistry.RegisterInvokableClass(TEncodeDecode, CreateEncodeDecode);
C++:
InvRegistry()->RegisterInvokableClass(__classid(TEncodeDecodeImpl), &CreateEncodeDecode);