Freigeben über


Dieser Artikel wurde maschinell übersetzt.

Windows mit C++

Windows-Webdienste

Kenny Kerr

Downloaden des Codebeispiels

Einer der Hauptgründe für viele Entwickler in geringerem Maße auf die Microsoft .NET Framework und Java flocked war die Tatsache, die es es viel einfacher zu schreiben, Software für das Internet vorgenommen. Gibt an, ob Sie eine HTTP-Client- oder Serveranwendung, die .NET Framework geschrieben wurden, mussten Sie die mit Klassen für HTTP-Anforderungen vornehmen und problemlos XML-Verarbeitung behandelt. Sie konnte auch SOAP-Clients aus WSDL-Dokumente generieren und SOAP-Server mit ASP.NET implementieren. Als die Standards entwickelt, um Webdienste ausgereift, Microsoft Windows Communications Foundation (WCF), auch auf .NET Framework, die zunehmend komplexen Webstandards verwenden, für die Behandlung von anderen Transporte, z. B. TCP und UDP, erleichtern und Bereitstellen von vielseitigere Sicherheitsoptionen erstellt.

C++-Entwickler wurden jedoch beibehalten, Fragen Sie sich, ob es sogar praktisch von C++ zum Schreiben von Webanwendungen war. Microsoft hat eine Reihe von temporären Lösungen in Form von ATL-Klassen und ein COM-basierte Toolkit, jedoch in diese mit den Fortschritt zu halten konnte nicht, die die verwaltete SOAP-Stapel vorgenommen hatte und wurden daher größtenteils aufgegeben Ende bereitgestellt.

Entwickler bei Microsoft noch nicht über die C++-Entwickler vergessen, trotz der scheinbar single-minded den Fokus auf dem .NET Framework. In der Tat viele davon sind weiterhin und ist weiterhin Leidenschaftliches C++-Entwickler. Als Ergebnis erhalten Sie Lösungen, die C++-Entwicklern helfen, ein wichtiger Bestandteil des Microsoft-Strategie für die Windows-Plattform. Natürlich landen viele der APIs, die an C++-Entwickler gerichtet sind auch viele der verwalteten Frameworks underpinning.

Obwohl es gibt zu viele hier auflisten, einige sind erwähnenswert. Windows HTTP Services (WinHTTP)-API bietet eine leistungsfähige und flexible Lösung für das Schreiben von HTTP-Clients. Sie erhalten weitere Informationen zu WinHTTP in meinem Artikel vom August 2008. Der HTTP-Server-API (http.sys) stellt die Grundlagen zum Erstellen von Hochleistungs-HTTP-Server ohne auf eine vollwertige Webserver wie IIS (INTERNETINFORMATIONSDIENSTE) zurückgreifen zu müssen. In der Tat IIS selbst diese dieselbe API basiert. Und natürlich der XmlLite-API bietet einen kleinen und schnellen XML-Parser für systemeigenen Code. Sie erhalten weitere Informationen zu XmlLite in meinem Artikel vom April 2007 Feature.

Angesichts all dies ist es immer noch recht eine schwierige Aufgabe, einen SOAP-Client oder Server zu schreiben. Obwohl die SOAP-off “ einfachen ” gestartet (das ist was “ S ” steht für), es nicht lange auf diese Weise bleiben. HTTP.sys, WinHTTP und XmlLite möglicherweise erhalten Sie eine lange Möglichkeit durch Behandeln des HTTP-Transports und der XML-Analyse, besteht jedoch weiterhin eine Menge Code schreiben, den Communications Layer behandeln: Dinge wie die Formatierung und Interpretieren der SOAP-Headern, ganz zu schweigen Unterstützung von anderen Transporte wie TCP oder UDP. Selbst wenn Sie all dies irgendwie verwalten können, sind Sie weiterhin nach links mit der Analyse von SOAP-Envelopes anstelle von logische SOAP-Operationen als Funktionsaufrufe behandeln können.

Diese Kopfschmerzen sind ebenfalls, eine Sache der Vergangenheit. Seit der Einführung der WWS-API (Windows Web Services) müssen sich C++-Entwickler in der Welt der Webdienste nicht mehr als Bürger zweiter Klasse fühlen. WWS wurde entwickelt von Grund auf bis zu eine Implementierung vollständig systemeigenem Code SOAP, einschließlich der Unterstützung für viele der WS-* Protokolle. WWS wird, genau genommen verfügbar gemacht, über eine C-API, die Interoperabilität mit anderen Sprachen und die Laufzeiten sehr einfach machen, aber es ist der C++-Entwickler, der wahrscheinlich am meisten profitieren werden. In der Tat mit ein wenig Unterstützung von C++ möglich eine echte Joy verwenden – wie Sie in diesem Artikel sehen dürfen.

Architektur und Prinzipien

WWS wird alles, die .NET Framework-basierten Bibliotheken nicht verkörpert. Es ist für systemeigenen Code vorgesehen. Es wurde entwickelt, um eine minimale Anzahl von Abhängigkeiten. Es soll als wenig Speicher wie möglich verwenden. Und es schnell sein soll. Wirklich schnelle. Das Team zuständig für die Entwicklung von WWS wird auf jedem neuen Build, Vergleich mit WCF und RPC-Leistungstests ausgeführt. RPC wird als eine Art Basisplan verwendet, da nichts kann schneller sein, aber es zuverlässige Möglichkeit bietet der laufenden Geschwindigkeit Regressionen. Es ist, jedoch beim Vergleichen von WCF und WWS beleuchtet. Abbildung 1 zeigt einen Vergleich des Workingsets für einen Client mithilfe von WCF und WWS. Das ist eine ziemlich drastische Rand, aber vielleicht nicht, wann überraschend Sie denken, dass .NET Framework beteiligt ist. Abbildung 2, allerdings sollte überrascht, wenn Sie, wie viele andere WCF werden die neuesten Stand der Technik berücksichtigen. Es zeigt den Durchsatz in Vorgänge pro Sekunde für einen Server mithilfe von WCF und WWS. WWS ist mehr als doppelt so schnell! Verstehen Sie mich bitte nicht falsch: Es gibt nichts auszusetzen WCF oder .NET Framework, aber wenn Sie etwas klein und schnell, es ist schwierig, C++ und systemeigenem Code zuvorkam. Aber Sie wissen, dass bereits!


Abbildung 1 Vergleich Client Arbeitssatz (unten ist besser)

Die Common Language Runtime WWS ist in WebServices.dll, verpackt die im Lieferumfang von Windows 7 und Windows Server 2008 R2 enthalten ist. Es ist auch als Systemupdate für Windows XP und höher verfügbar. Aus WebServices.dll exportierte Funktionen stellen die WWS-API dar, und Sie erhalten Zugriff auf diese durch Verknüpfen mit WebServices.lib und einschließlich der WebServices.h-Header-Datei aus dem Windows SDK. So weit, so gut. Aber was das API aussehen? Nun, erfordert im Gegensatz zu COM-Format-APIs wie der XmlLite oder Direct2D, diese C-API angenommen, eine Gruppe von logischen Ebenen und Objekte, die hinter den Kulissen live und sind nur bereit, um zu verlassen. Let’s zunächst betrachten Sie hinsichtlich der Schichten. Abbildung 3 zeigt die Ebenen der Funktionalität, die von der WWS-API verfügbar gemacht, mit jeder Schicht bei der eine darunter erstellen. Jede Schicht wird durch eine Reihe von Funktionen und Strukturen dargestellt und stellt eine Reihe von Abstraktionen bereit. Die Anwendung kann machen, wie Sie denken können, verwenden Sie eines der Schichten, aber in den meisten Fällen möchten Sie das Dienstmodell verwenden, die das einfachste Programmiermodell bereitstellt und blendet viele Details für Sie. Die Transporte Schicht ist einfach eine Erinnerung, die darunter alle gibt es einige Netzwerkprotokoll werden. WWS verwendet WinHTTP, HTTP.sys oder Winsock, abhängig von der ausgewählte Transport und gibt an, ob Sie verwendet wird, um ein Client oder Server zu implementieren.


Abbildung 2 Vergleich von Server-Durchsatz (höher ist besser)


Abbildung 3 Layered API

Wie sein Name schon sagt, wird in der Dienstmodellschicht vollständig abstrahiert die SOAP-Nachrichtenaustausch und logischen Webdienstvorgänge als Funktionsaufrufe modelliert. Es ist, nicht jedoch eigenständig, sondern stützt sich auf die Verwendung von ein Befehlszeilenprogramm namens Wsutil.exe aus dem Windows SDK. Auf der Grundlage einer WSDL-Datei, dieses Tool generiert eine Headerdatei und eine C-Quelldatei mit der größte Teil des Codes erforderlich, beide Herstellen einer Verbindung mit einem Webdienst, der der angegebenen Beschreibung und wie implementieren ein Webdienst, dauert wichtig sind, um sicherzustellen, dass die Kanal-Bindung ist richtig konfiguriert sowie Nachrichten ordnungsgemäß formatiert sind. Dies ist bei weitem einfachste Ansatz und bietet ein Programmiermodell, das ist sehr ähnlich wie Erwartungen von herkömmlichen RPC.

Der Kanal dar, auf der anderen Seite verfügbar macht, die Nachrichten gesendet und empfangen auf einen bestimmten Transport, aber immer noch optional, abschirmt Sie davor, tatsächlich die Nachrichten selbst formatieren. Der Vorteil besteht hier darin, dass Sie aus bestimmten Transport abgeschirmte sind und Codierung, die verwendet wird. Die Kanal-Ebene ist, in denen Sie steuern, Bindungsinformationen und wo Sie Ihre Kommunikation, ob für die Authentifizierung oder Datenschutz sichern können.

Die XML-Schicht, die Sie die Formatierung von Nachrichten und Serialisierung von Daten gewährt. Sie haben vollen Zugriff auf den Inhalt von Nachrichten jedoch aus bestimmten Codierung abgeschirmte, ob Sie mit Text, binäre Kommunikation sind oder MTOM. Sie vielleicht überraschen, dass WWS eine eigene XML-Parser verfügt hören. Warum ist nicht es einfach XmlLite verwenden? Obwohl XmlLite sicherlich einfache und sehr schnell ist, ist es ziemlich eine perfekte Übereinstimmung für eine Reihe von Gründen nicht. Die offensichtliche Ursache ist, dass WWS andere Codierungen unterstützen im Gegensatz zu XmlLite nur Text unterstützt. SOAP-Nachrichten werden in der Regel auch codiert mit UTF-8, während Sie bei XmlLite verfügbar macht alle Eigenschaften mit Unicode-Zeichenfolgen und dies führt zu unnötigen Kosten beim Kopieren von Werten. WWS ist auch sehr strenge Speicher Verbrauch Ziele (es ist tatsächlich eine API dafür, wie wir später sehen werden), können mit XmlLite nicht erfüllt werden. Letztendlich konnte das WWS-Team einen benutzerdefinierten Parser speziell für SOAP implementieren, das wesentlich schneller als XmlLite. Sollten Sie beachten, der nicht der WWS-Parser XmlLite ersetzen soll. Als eine allgemeine XML-Parser es ist schwierig, beat, aber die XML-WWS-Schicht bietet Entwicklern mit sehr spezifischen Features zur Serialisierung von Daten effizient in und aus SOAP-Nachricht mithilfe der Teilmenge der XML-SOAP-erforderlich.

Abgesehen von den Funktionen und Datenstrukturen, die logisch zu diese drei Schichten gehören, enthält die WWS-API eine Reihe von Funktionen, die alle Ebenen, einschließlich Fehlerbehandlung, asynchronen Abschluss, Abbruch, Speicherverwaltung und vieles mehr gemeinsam sind. Da ich beschränkten Speicherplatz haben einen und Ihnen den schnellen Einstieg zu erleichtern möchten, werde ich den Rest dieses Artikels auf die Verwendung des Dienstmodells für die Erstellung eines Web Service-Client und Server zu beschränken. In einem zukünftigen Artikel werde ich stärker in den anderen Teilen des WWS untersuchen.

Erste Schritte

Um zu Beginn wird der minimale Webdienstdefinition von Abbildung 4 verwendet. Diese WSDL-Dokument definiert die Typen, Nachrichten, Vorgänge, Endpunkte und Kanalbindungen des Dienstes. Führen Sie als Erstes wird durch Wsutil.exe wie folgt ausgeführt:

Wsutil.exe Calculator.wsdl

Dies erzeugt eine Headerdatei namens Calculator.wsdl.h und eine C-Quelldatei Calculator.wsdl.c aufgerufen.

Abbildung 4 Web Service Definition

<wsdl:definitions xmlns:wsdl="https://schemas.xmlsoap.org/wsdl/"
 xmlns:soap="https://schemas.xmlsoap.org/wsdl/soap/"
 xmlns:xsd="ttp://www.w3.org/2001/XMLSchema"
 xmlns:tns="http://calculator.example.org/"
 targetNamespace="http://calculator.example.org/">
    
 <wsdl:types>
   <xsd:schema elementFormDefault="qualified" 
    targetNamespace="http://calculator.example.org/">
    <xsd:element name="AddRequest">
     <xsd:complexType>
      <xsd:sequence>
       <xsd:element name="first" type="xsd:double" />
       <xsd:element name="second" type="xsd:double" />
      </xsd:sequence>
     </xsd:complexType>
    </xsd:element>
    <xsd:element name="AddResponse">
     <xsd:complexType>
      <xsd:sequence>
       <xsd:element name="result" type="xsd:double" />
      </xsd:sequence>
     </xsd:complexType>
    </xsd:element>
   </xsd:schema>
  </wsdl:types>

  <wsdl:message name="AddRequestMessage">
    <wsdl:part name="parameters" element="tns:AddRequest" />
  </wsdl:message>
  <wsdl:message name="AddResponseMessage">
    <wsdl:part name="parameters" element="tns:AddResponse" />
  </wsdl:message>

  <wsdl:portType name="CalculatorPort">
    <wsdl:operation name="Add">
      <wsdl:input message="tns:AddRequestMessage" />
      <wsdl:output message="tns:AddResponseMessage" />
    </wsdl:operation>
  </wsdl:portType>

  <wsdl:binding name="CalculatorBinding" type="tns:CalculatorPort">
    <soap:binding transport="https://schemas.xmlsoap.org/soap/http"/>
    <wsdl:operation name="Add">
      <soap:operation soapAction=
       "http://calculator.example.org/Add" style="document"/>
      <wsdl:input>
       <soap:body use="literal"/>
      </wsdl:input>
      <wsdl:output>
       <soap:body use="literal"/>
      </wsdl:output>
     </wsdl:operation>
   </wsdl:binding>

   <wsdl:service name="CalculatorService">
    <wsdl:port name="CalculatorPort" binding="tns:CalculatorBinding">
     <soap:address location="http://localhost:81/calculator"/>
    </wsdl:port>
   </wsdl:service>

</wsdl:definitions>

Bevor wir uns an, was generiert wurde, benötigen wir einige Grundlagen abgeschlossen, unabhängig davon, ob Sie dem Client oder dem Server implementiert. Als Erstes müssen Sie ist eine Möglichkeit, um Fehlerinformationen auszudrücken. Die WWS-API stellt sowohl für eigene Funktionen sowie die SOAP-Fehler über ein Fehlerobjekt ausführlichen Fehlerinformationen zur Verfügung. Eine C-API wird, wird dieses Objekt durch ein nicht transparentes Handle und eine Reihe von Funktionen dargestellt. In diesem Fall WS_ERROR 1 stellt ein Handle zu einem Fehlerobjekt, und WsCreateError dient die Funktion, die es erstellt. Das Objekt wird durch Aufrufen der WsFreeError-Funktion freigegeben. Das Fehlerobjekt kann eine Anzahl von Zeichenfolgen mit unterschiedlichen Informationen zu einem Fehler gespeichert werden. Um diese abzurufen, müssen Sie zunächst ermitteln, wie viele Zeichenfolgen vorhanden sind. Dies erfolgt durch Aufrufen der WsGetErrorProperty-Funktion, indem er ihm das Handle auf das Fehlerobjekt und WS_ERROR_PROPERTY_STRING_COUNT-Konstante angeben. Mit diesen Informationen ausgestattet, rufen Sie die WsGetErrorString-Funktion, indem er ihm das Handle auf das Fehlerobjekt sowie der nullbasierte Index der abzurufenden Zeichenfolge. Die API-Funktionen können auch eigene Fehlerobjekte aufzufüllen. Natürlich gehen ein wenig C++ lange so, dass dies einfacher und zuverlässiger. Abbildung 5 stellt einen einfachen Fehler Objektwrapper bereit. Sie können problemlos die Zeichenfolgen in ein Fehlerobjekt auflisten, wie in Abbildung 6-dargestellt.

Abbildung 5 Error-Objekt Wrapper

class WsError
{
    WS_ERROR* m_h;
public:
    WsError* m_h(0)
    {}
    ~WsError()
    {
        if (0 != m_h)
    }
    HRESULT Create(const WS_ERROR_PROPERTY* properties,
                        ULONG propertyCount)
    {
        return WsCreateError(properties, propertyCount, &m_h);
    }
    HRESULT GetProperty(WS_ERROR_PROPERTY_ID id, void* buffer,
                            ULONG bufferSize)
    {
        return WsGetErrorProperty(m_h, id, buffer, bufferSize);
    }
    template <typename T>
    HRESULT GetProperty(WS_ERROR_PROPERTY_ID id, out T* buffer)
    {
        return GetProperty(id, buffer, sizeof(T));
    }
    HRESULT GetString(ULONG index, WS_STRING* string)
    {
        return WsGetErrorString(m_h, index, string);
    }
    operator WS_ERROR*() const
    {
        return m_h;
    }
};

Abbildung 6 auflisten die Zeichenfolgen in ein Error-Objekt

WsError error;
HR(error.Create(0, 0));
// Something goes wrong . . .
ULONG stringCount = 0;
HR(error.GetProperty(WS_ERROR_PROPERTY_STRING_COUNT,&stringCount));

for (ULONG i = 0; i < stringCount; ++i)
{
    WS_STRING string;
    HR(error.GetString(i, &string));
    wprintf(L"Error %d: %.*s\n", i, string.length, string.chars);
}

Als nächste, das wir müssen ist ein Heap-Objekt. Das Heap-Objekt ermöglicht genauere Steuerung über Speicherzuweisung beim Erstellen oder nutzen von Meldungen und, wenn verschiedene API-Strukturen zugeordnet werden müssen. Auch vereinfacht das Programmiermodell, denn es keine Notwendigkeit, Sie besteht wissen, genau wie viel Speicher benötigt für eine bestimmte Funktion erfolgreich ausgeführt wird. Viele ältere Funktionen im Windows SDK werden z. B. ist es erforderlich, dass Sie zu erraten, wie viel Speicherplatz erforderlich ist oder zunächst eine Funktion in einer bestimmten Art und Weise zu bestimmen, wie viel Speicher aufrufen sollten Sie für die Funktion erfolgreich ausgeführt werden kann reservieren. Die Verwendung des Objekts Heap WWS entfernt alle diese zusätzliche Codierung und bietet eine gute Möglichkeit, die Speicherauslastung der API zu steuern. Dies ist ebenfalls praktisch aus Gründen der Sicherheit, wo möchten Sie möglicherweise angeben erwarteten Grenzwerte auf wie viel die API reserviert werden kann. WS_HEAP 1 stellt ein Handle zu einem Heap-Objekt, und WsCreateHeap dient die Funktion, die es erstellt. Das Objekt wird durch Aufrufen der WsFreeHeap-Funktion freigegeben. Sobald es erstellt wurde, können Sie keinen Speicher von den Heap mithilfe der WsAlloc-Funktion, aber für diesen Artikel wir werde übergeben Sie einfach das Handle an das Heap-Objekt, das andere API-Funktionen verwenden zu müssen, wie erforderlich.

Abbildung 7 stellt einen einfachen Heap Objektwrapper bereit. Wenn dies, können Sie ein Heap-Objekt auf 250 Byte beschränkt wie folgt erstellen:

WsHeap heap;

HR(heap.Create(250, // max size
               0, // trim size
               0, // properties
               0, // property count
               error));

Beachten Sie, wie ich das Fehlerobjekt des Heap-Objekts Create-Methode übergeben an bin. Etwas schief gehen sollte beim Erstellen des Heap-Objekts, werde ich das Fehlerobjekt, um die Ursache herauszufinden abgefragt werden sollen.

Abbildung 7 Heap Object Wrapper

class WsError
{
    WS_ERROR* m_h;
public:
    WsError* m_h(0)
    {}
    ~WsError()
    {
       if (0 != m_h)
    }
    HRESULT Create(const WS_ERROR_PROPERTY* properties, 
            ULONG propertyCount)
    {
       return WsCreateError(properties, propertyCount, &m_h);
    }
    HRESULT GetProperty(WS_ERROR_PROPERTY_ID id, void* buffer, 
               ULONG bufferSize)
    {
        return WsGetErrorProperty(m_h, id, buffer, bufferSize);
    }
    template <typename T>
    HRESULT GetProperty(WS_ERROR_PROPERTY_ID id, out T* buffer)
    {
        return GetProperty(id, buffer, sizeof(T));
    }
    HRESULT GetString(ULONG index, WS_STRING* string)
    {
        return WsGetErrorString(m_h, index, string);
    }
    operator WS_ERROR*() const
    {
      return m_h;
    }
};
class WsHeap
{
    WS_HEAP* m_h;
public:
    WsHeap() : m_h(0)
    {}
    ~WsHeap()
    {
        if (0 != m_h) WsFreeHeap(m_h);
    }
    HRESULT Create(SIZE_T maxSize, SIZE_T trimSize, 
            const WS_HEAP_PROPERTY* properties, 
            ULONG propertyCount, 
            in_opt WS_ERROR* error)
    {
        return WsCreateHeap(maxSize, trimSize, properties, propertyCount,
                      &m_h, error);
    }
    operator WS_HEAP*() const
    {
        return m_h;
    }
};

Der Client

Die clientseitige das Dienstmodell zentriert auf dem Proxy-Dienst-Objekt. Der generierten Quellcode enthält eine Funktion mit dem Namen CalculatorBinding_CreateServiceProxy. Der Name wird von dem des Endpunkts abgeleitet oder Namen Bindung in WSDL-Dokument definiert. Diese Funktion erstellt daraus ein Proxyobjekt Dienst und gibt ein WS_SERVICE_PROXY 1 ein nicht transparentes Handle für dieses Objekt darstellt. Das Objekt wird durch Aufrufen der WsFreeServiceProxy-Funktion freigegeben. Nach dem Erstellen Ihrer Anwendung den Dienstendpunkt, der mit der WsOpenServiceProxy-Funktion öffnen und dann an den Webdienst führen Aufrufe über den Dienstproxy. Was leistet genau WsOpenServiceProxy ist abhängig von den Transport verwendet wird. Sie müssen auch Achten Sie darauf, schließen Sie den Dienstproxy-vor, um das Objekt mithilfe der WsCloseServiceProxy-Funktion freigegeben. Natürlich können aller dieser Verwaltungsgründen ordentlich in eine einfache Klasse, die in Abbildung 8 bereitgestellten gewrappt werden nach oben. Wenn dies, können Sie erstellen und öffnen den Dienstproxy-, wie in Abbildung 9 dargestellt.

Abbildung 8 Service Proxy Wrapper

class WsServiceProxy
{
    WS_SERVICE_PROXY* m_h;
public:
    WsServiceProxy() : m_h(0)
    {}
    ~WsServiceProxy()
    {
        if (0 != m_h)
        {
            Close(0, 0); // error
            WsFreeServiceProxy(m_h);
        }
    }
    HRESULT Open(const WS_ENDPOINT_ADDRESS* address, 
            const WS_ASYNC_CONTEXT* asyncContext, 
            WS_ERROR* error)
    {
        return WsOpenServiceProxy(m_h, address, asyncContext, error);
    }
    HRESULT Close(const WS_ASYNC_CONTEXT* asyncContext, 
            WS_ERROR* error)
    {
        return WsCloseServiceProxy(m_h, asyncContext, error);
    }
    WS_SERVICE_PROXY** operator&()
    {
        return &m_h;
    }
    operator WS_SERVICE_PROXY*() const
    {
        return m_h;
    }
};

Abbildung 9 Erstellen und öffnen Sie Webdienstproxys

WsServiceProxy serviceProxy;

HR(CalculatorBinding_CreateServiceProxy(0, // template value
                                        0, // properties
                                        0, // property count
                                        &serviceProxy,
                                        error));

WS_ENDPOINT_ADDRESS address =
{
    {
        static_cast<ULONG>(wcslen(url)),
        const_cast<PWSTR>(url)
    }
};

HR(serviceProxy.Open(&address,
                     0, // async context
                     error));

Die Struktur WS_ENDPOINT_ADDRESS wird verwendet, um die gesendeten Nachrichten zu adressieren. In der Regel verwenden diese Struktur Sie beim Programmieren auf der Ebene Kanal. In diesem Fall wir nur den URL Teil füllen und kümmert sich der Service-Proxy den Rest.

Zu diesem Zeitpunkt können wir eine andere generierte Funktion nämlich CalculatorBinding_Add, die unsurprisingly des Webdienstes Add-Operation darstellt. Es ist nicht wirklich viel einfacher abrufen:

const double first = 1.23;
const double second = 2.34;
double result = 0.0;

HR(CalculatorBinding_Add(serviceProxy,
                         first,
                         second,
                         &result,
                         heap,
                         0, // properties
                         0, // property count
                         0, // async context
                         error));

ASSERT(3.57 == result);

Sobald Sie fertig sind müssen mit dem Webdienst interagieren Sie lediglich die Service-Proxy-Kommunikationskanal zu schließen:

HR(serviceProxy.Close(0, // async context
                      error));

Der Server

Während das clientseitige Programmiermodell auf dem Dienstproxy-zentriert, wird der Server stattdessen erstellt und verwaltet eine Dienst-Host, die die erforderliche Common Language Runtime das Abhören von den verschiedenen Endpunkten, die auf der Grundlage der bereitgestellten Kanalinformationen bereitstellt. Erneut, da wir das Dienstmodell verwenden die meisten Details abstrahiert, und wir sind nur zum Erstellen der Dienstendpunkt und Host links. WWS wird den Rest führen.

Die erste Schritt besteht darin, den Dienstendpunkt erstellen. Das Dienstmodell erledigt dies in Form von einer anderen generierten Funktion, nämlich CalculatorBinding_CreateServiceEndpoint, wie in Abbildung 10 dargestellt. Erstellen den Endpunkt erfordert die Angabe der Adresse auf der der Endpunkt abhören soll. Dies wird zur Verfügung gestellt von der WS_STRING-Struktur, die ein Unicode-Zeichenfolge mit Längenpräfix ist und ist nicht erforderlich, um null werden beendet. Da der Endpunkt für Anforderungen erfüllen zuständig ist, müssen Sie eine Tabelle von Funktionszeigern zuordnen zu den Operationen, die vom Dienst offen gelegt bereitstellen. Die generierte CalculatorBindingFunctionTable Struktur wird für diesen Zweck verwendet. Zum Schluss der Endpunkt selbst wird durch die WS_SERVICE_ENDPOINT-Struktur dargestellt und wird im bereitgestellten Heap reserviert.

Abbildung 10 CalculatorBinding_CreateServiceEndpoint

class WsServiceHost
{
    WS_SERVICE_HOST* m_h;
public:
  WsServiceHost() : m_h(0)
  {}
  ~WsServiceHost()
  {
    if (0 != m_h)
    {
        Close(0, 0); 
        WsFreeServiceHost(m_h);
    }
  }
  HRESULT Create(const WS_SERVICE_ENDPOINT** endpoints, 
          const USHORT endpointCount, 
          const WS_SERVICE_PROPERTY* properties, 
          ULONG propertyCount, WS_ERROR* error)
  {
     return WsCreateServiceHost(endpoints, endpointCount, properties,
                      propertyCount, &m_h, error);
  }
  HRESULT Open(const WS_ASYNC_CONTEXT* asyncContext, WS_ERROR* error)
  {
    return WsOpenServiceHost(m_h, asyncContext, error);
  }
  HRESULT Close(const WS_ASYNC_CONTEXT* asyncContext, WS_ERROR* error)
  {
    return WsCloseServiceHost(m_h, asyncContext, error);
  }
  operator WS_SERVICE_HOST*() const
  {
    return m_h;
  }
};

WsServiceProxy serviceProxy;
const WS_STRING address =
{
    static_cast<ULONG>(wcslen(url)),
    const_cast<PWSTR>(url)
};

CalculatorBindingFunctionTable functions =
{
    AddCallback
};

WS_SERVICE_ENDPOINT* endpoint = 0;

HR(CalculatorBinding_CreateServiceEndpoint(0, // template value
                                           &address,
                                           &functions,
                                           0, // authz callback
                                           0, // properties
                                           0, // property count
                                           heap,
                                           &endpoint,
                                           error));

Die nächste Schritt besteht darin, den Dienst Host zu erstellen. WS_SERVICE_HOST 1 stellt ein Handle an ein Dienstobjekt Host und WsCreateServiceHost dient die Funktion, die es erstellt. Das Objekt wird durch Aufrufen der WsFreeServiceHost-Funktion freigegeben. Der WsCreateServiceHost-Funktion wird das Host-Dienstobjekt anhand einer Liste von Endpunkten erstellt. Zu diesem Zeitpunkt können Sie die Listener starten, wenn Sie auf den Endpunkten, die mit der WsOpenServiceHost-Funktion. Beenden Sie die Kommunikation mit der WsCloseServiceHost-Funktion. Wie bei den Dienstproxy-stellen Sie sicher, dass Sie den Dienst Host prior to Freigeben von es zu schließen. Erneut, können alle diese Übersichtlichkeit ordentlich in eine einfache Klasse, die in Abbildung 11 bereitgestellten gewrappt werden nach oben. Wenn dies, können Sie den Webdienst wie folgt starten:

const WS_SERVICE_ENDPOINT* endpoints[] = { endpoint };

WsServiceHost serviceHost;

HR(serviceHost.Create(endpoints,
                      _countof(endpoints),
                      0, // properties
                      0, // property count
                      error));

HR(serviceHost.Open(0, // async context
                    error));

In diesem Fall nur ein einzelner Endpunkt vorhanden ist, aber Sie sehen, wie einfach es wäre, den Dienst Host zusätzliche Endpunkte hinzufügen. Wenn der Dienst beendet wird, müssen Sie lediglich der Dienst Host Kommunikationskanäle zu schließen.

HR(serviceHost.Close(0, // async context
                     error));

Tatsächlich implementieren den Webdienstvorgang wird über der einfachste Teil:

HRESULT CALLBACK AddCallback(__in const WS_OPERATION_CONTEXT*, 
                 __in double first, 
                 __in double second, 
                 __out double* result, 
                 __in_opt const WS_ASYNC_CONTEXT* /*asyncContext*/,
                 __in_opt WS_ERROR* /*error*/)
{
    *result = first + second;
    return S_OK;
}

Die Signatur für die AddCallback-Funktion wird ebenfalls bereitgestellt, indem Sie den generierten Quellcode Zweifel darüber, wie Sie es festlegen haben soll.

Und das ist alles für diesen Monat habe ich Speicherplatz, aber müsste nun einen Überblick über die Features und Vorteile, die die Windows-Web Services-API zu bieten hat. Wie Sie sehen können, haben C++-Entwickler schließlich einen modernen SOAP-Stapel außerhalb des im Feld. Es bietet die am besten mögliche Leistung und Speichernutzung und ein Vergnügen, mit ein wenig Unterstützung von C++ zu verwenden.

Abbildung 11 Service Host Wrapper

class WsServiceHost
{
    WS_SERVICE_HOST* m_h;
public:
    WsServiceHost() : m_h(0)
    {}
    ~WsServiceHost()
    {
        if (0 != m_h)
        {
            Close(0, 0);
            WsFreeServiceHost(m_h);
        }
    }
    HRESULT Create(const WS_SERVICE_ENDPOINT** endpoints,
            const USHORT endpointCount,
            const WS_SERVICE_PROPERTY* properties,
            ULONG propertyCount, WS_ERROR* error)
    {
        return WsCreateServiceHost(endpoints, endpointCount, properties,
                                            propertyCount, &m_h, error);
    }
    HRESULT Open(const WS_ASYNC_CONTEXT* asyncContext, WS_ERROR* error)
    {
        return WsOpenServiceHost(m_h, asyncContext, error);
    }
    HRESULT Close(const WS_ASYNC_CONTEXT* asyncContext, WS_ERROR* error)
    {
        return WsCloseServiceHost(m_h, asyncContext, error);
    }
    operator WS_SERVICE_HOST*() const
    {
        return m_h;
    }
};

Wer ist es verwenden?

Verschiedene Teams bei Microsoft bereits gestartet wurden Windows Web Services (WWS) API innerhalb Ihrer eigenen Produkte oder Technologien Übernahme. In vielen Fällen Dies ersetzt kundenspezifischen SOAP-Stacks, und einige haben sogar kommerzielle Implementierungen wie Windows Communication Foundation (WCF) mit WWS zu ersetzen. Hier sind nur einige Beispiele.

Microsoft Web Services auf Geräte-API (WSD) ermöglicht Entwicklern das Schreiben von Clients und Servern, basierend auf den Geräte-Profil für Web Services (DPWS). In Windows 7 seitdem WSD-API WWS für die Erstellung und Kanonisierung von XML im SOAP-Nachrichten, die die Common Language Runtime WSD sendet. Das Team WSD plant, ihrer Verwendung von WWS zu erweitern, Hinzufügen von neuen Features und vorhandenen Code umzugestalten.

Windows CardSpace ist Microsofts Implementierung eines Systems Identität basierend auf Webdienststandards. Ursprünglich mit WCF implementiert, ist es umgeschrieben werden mithilfe von systemeigenem Code und WWS um sehr strenge Geschäftsanforderungen an die Größe der herunterladbaren Installer und die Common Language Runtime Arbeitsseiten zu erfüllen.

Microsoft Forefront Threat Management Gateway (TMG) ist eine Plattform für Sicherheit, Bereitstellung von Firewall und Zwischenspeicherung Features zum Sichern und Verbessern der Leistung von Netzwerken. Die URL-Filterfunktion verwendet WWS zur Verbindung mit der Microsoft-Absenderzuverlässigkeitsdienst, um URLs zu kategorisieren.

Schließlich stellt der Client Windows Public Key Infrastructure (PKI) automatische Verwaltung von Zertifikaten mit automatische Registrierung sowie Benutzer- und Anwendungseinstellungen gesteuerten Zertifikatregistrierung. Windows 7 stellt eine neue Gruppe von Webdiensten, die für die Zertifikatregistrierung ohne die traditional-Beschränkungen von LDAP- und DCOM abgeschlossen sein zulassen. Der PKI-Client stellt für alle Vorgänge, einschließlich ein neues Protokoll Certificate Enrollment Policy (MS-XCEP) und eine WS-Trust-Erweiterung für die Zertifikatsregistrierung (MS-WSTEP) von WWS verwenden. Der WWS-Client kommuniziert mit einem neuen Satz von Active Directory-Zertifikatdienste in Windows Server 2008 R2, die implementiert werden mit WCF, ebenso wie mit Webdiensten, die vom Aussteller des Zertifikats für öffentlichen bereitgestellt.

Kenny Kerr ist Softwarespezialist, der auf die Softwareentwicklung für Windows spezialisiert ist. Er schreibt leidenschaftlich gern und unterrichtet Entwickler in den Bereichen Programmierung und Softwareentwurf. Sie können Kerr unter weblogs.asp.net/kennykerr-erreichen.