Calling Invokable Interfaces

From RAD Studio
Jump to: navigation, search

Go Up to Writing Clients for Web Services


To call an invokable interface, your client application must include any definitions of the invokable interfaces and any remotable classes that implement complex types.

If the server is written in Delphi or C++Builder, you can use the same units that the server application uses to define and register these interfaces and classes instead of the files generated by importing a WSDL file. Be sure that the unit uses the same namespace URI and SOAPAction header when it registers invokable interfaces. These values can be explicitly specified in the code that registers the interfaces, or it can be automatically generated. If it is automatically generated, the unit that defines the interfaces must have the same name in both client and server, and both client and server must define the global AppNameSpacePrefix variable to have the same value.

Once you have the definition of the invokable interface, there are two ways you can obtain an instance to call:

  • If you imported a WSDL document, the importer automatically generates a global function that returns the interface, which you can then call.
  • You can use a remote interfaced object.

Obtaining an invokable interface from the generated function

The WSDL importer automatically generates a function from which you can obtain the invokable interfaces you imported. For example, if you imported a WSDL document that defined an invokable interface named IServerInterface, the generated unit would include the following global function:

function GetIServerInterface(UseWSDL: Boolean; Addr: string; HTTPRIO: THTTPRIO): IServerInterface;
 
_di_IServerInterface GetIServerInterface(bool UseWSDL, AnsiString Addr, THTTPRIO* HTTPRIO);

The generated function takes three parameters: UseWSDL, Addr, and HTTPRio. UseWSDL indicates whether to look up the location of the server from a WSDL document (true), or whether the client application supplies the URL for the server (false).

When UseWSDL is false, Addr is the URL for the Web Service. When UseWSDL is true, Addr is the URL of a WSDL document that describes the Web Service you are calling. If you supply an empty string, this defaults to the document you imported. This second approach is best if you expect that the URL for the Web Service may change, or that details such as the namespace or SOAP Action header may change. Using this second approach, this information is looked up dynamically at the time your application makes the method call.

Note: For performance reasons, it is best to use a URL instead of a WSDL parameter. For WSDL, the SOAP runtime must perform an HTTP GET for the WSDL parameter before invoking a Web Service operation. The HTTPRio parameter specifies the Remotable Interface Object to be used to obtain the interface. If you specify a value of NULL for the HTTPRio parameter, the method creates a new THTTPRio instance. You can specify the THTTPRio instance if you want to use the Object Inspector to customize the object and handle events.

Note: The generated function uses an internal remote interfaced object to implement the invokable interface. If you are using this function and find you need to access that underlying remote interfaced object, you can obtain an IRIOAccess interface from the invokable interface, and use that to access the remote interfaced object:

 var
   Interf: IServerInterface;
   RIOAccess: IRIOAccess;
   X: THTTPRIO;
 begin
   Intrf := GetIServerInterface(True,
           'http://MyServices.org/scripts/AppServer.dll/wsdl');
   RIOAccess := Intrf as IRIOAccess;
   X := RIOAccess.RIO as THTTPRIO;
 
 _di_EchoUnboundedSoap service = GetEchoUnboundedSoap();
   _di_IRIOAccess rioAccess = service;
   TSOAPConvertOptions options = rioAccess->GetRIO()->Converter->GetOptions();

Using a remote interfaced object

If you do not use the global function to obtain the invokable interface you want to call, you can create an instance of Soap.SOAPHTTPClient.THTTPRIO for the desired interface:

 X := THTTPRio.Create(nil);
 
 X = new THTTPRio(NULL);

Note: It is important that you do not explicitly destroy the THTTPRio instance. If it is created without an Owner (as in the previous line of code), it automatically frees itself when its interface is released. If it is created with an Owner, the Owner is responsible for freeing the THTTPRio instance.

Once you have an instance of THTTPRio, provide it with the information it needs to identify the server interface and locate the server. There are two ways to supply this information:

If you do not expect the URL for the Web Service or the namespaces and soap Action headers it requires to change, you can simply specify the URL for the Web Service you want to access. THTTPRio uses this URL to look up the definition of the interface, plus any namespace and header information, based on the information in the invocation registry. Specify the URL by setting the URL property to the location of the server:

 X.URL := 'http://www.myco.com/MyService.dll/SOAP/IServerInterface';

If you want to look up the URL, namespace, or Soap Action header from the WSDL document dynamically at run time, you can use the WSDLLocation, Service, and Port properties, and it will extract the necessary information from the WSDL document:

 X.WSDLLocation := 'Cryptography.wsdl';
 X.Service := 'Cryptography';
 X.Port := 'SoapEncodeDecode';

After specifying how to locate the server and identify the interface, you can obtain an interface pointer for the invokable interface from the THTTPRio object. You obtain this interface pointer using the as operator. Simply cast the THTTPRio instance to the invokable interface:

 InterfaceVariable := X as IEncodeDecode;
 Code := InterfaceVariable.EncodeValue(5);
 
 _di_IEncodeDecode InterfaceVariable;
 if (X->QueryInterface(InterfaceVariable) == S_OK)
 {
   Code = InterfaceVariable->EncodeValue(5);
 }

When you obtain the interface pointer, THTTPRio creates a vtable for the associated interface dynamically in memory, enabling you to make interface calls.

THTTPRio relies on the invocation registry to obtain information about the invokable interface. If the client application does not have an invocation registry, or if the invokable interface is not registered, THTTPRio can't build its in-memory vtable.

Warning: If you assign the interface you obtain from THTTPRio to a global variable, you must change that assignment to nil before shutting down your application. For example, if InterfaceVariable in the previous code sample is a global variable, rather than stack variable, you must release the interface before the THTTPRio object is freed. Typically, this code goes in the OnDestroy event handler of the form or data module:

 procedure TForm1.FormDestroy(Sender: TObject);
 begin
   InterfaceVariable := nil;
 end;
 void __fastcall TForm1::FormDestroy(TObject *Sender)
 {
   InterfaceVariable = NULL;
 }

The reason you must reassign a global interface variable to nil is because THTTPRio builds its vtable dynamically in memory. That vtable must still be present when the interface is released. If you do not release the interface along with the form or data module, it is released when the global variable is freed on shutdown. The memory for global variables may be freed after the form or data module that contains the THTTPRio object, in which case the vtable will not be available when the interface is released.

See Also