Announcement

Collapse
No announcement yet.

Webservice mit D5 - Programm "konsumieren"

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Webservice mit D5 - Programm "konsumieren"

    <pre>
    Hallo,

    eine Frage: Ist es auch mit D5-Enterprise möglich einen
    Windows-Client zu entwickeln, der einen .NET-Webservice
    nutzen kann??
    Die meisten Beispiele setzen wohl immer D6-7 vorraus..

    MfG

    Max

    </pre>

  • #2
    Hallo,

    &gt;Ist es auch mit D5-Enterprise ....

    ja, es reicht Delphi 5 <b>Professional</b> aus, um einen Web Service sowie einen Client für den Web Service zu schreiben. Die teuere Enterprise-Version von Delphi 6/7 wird nur benötigt, wenn man den Borland-Weg einschlagen möchte. Der Microsoft-Weg eines Web Service bzw. eines Web Service-Clients ist für Delphi kostenlos, das <i>Microsoft SOAP Toolkit 2.0</i> ( http://www.microsoft.com/downloads/details.aspx?FamilyId=147ED727-0BE8-48A1-B1DA-D50B1EA582CB&displaylang=en ) kann frei installiert werden.

    Das folgende Beispiel für einen MS SOAP-Client stammt aus einem Artikel, den ich vor einiger Zeit für <i>DER ENTWICKLER 3.2002</i> geschrieben habe. Da der Client nur auf die Microsoft-COM-Objekte für einen Web Service zugreift, reicht jede Delphi-Version aus, die COM-Objekte aufrufen kann. Die komplette Arbeit der Anbindung an den Web Service wird innerhalb der aufgerufenen COM-Objekte erledigt. Der "Haken" an der Sache besteht nur darin, dass nur die späte Bindung zur Verfügung steht (d.h. es gibt keine Programmierhilfe und Codevervollständigung für die Web Service-Methoden).

    <pre>

    unit MSSOAPClientFrm;

    interface

    uses
    Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
    Dialogs, StdCtrls, ComCtrls;

    type
    TForm1 = class(TForm)
    StatusBar1: TStatusBar;
    Label1: TLabel;
    Edit1: TEdit;
    UpDown1: TUpDown;
    ButtonDoWork: TButton;
    procedure ButtonDoWorkClick(Sender: TObject);
    private
    { Private-Deklarationen }
    public
    { Public-Deklarationen }
    end;

    var
    Form1: TForm1;

    implementation

    {$R *.dfm}

    uses MSSOAPLib_TLB; // Import der Microsoft Soap Type Library in Delphi 6

    { Inhalt der vom Wizard generierten WSML-Datei: Servicename + Portname

    <?xml version="1.0" encoding="UTF-8" ?>
    - <!-- Generated 03/03/02 by Microsoft SOAP Toolkit WSDL File Generator, Version 1.02.813.0
    -->
    - <servicemapping name="OSWebService">
    - <service name="OSWebService">
    <using PROGID="OSWebServiceCOMplus.OSWebServiceCOMplusObj " cachable="0" ID="OSWebServiceCOMplusObjObject" />
    + <port name="OSWebServiceCOMplusObjSoapPort">
    - <operation name="DoWork">
    - <execute uses="OSWebServiceCOMplusObjObject" method="DoWork" dispID="2">
    <parameter callIndex="1" name="iInput" elementName="iInput" />
    <parameter callIndex="2" name="iOutput" elementName="iOutput" />
    <parameter callIndex="3" name="sSrvMsg" elementName="sSrvMsg" />
    </execute>
    </operation>
    </port>
    </service>
    </servicemapping>
    }

    procedure TForm1.ButtonDoWorkClick(Sender: TObject);
    var
    aSOAPClt : ISOAPClient;
    vClient : OleVariant;
    iInput : Integer;
    iOutput : Integer;
    sSrvMsg : String;
    begin
    aSOAPClt := CoSoapClient.Create;
    aSOAPClt.mssoapinit('http://localhost/WebService/OSWebService.WSDL',
    'OSWebService', 'OSWebServiceCOMplusObjSoapPort',
    'http://localhost/WebService/OSWebService.wsml');
    vClient := aSOAPClt;
    iInput := UpDown1.Position;
    vClient.DoWork(iInput, iOutput, sSrvMsg);
    ShowMessage(IntToStr(iOutput));
    StatusBar1.SimpleText := sSrvMsg;
    end;

    end.

    </pre>

    Comment


    • #3
      <pre>
      Vielen Dank für die schnelle Hilfe, Herr Kosch!

      ein Problem hätte ich noch:
      Bei einem mit VS.NET (ASP.NET Webdienst) oder
      Webmatrix (XML Webservice) erstellten Dienst
      (basierend auf einer .asmx-Datei) bekomme ich nicht die
      erforderlichen WSDL/WSML-Dateien.
      Ich gebe zu, bei den MS-Tools bin ich absoluter Neuling.
      Nach dem, was ich Web so finden konnte müsste es doch möglich
      sein einen .NET-Webservice mit einem SoapClient (wie in Ihrem Beispiel) nutzen zu können.
      Allerdings gelingt es mir nicht, die benötigten WSDL bzw. WSML-
      Dateien zu erzeugen.
      Ich kann mit WSDL.EXE eine C#-Datei erzeugen daraus auch per
      csc.exe eine DLL (im Falle Webmatrix, VS.NET stellt auch gleich das bin-Verzeichnis mit DLL zur Verfügung).
      Wenn ich aber mit dem SOAP-Toolkit Wizard aus dieser DLL die
      WSDL/WSML-Dateien erzeugen will kommt lediglich eine Fehlermeldung
      "Initialization failed ... internal error -2147312566"
      Mir ist sowieso etwas unklar, warum solche Tools wie VS.NET
      nicht sofort die WSDL und WSML-Dateien erzeugen.
      Muss meine erste Frage also präzisieren: Kann man mit dem unter D5
      eingebundenen SoapClient aus der MSSOAPLIBxx_TLB auch einen
      ASP.NET Webdienst bzw. XML-Webservice nutzen??

      </pre&gt

      Comment


      • #4
        Hallo,

        &gt;Mir ist sowieso etwas unklar, warum solche Tools wie VS.NET <br>
        &gt;nicht sofort die WSDL und WSML-Dateien erzeugen.

        Diese Infos werden bereits automatisch erzeugt und können im "Testfenster" des Web Services über den Link "<i>The following operations are supported. For a formal definition, please review the <b>Service Description</b>.</i>" abgerufen werden.

        In der folgenden Schritt-für-Schritt-Anleitung stelle ich die Arbeitsschritte vor, die angefangen vom .NET-Web Service bis zum Delphi 5-Client notwendig sind (der Schritt 2 ist "überflüssig" und dient nur dem besseren Verständnis).

        Schritt 1: Mini-Webservice in Notepad schreiben:
        <pre>

        using System;
        using System.Web.Services;

        public class OSMyFirstWebService {

        [WebMethod]
        public String HelloWebService( String str )
        {
        return str + " (WebService)";
        }
        }

        </pre>
        Schritt 2: Web Service über den Internet Explorer aufrufen (Bsp: <i>http://localhost/OSASPNET/OSFirstWS.asmx</i>), die CLR von .NET compiliert das Teil beim erstmaligen Aufruf automatisch als Assembly (DLL). Wenn ein Web Service (genauer gesagt die ASMX-Datei) ohne QueryString aufgerufen wird, zeigt ASP.NET die Standardseite an. Diese Seite stammt aus der im <i>Config</i>-Unterverzeichnis des .NET-Frameworks (im Fall von Windows XP und dem .NET-Framework 1.0 also im Verzeichnis C:\WINDOWS\Microsoft.NET\Framework\v1.0.3705\CONFI G) liegenden Datei <i>DefaultWsdlHelpGenerator.aspx</i>. Wie der Dateiname schon vermuten lässt, steckt dahinter der WSDL-Generator, so dass man die WSDL-Informationen über den Internet Explorer abrufen und als Datei lokal abspeichern kann. Alternativ dazu kann man diese Infos auch über das Tool <b>DISCO.EXE</b> aus dem .NET Framework SDK abrufen.

        Schritt 3: Delphi 5 importiert einmalig die Typbibliothek von MS SOAP

        Schritt 4: Delphi 5 ruft den ASP.NET Web Service auf:

        <pre>

        <b>uses</b> MSSOAPLib_TLB; <font color="#003399"><i>// Import der Microsoft Soap Type Library in Delphi 5</i></font>

        <b>procedure</b> TForm1.Button1Click(Sender: TObject);
        <b>var</b>
        aSOAPClt : ISOAPClient;
        vClient : OleVariant;
        sSrvMsg : <b>String</b>;
        <b>begin</b>
        aSOAPClt := CoSoapClient.Create;
        aSOAPClt.mssoapinit(<font color="#9933CC">'http://localhost/OSASPNET/OSFirstWS.asmx?WSDL'</font>,
        <font color="#9933CC">'OSMyFirstWebService'</font>, <font color="#9933CC">'OSMyFirstWebServiceSoap'</font>,
        <font color="#9933CC">''</font>);
        vClient := aSOAPClt;
        sSrvMsg := vClient.HelloWebService(<font color="#9933CC">'MSSOAP-Client'</font>);
        ShowMessage(sSrvMsg);
        <b>end</b>;

        </pre>

        Schritt 5: Delphi 5-Client starten - die MessageBox zeigt den vom Web Service geänderten Text an.

        Somit hat der praktische Test nachgewiesen, dass Delphi 5 erfolgreich einen mit .NET geschriebenen Web Service aufrufen kann

        Comment


        • #5
          <pre>
          Tausend Dank, Herr Kosch!
          Mit dieser narrensicheren Anleitung hat es geklappt ;-)
          Habe mich wohl auch zu sehr an den WDSL/WSML-Dateien der
          SoapToolkit-Samples 'festgebissen'.

          Jetzt grübele ich noch an der Übergabe eines .NET Datasets
          per Webservice um es in ein Recordset o.ä. auf Delphi-Seite
          zu übernehmen.
          Da es noch nicht gelingt, habe ich mir erstmal mit der
          Übergabe der XML-Daten ( custDS.GetXml() ) und einem vorhandenen XML-Parser geholfen - nicht sehr elegant, aber mit der Umsetzung
          von .NET DataSets, die ja auch mehrere Tabellen enthalten
          können, auf etwas Delphi-kompatibles haperts noch.

          Die WebMethode könnte wohl so aussehen:
          [WebMethod( Description = "Dataset mit Usern", EnableSession=false)]
          public DataSet GetUserDS(string usearch)
          {
          string selcmd = "SELECT * from tuser WHERE username like '%"
          + usearch + "%'";
          SqlConnection sconn = new SqlConnection("server=local);"
          +"InitialCatalog=knetweb;User ID=sa;Password=dotnet");
          SqlDataAdapter custDA = new SqlDataAdapter(selcmd, sconn);
          DataSet custDS = new DataSet("USERLIST");
          custDA.MissingSchemaAction = MissingSchemaAction.AddWithKey;
          custDA.Fill(custDS, "USERREC");
          return custDS;
          }

          Der Empfänger:
          Delphi5 mit MSSOAPLib30_TLB // SoapToolkit 3.0

          ..
          var
          aSOAPClt : ISOAPClient;
          vClient : OleVariant;
          ds: OleVariant;
          begin

          // URL = 'http://localhost/websvc.asmx?WSDL'
          aSOAPClt := CoSoapClient30.Create;
          aSOAPClt.mssoapinit(URL, 'TestWeb', 'TestWebSoap', '');
          vClient := aSOAPClt;
          ds := vClient.GetUserDS(Edit1.Text);
          // soweit klappts auch, aber nun ??

          end;

          Es gibt wohl Kompo's in Web, die setzen aber auch wieder auf D6 oder 7 auf.., geht's auch anders?

          </pre&gt

          Comment


          • #6
            Hallo,

            &gt;..geht's auch anders?

            Sicher - denn der .NET-Webservice liefert das gefüllte ADO.NET-DataSet-Objekt in Form einer Zeichenkette mit XML-Daten an den Client zurück. Somit muss der Web Service nicht erst explizit XML anfordern - das passiert völlig automatisch. Wenn man den Rückgabewert in einem TMemo anzeigen lässt, sollten die Datensätze im XML-Text sichtbar sein.

            Eine andere Frage ist, wie diese XML-Daten in eine von Delphi auswertbare Form (Bsp: TDBGrid) gebracht werden können. In <b>Delphi 6 Enterprise</b> steht das <i>XML-Umwandlungs-Tool</i> zur Verfügung, um auf visuellen Weg eine Transformationsdatei anlegen zu lassen. Das gleiche Tool ist zwar auch in <b>Delphi 7 Enterprise</b> verfübar, aber dort wegen eines Bugs an dieser Stelle nicht einsetzbar (Delphi 7 liefert nur die Tabellenstruktur, aber keine Daten zurück). Außerdem können die Daten über den Borland-Weg nur in ein TClientDataSet geladen werden - also darf im gefüllten DataSet immer nur die Datenmenge aus einer Tabelle sein.

            Alternativ könnte man die XML-Umwandlung auch von Hand erledigen - allerdings habe ich vor derartigen "Untiefen" immer einen großen Bogen gemacht :-)

            Es gibt da aber noch einen Weg. Der .NET Web Service darf alternativ zu ADO.NET selbstverständlich auch auf die "alten" ADO-Objekte zurückgreifen und die Daten im <b>Recordset</b>-Objekt von ADO ablegen.
            <pre>

            ADODB.Connection aADOCon = new ADODB.Connection();
            ADODB.Recordset aRS;
            aADOCon.ConnectionString = sCSADO;
            aADOCon.CursorLocation = ADODB.CursorLocationEnum.adUseClient;
            aADOCon.Open(sCSADO, "", "", 0);
            try
            {
            aRS = new ADODB.Recordset();
            aRS.CursorLocation = ADODB.CursorLocationEnum.adUseClientBatch;
            aRS.Open("SELECT * FROM " + sNP, aADOCon,
            ADODB.CursorTypeEnum.adOpenStatic, ADODB.LockTypeEnum.adLockBatchOptimistic, 0);
            aRS.ActiveConnection = null;
            ...
            // Umwandeln
            ...
            }
            finally
            {
            aADOCon.Close();
            Marshal.ReleaseComObject(aADOCon);
            }
            </pre>
            Über den ADO-Weg kann das gefüllte Recordset in XML konvertiert werden, wie das folgende Delphi-Beispiel zeigt (das sich aber auch in .NET nachbauen lässt):
            <pre>

            function RecordsetToXML2(const aRS: Recordset): String;
            var
            aStrm : Stream;
            begin
            aStrm := CoStream.Create;
            aRS.Save (aStrm, adPersistXML);
            Result := aStrm.ReadText(aStrm.Size);
            aStrm.Close;
            end;

            </pre>
            Der .NET Web Service liefert dann nur als String den XML-Inhalt an den Delphi 5-Client zurück. Und der Delphi 5-Client greift auf die Hilfe der gleichen ADO-Objekte zurück, um aus den XML-Daten wieder eine gefüllte Recordset-Objektinstanz zu machen:
            <pre>

            function XMLToRecordset (const XML: string): Recordset;
            var
            stm: Stream;
            begin
            Result := CoRecordset.Create;
            Result.CursorLocation := adUseClient;
            stm := CoStream.Create;
            stm.Open (EmptyParam, adModeUnknown,
            StreamOpenOptionsEnum(adOpenStreamUnspecified), '', '');
            stm.WriteText (XML, stWriteChar);
            stm.Position := 0;
            Result.Open (stm, EmptyParam, CursorTypeEnum(adOpenUnspecified),
            LockTypeEnum(adLockUnspecified), 0);
            stm.Close;
            end;

            </pre>
            Und wenn der Client erst einmal die gefüllte Recordset-Objektinstanz hat, reicht die Zuweisung an <i>TADODataSet.Recordset</i> aus, um die Daten zum Beispiel im TDBGrid anzeigen zu können.

            Resümee: Je länger ich darüber nachdenke, scheint der ADO-Weg über das konvertierte Recordset die am Besten geeignete Alternative zu sein. Der Weg hat aus meiner Sicht zwei gravierende Vorteile: <br>
            1. Er funktioniert mit jeder Delphi-Version (auch Professional) <br>
            2. Er ist unabhängig von den XML-Bugs der verschiedenen Delphi Enterprise-Versionen (6, 6.01, 6.02, 7)<br>

            P.S: Das sollte auch ein gutes Thema für einen Artikel im DER ENTWICKLER sein :-)
            &#10

            Comment

            Working...
            X