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... #40
See Also: other Dr.Bob Examines columns or Delphi articles

An earlier version of this article originally appeared in Hardcore Delphi (February 2003). Copyright Lawrence Ragan Communications, Inc. All Rights Reserved.

Delphi for .NET and ASP.NET Web Services
Two months ago, we explored the Delphi for .NET preview command-line compiler as scripting language environment for ASP.NET applications. This time, I'll show that apart from producing server side scripted web pages, Delphi for .NET and ASP.NET can also play an important role when it comes to .NET web services.

Note: for this article, you again need the .NET Framework and SDK installed, as well as the Delphi for .NET preview command-line compiler that ships as separate CD in the box with Delphi 7 Studio. Note that both the original version and the updates (#1 made available on November 22nd 2002 and #3 on February 20th 2003) work just fine with the code in this article.

ASP.NET Web Service
A web service is an engine that can perform some kind of non-visual task, usually a conversion or other service, that communicates with the outside world. SOAP has been a much hyped communication protocol (using XML over HTTP) that is one of the cornerstones of .NET. In fact, SOAP has been built in .NET from the start, so you shouldn't be surprised to find SOAP and Web Services to be almost native in C# and also Delphi for .NET.
In order to build a Web Service on .NET, we must use the System.Web.Services assembly (which contains everything we need), and derive our new web service class - like Euro42 - from WebService. Each method that needs to be exported to the outside world must be preceded with the WebMethod attribute. That's all. So the following short source unit (placed in file eBob42.asmx) implements a Euro conversion web service called Euro42.

  <%@ WebService Language="Delphi" Class="eBob42.Euro42" %>
  unit eBob42;
  interface
  uses
    System.Web.Services;

  type
    Euro42 = class(WebService)
    public
      [WebMethod]
      function About: String;
      [WebMethod]
      function GuldenToEuro(Gulden: double): double;
      [WebMethod]
      function EuroToGulden(Euro: double): double;
    end;

  implementation

  function Euro42.About: String;
  begin
    Result := 'Dr.Bob''s Euro42 Web Service written in Delphi for .NET (preview)'
  end;

  const
    GuldenPerEuro = 2.20371;

  function Euro42.GuldenToEuro(Gulden: double): double;
  begin
    Result := Gulden / GuldenPerEuro
  end;

  function Euro42.EuroToGulden(Euro: double): double;
  begin
    Result := Euro * GuldenPerEuro
  end;

  end.
In order to deploy the web service we need to place the source file in the same Scripts directory that could contain our Delphi for .NET ASP.NET pages (i.e. with a web.config file that refers to the DelphiProvider).

Watching Delphi for .NET Web Services
In order to test the web service, we have to view it in Internet Explorer as follows (note the warning about the default namespace that we're using):

In order to add a WebService attribute to our Delphi for .NET code, we need to literally add an attribute called WebServiceAttribute, with properties Namespace and optional Description. Note that Namespace is case sensitive, if you accidentally type "NameSpace", you'll get a compiler error (NameSpace property specified was not found). Been there, done that, took a while to find and fix it.
This means that the Euro42 class gets one long attribute line before its declaration itself starts:

  type
  [WebServiceAttribute(Namespace='http://www.eBob42.com',
   Description='Dr.Bob''s Euro42 Web Service written in Delphi for .NET (preview)')]
    Euro42 = class(WebService)
    public
      [WebMethod]
       ...
    end;
And if you now redeploy the eBob42.asmx file, you won 't get the default namespace warning again. In fact, we even get to see the description now.

WSDL for Delphi for .NET Web Services
In the previous screenshot, you may have seen the link to the Service Description of the web service. The URL for this is http://localhost/scripts/eBob42.asmx?WSDL and if you click on it you get the WSDL (Web Service Description Language) description of the functionality of the web service. WSDL is a cross-platform and cross-language standard that can be used by any web service compliant environment. It's actually an XML document, describing in formal language what the web service can do and how to connect to it. I won't list it here, because even for a few methods it's already a number of pages long. The thing to remember is that a .NET web service (derived from the WebService class from the System.Web.Services assembly) will automatically produce its WSDL.
We'll get back in a moment and see how we can use (and import) this WSDL in order to actually consume the web service. Especially when done in another environment than Delphi for .NET itself (such as Delphi 6+, Kylix 2+ or even C#), we can demonstrate the cross-platform and cross-language nature of web services.

Testing Delphi for .NET Web Services
Apart from watching the WSDL, it may be even more fun to actually test the Delphi for .NET web service. This is also supported "for free". In the previous screenshot, the three methods are listed: GuldenToEuro, EuroToGulden and About. If you click on one of these methods - say About - you enter a page that shows you a button to test the About method. It also shows the SOAP request and response. Once you click on the Invoke button (to test it), you see the result (note the namespace that points to http://www.eBob42.com):

As you can see, the result is a SOAP envelope that contains the simple XML string "Dr.Bob's Euro42 Web Service written in Delphi for .NET (preview)", just as I had implemented in the original Delphi for .NET source code.
Of course, for the simple About method, this isn't that exciting. But if you try the same for the GuldenToEuro or EuroToGulden method, you'll notice that you can now actually enter a value for the arguments that are passed to these methods (remember that About didn't have an argument, only a result).

In the above screenshot, I've entered 100 as value for the Euro parameter. If we click on the Invoke button now, it will send a SOAP envelope with the value 100 (for Euro) to the Euro42 web service in order to invoke the EuroToGulden method and retrieve the response, which is shown below:

And indeed, 100 euros is 220.371 guldens.

Importing Delphi for .NET Web Services
Once you've tested the web service (locally) and are happy to deploy it, it's time to tell other developers that the web service is available so they can use the WSDL in order to consume the web service. This is again less complex than it sounds, and involves importing the WSDL generated by the web service itself.
Assuming you have the .NET SDK installed, then you can find WSDL.exe in the Bin directory of the FrameworkSDK directory. It can be called with a number of arguments, including the /language which can be set to "cs" (for C#), but not to Delphi unfortunately.
Using wsdl.exe /language:cs /n:Euro42 http://localhost/scripts/eBob42.asmx?WSDL we can generate a C# import unit Euro42.cs for the Euro42 web service (using the explicit namespace "Euro42" - we'll get back to that in a moment). Inside this generated source code, we can find a class called Euro42, with methods About, GuldenToEuro and EuroToGulden. The rest is all needed to call the server, pass arguments, etc. using a proxy class. Before we can use this proxy class, we must first compile the generated source code using the C# command-line compiler (part of the .NET Framework), as follows (the /out part is optional):

  csc.exe /t:library /out:Euro42.dll Euro42.cs
Once we have a proxy DLL, we can make calls to the remote web service by using this proxy object in the DLL. An example C# project to do this is implemented as follows (inside a file called UseEuro42.cs):
  using System;
  namespace Euro42
  {
    class UseEuro42
    {
      static void Main(string[] args)
      {
        Euro42 euro = new Euro42();
        Console.WriteLine(euro.About());
        double gulden = euro.EuroToGulden(100);
        Console.WriteLine("100 euro = " + gulden.ToString() + " guldens.");
        Console.WriteLine("100 gulden = " + euro.GuldenToEuro(100).ToString() + " euros.");
      }
    }
  }
We can compile this C# application using the following command-line:
  csc.exe /r:Euro.dll UseEuro42.cs
And we can then run the UseEuro42.exe which will produce the expected output, as can be seen below:
  Dr.Bob's Euro42 Web Service written in Delphi for .NET (preview)
  100 euro = 220.371 guldens.
  100 gulden = 45.3780216090139 euros.
Of course, it would be nice if we could import web services with the Delphi for .NET preview command-line compiler as well. And although the wsdl.exe utility cannot produce Delphi source code, we can still use the resulting Euro42.dll assembly. I'll demonstrate this with the following Delphi for .NET console application called UseEuro42.dpr:
  program UseEuro42;
  {$APPTYPE CONSOLE}
  uses
    Euro42;
  var
    E: Euro42.Euro42;
  begin
    E := Euro42.Euro42.Create;
    writeln(E.About);
    writeln('100 euro = ', E.EuroToGulden(100), ' guldens.');
    writeln('100 gulden = ', E.GuldenToEuro(100), ' euros.')
  end.
Note the Euro42.Euro42 construction, which is needed because the first Euro42 points to the name of the assembly (which has to use the same namespace, otherwise the -LU flag doesn't work for me), and the second Euro42 points to the class type within the unit and namespace of the same name. The project UseEuro42.dpr can be compiled as follows, using the -LU flag to indicate that we need to import and use the Euro42.dll assembly:
  dccil -LUEuro42 UseEuro42.dpr
This will produce an error message (Error: Identifier redeclared '.Euro42'), but we can just ignore it, since it will also produce a perfectly fine UseEuro42 executable. Running the resulting UseEuro42.exe produces the exact same results as before, so as long as we remember to import an external web service with wsdl.exe using the /n: switch to add a namespace (which must be the same name as the resulting filename), we can import/use it within Delphi for .NET applications just fine.

Importing in Delphi 7
As final example, I want to demonstrate how we can use the Delphi for .NET web service in a Win32 version of Delphi (in this case Delphi 7 Studio). Few people know that Delphi also comes with a command-line WSDL Import unit, called WSDLImp.exe. We can use this tool to generate a Delphi import unit for the Euro42 web service as follows:

  WSDLImp.exe http://localhost/scripts/eBob42.asmx?WSDL
This will produce a file eBob42.pas (and not Euro42.pas), with - among others - the following snippet to describe the interface type Euro42Soap as well as the GetEuro42Soap function to return this interface:
  type
    Euro42Soap = interface(IInvokable)
    ['{A6AE3E42-89EC-46DE-4455-E72DB3AB4E75}']
      function About: WideString; stdcall;
      function GuldenToEuro(const Gulden: Double): Double; stdcall;
      function EuroToGulden(const Euro: Double): Double; stdcall;
    end;

  function GetEuro42Soap(UseWSDL: Boolean=System.False;
    Addr: string=''; HTTPRIO: THTTPRIO = nil): Euro42Soap;
Inside a Delphi 7 application we can call the GetEuro42Soap function to obtain the Euro42Soap interface, and then call the About, GuldenToEuro or EuroToGulden functions. Calling a .NET web service from a Win32 application, so this is yet another way to "connect" the .NET world with the Win32 world.
For those of you who want to try this on their own, the Euro42 .NET web service is available on the internet as http://www.eBob42.com/cgi-bin/eBob42.asmx (as you may have noticed from the screenshots in this article), so you can use this web service and connect to it using Delphi 7 or any other tool. Feel free to contact me with questions, comments or feedback - you know where to find me!
This webpage © 2003-2010 by Bob Swart (aka Dr.Bob - www.drbob42.com). All Rights Reserved.