Delphi Clinic C++Builder Gate Training & Consultancy Delphi Notes Weblog Dr.Bob's Webshop
Dr.Bob's Delphi Notes Dr.Bob's Delphi Clinics Dr.Bob's Delphi Courseware Manuals
 Dr.Bob Examines... #102
See Also: Dr.Bob's Delphi Papers and Columns

Win32 SOAP and Exceptions
In this article, I'll demonstrate how Delphi for Win32 Web Services can create and handle SOAP Exceptions (as well as custom SOAP Exceptions) - with a little fix from CodeGear's Jean-Marie (Bruneau) Babet.

Win32 SOAP and Exceptions
The SOAP standard includes support for SOAP exceptions.For Delphi Win32 developers, this has worked in the past, but was broken in recent versions of Delphi.The next few pages will example the fixes added to Delphi 2007 for Win32 to make SOAP Exceptions work again. We can extend existing Win32 Web Service applications with SOAP exceptions and can use these SOAP exceptions to communicate serious errors from the server to the client.

The easiest way to illustrate the power of SOAP exceptions is to use an example Win32 web service.For example, the Echo web service that we can get by adding a new SOAP Server Interface with the default sample methods:

The IEcho SOAP server interface contains four methods, defined as follows in the EchoIntf.pas unit:

  { Invokable interfaces must derive from IInvokable }
  IEcho = interface(IInvokable)
  ['{971E680F-BE30-4757-89C2-F0A9C7B707DF}']
    { Methods of Invokable interface must not use the default }
    { calling convention; stdcall is recommended }

    function echoEnum(const Value: TEnumTest): TEnumTest; stdcall;
    function echoDoubleArray(const Value: TDoubleArray): TDoubleArray; stdcall;
    function echoMyEmployee(const Value: TMyEmployee): TMyEmployee; stdcall;
    function echoDouble(const Value: Double): Double; stdcall;
  end;
The echoEnum and echoDouble are not an easy target to define a useful SOAP exception for, but the echoDoubleArray and echoMyEmployee are good candidates.For example with the echoDoubleArray, we can check to see if the array is not empty.And for the echoMyEmployee, we can verify that the employee has a non-negative salary field (nobody should have to pay to get to work – unless the job involves being captain of the starship Enterprise).

Echo Interface unit
In order to add a SOAP exception, we first need to make a change to the ECHO interface unit, and add the SOAP exception type, derived from ERemotableException:

type
  EmptyArrayException = class(ERemotableException)
  end;
Note that we can add fields and properties to the EmptyArrayException.Just remember that the properties must be published in order for them to appear in the WSDL document (and be sent over the wire and received by the client applications). Inside the initialization section, we must then make sure that the EmptyArrayException is registered for the IEcho SOAP interface, as follows:
initialization
  { Invokable interfaces must be registered }
  InvRegistry.RegisterInterface(TypeInfo(IEcho));
  InvRegistry.RegisterException(TypeInfo(IEcho), EmptyArrayException);
end.

Echo implementation unit
Inside the implementation unit, we can now use the EmptyArrayException in the echoDoubleArray function, as follows:

function TEcho.echoDoubleArray(const Value: TDoubleArray): TDoubleArray; stdcall;
begin
  { TODO : Implement method echoDoubleArray }
  if (not Assigned(Value)) or (Length(Value) = 0) then
    raise EmptyArrayException.Create('echoDoubleArray empty parameter')
  else
    Result := Value;
end;

Echo import unit
Importing the WebService leads to an import unit with the SOAP exception defined as well:

  // ************************************************************************ //
  // XML       : EmptyArrayException, global, <complexType>
  // Namespace : urn:EchoIntf
  // Info      : Fault
  // ************************************************************************ //
  EmptyArrayException = class(ERemotableException)
  private
  published
  end;
And we can use this to place the call to echoDoubleArray inside a try-except block, for example as follows:
procedure TForm5.Button1Click(Sender: TObject);
begin
  try
    GetIEcho.echoDoubleArray(nil)
  except
    on E: Exception do
      ShowMessage(E.ClassName + ': ' + E.Message)
  end
end;
Unfortunately, when running the client application, we get an ERemotableException and not an EmptyArrayException as we’d hoped.

This happens with all versions of Delphi for Win32, except for Delphi 6 (for which it just works right).

SOAP Exception Fix
For Delphi 2007 for Win32, Jean-Marie Babet has made a number of fixes in the OPToSOAPDOMConv.pas unit. The latest version was uploaded on June 13th 2008 to the borland.public.attachments newsgroup (and is also added to CodeCentral). This fix is already part of Delphi 2009 for Win32, by the way.

Apart from the fixes in the OPToSOAPDOMConv.pas, we must also make a small change to the unit InvokeRegistry.pas (otherwise you might get an access violation). First of all, the {$M-} compiler directive must be moved from line 197 to line 199, so the definition on line 198 of

  ERemotableExceptionClass = class of ERemotableException;
Is also included in the {$M+} section.

Also, the procedure GetExternalPropName needs one more line to be added to 2796 (marked in red in the code snippet below):

function TRemotableTypeRegistry.GetExternalPropName(Info: PTypeInfo;
  InternalName: string): InvString;
var
  Index: Integer;
  Found: Boolean;
  Len, I: Integer;
begin
  Result := InternalName;
  Lock;
  try
    Index := FindEntry(Info, Found);
    if Found then
    begin
      Len := Length(URIMap[Index].PropNameMap);
      for I := 0 to Len-1 do
      begin
        if URIMap[Index].PropNameMap[I].Name = InternalName then
        begin
          Result := URIMap[Index].PropNameMap[I].ExtName;
          Exit
        end;
      end;
    end;
    if (Info.Kind = tkClass)
      and Assigned(GetTypeData(info).ParentInfo) then
    begin
      Info := GetTypeData(Info).ParentInfo^;
      if Assigned(Info) and (Info <> TRemotable.ClassInfo) and
                            (Info <> TObject.ClassInfo) then
        Result := GetExternalPropName(Info, InternalName);
    end;
  finally
    UnLock;
  end;
end;
The result of these changes – we need to recompile the server as well as the client – is that the right kind of exception is received by the client:

This also means that we can add and use published properties in our custom exceptions.

For more recent information on this topic, check out my Delphi 2010 XML, SOAP & Web Services courseware manual.


This webpage © 2008-2010 by Bob Swart (aka Dr.Bob - www.drbob42.com).All Rights Reserved.