Cet article a fait l'objet d'une traduction automatique.
Windows avec C++
Services Web Windows
Kenny Kerr
Une des principales raisons pour lesquelles de nombreux développeurs flocked pour le Microsoft .NET Framework et Java à un degré moindre, était le fait qu'il rendait beaucoup plus facile à écrire des logiciels pour Internet. Si vous écriviez un client ou serveur application HTTP, le .NET Framework a dû vous couvert avec classes pour effectuer des demandes HTTP et de traitement XML facilement. Vous pouvez même générer des clients SOAP à partir de documents WSDL et implémenter des serveurs SOAP avec ASP.NET. Comme les normes autour des services Web mûri, Microsoft a développé le Windows Communications Foundation (WCF), également créé sur le .NET Framework, pour faciliter l'utilisation des normes Web les plus en plus complexes pour la gestion des différents transports, tels que TCP et UDP et fournir des options de sécurité plus polyvalentes.
Toutefois, les développeurs C++, ont été laissés vous demandez si elle était encore pratique d'utiliser C++ pour écrire des applications Web. Microsoft avait fourni quelques solutions temporaires dans la forme de classes ATL et un kit d'Shared Computer Toolkit basés sur COM, mais en fin de que ces n'a pas pu suivre la progression qui les piles gérées SOAP avaient apportées et ont été abandonnées donc en grande partie.
Malgré le focus apparemment single-minded sur le .NET Framework, les développeurs chez Microsoft n'avez pas oublié le développeur C++. En fait, beaucoup d'entre eux sont toujours et continueront à être baisers les développeurs C++. Par conséquent, les solutions qui aident les développeurs C++ sont un élément essentiel de la stratégie de Microsoft pour la plate-forme Windows. Bien entendu, la plupart des API destinés aux développeurs C++ finissent également bon nombre des structures managées secondaires.
Bien qu'il existe trop d'énumérer ici, quelques sont important de mentionner. Windows HTTP Services (WinHTTP) API fournit une solution puissante et flexible pour l'écriture de clients HTTP. Vous pouvez en savoir plus sur WinHTTP dans mon article d'août 2008. Le serveur virtuel Exchange HTTP (http.sys) API fournit les fondations pour la création des serveurs HTTP de hautes performances sans se baser sur un serveur Web à part entière comme les Services Internet (IIS). En fait, IIS lui-même est basé sur ce même API. Et bien entendu l'API de XmlLite fournit un analyseur XML petit et rapide pour le code natif. Vous pouvez en savoir plus sur XmlLite dans mon article de la fonctionnalité avril 2007.
Tout ceci, il est toujours tout à fait à une tâche complexe pour écrire un client SOAP ou un serveur. Bien que SOAP commencé “ simple respectez (c'est ce que signifie “ S respectez), il n'a pas rester ainsi pour long. WinHTTP, HTTP.sys et XmlLite peuvent obtenir vous un moyen de long en gérant le transport HTTP et l'analyse XML, mais il y a toujours une tonne de code à écrire gérer la couche de communications : notamment mise en forme et l'interprétation des en-têtes SOAP, ne pas de mentionner la prise en charge d'autres transports tels que TCP ou UDP. Même si vous Impossible de gérer une certaine manière tout ceci, vous êtes toujours à gauche avec enveloppes SOAP au lieu d'être en mesure de traiter les opérations logiques de SOAP comme des appels de fonction d'analyse.
Eh bien, ces maux de tête est partie du passé. Avec l'arrivée de l'API des services Web Windows (WWS), les développeurs C++ n'ont plus de raisons de se considérer comme des citoyens de seconde classe dans l'univers des services Web. WWS est conçu dès le départ jusqu'à être une implémentation de code natif entièrement de SOAP, y compris la prise en charge pour un grand nombre de WS-* protocoles. WWS est, strictement parlant, exposé via une API C, rendant l'interopérabilité avec d'autres langages et les runtimes très simple, mais il est le développeur C++ qui bénéficier probablement le meilleur parti. En fait, avec un peu d'aide à partir de C++, il peut être un véritable joie à utiliser, comme vous ne doit voir dans cet article.
Architecture et principes
WWS englobe tout ce que les bibliothèques de .NET Framework ne sont pas. Il est conçu pour le code natif. Il est conçu pour introduire un nombre minimal de dépendances. Il est conçu pour utiliser en tant que peu de mémoire que possible. Et il est conçu pour être rapide. Très rapide. L'équipe responsable du développement WWS exécute les tests de performances sur chaque nouvelle version, comparaison avec WCF et RPC. RPC est utilisé comme une sorte de base dans la mesure où rien n'a pu être plus rapide, mais il fournit un moyen fiable de suivi des régressions de vitesse. Il est, cependant, éclairant lorsque vous comparez WCF et WWS. La figure 1 affiche une comparaison entre le jeu de travail pour un client à l'aide de WCF et WWS respectivement. C'est une marge assez considérable, mais qui peut-être pas surprenant lorsque vous pensez que le .NET Framework est impliqué. La figure 2, toutefois, doit être surprenant si vous, comme beaucoup d'autres, considérez WCF pour être de pointe. Elle indique le débit dans les opérations par seconde pour un serveur à l'aide de WCF et WWS respectivement. WWS est plus de deux fois aussi rapide ! Ne pas obtenir m'est incorrect : Il n'y a rien incorrect avec WCF ou le .NET Framework, mais lorsque vous devez quelque chose petit et rapide, il est difficile à battre C++ et le code natif. Mais vous savez déjà que !
La figure 1 client plage de travail de comparaison (inférieur est meilleure)
Le runtime WWS est empaqueté dans WebServices.dll, qui est fourni avec Windows 7 et Windows Server 2008 R2. Il est également disponible sous la forme d'une mise à jour du système pour Windows XP et versions ultérieures. Fonctions exportées à partir de WebServices.dll représentent l'API WWS et vous pouvez y accéder en liant à WebServices.lib et en incluant le fichier d'en-tête WebServices.h dans Kit de développement logiciel (SDK) Windows. Jusqu'ici tout va bien. Mais à quoi l'API ressemble ? Eh bien, à la différence des API de style COM comme celle de XmlLite ou Direct2D, cette API C nécessite que vous imaginez un ensemble de couches logiques et des objets qui vivent en arrière-plan et qui sont en attente uniquement pour sortir. Let’s tout d'abord jeter un œil il en termes de couches. La figure 3 illustre les couches de fonctionnalités exposées par l'API WWS, avec chaque couche s'appuyant sur celui situé dessous. Chaque couche est représentée par un certain nombre de fonctions et structures et fournit un jeu d'abstractions. Comme vous pouvez le deviner, l'application peut faire utiliser de toutes les couches, mais la plupart du temps vous souhaitez coller avec le modèle de service qui fournit le modèle de programmation plus simple et masque de nombreux détails de la pour vous. La couche de transport est juste un rappel qui sera certains protocole réseau en dessous de celui-ci tout. WWS utilisera WinHTTP, HTTP.sys ou Winsock, selon le transport sélectionné et s'il est utilisé pour implémenter un client ou un serveur.
La figure 2 comparaison débit du serveur (version supérieure est meilleure)
La figure 3 en couche API
Comme son nom l'indique, la couche de modèle de service entièrement résume les échanges de messages SOAP et modélise les opérations de service Web logiques que les appels de fonction. Il ne pas, toutefois, précédées mais repose sur l'utilisation d'un outil de ligne de commande appelé Wsutil.exe à partir du Kit de développement Kit de développement logiciel (SDK) Windows. Étant donné un fichier WSDL, cet outil génère un fichier d'en-tête ainsi qu'un fichier source C avec la plupart du code nécessaire à la fois à se connecter à un service Web de la description donnée et pour mettre en œuvre par un service Web, en prenant soin pour garantir la liaison de canal est correctement configuré et messages sont correctement mis en forme. Cela est de loin l'approche la plus simple et fournit un modèle de programmation qui est similaire ce que vous attendez de RPC traditionnel.
La couche de canal, en revanche, expose les messages envoyés et reçus sur un transport particulier, mais toujours éventuellement vous épargne d'avoir à formater en fait les messages. L'avantage ici est que vous êtes protégé du transport particulier et codage qui est utilisé. La couche de canal est dans laquelle vous contrôlez les informations de liaison et où vous pouvez sécuriser vos communications pour l'authentification ou de confidentialité.
La couche XML vous présente la mise en forme de messages et la sérialisation de données. Vous disposez d'un accès complet au contenu des messages mais protégé à partir du codage particulier si vous vous êtes en communication avec le texte, binaire ou MTOM. Vous serez peut-être surpris d'entendre que WWS possède son propre analyseur XML. Pourquoi n'est pas il juste utiliser XmlLite ? Bien que XmlLite est assurément légère et très rapide, il n'est pas tout à fait une correspondance parfaite pour un certain nombre de raisons. La raison la plus évidente est que WWS doit prendre en charge des codages différents alors que XmlLite ne prend en charge que le texte. LES messages SOAP sont aussi généralement codés à l'aide de UTF-8, bien que XmlLite expose toutes les propriétés avec des chaînes Unicode et cela introduit des coûts inutiles lors de la copie de valeurs. WWS a également des objectifs de consommation de mémoire très strict (il est en fait une API pour ce faire, comme nous le verrons plus tard) qui ne peuvent pas être satisfaites avec XmlLite. Au final, l'équipe WWS a été en mesure d'implémenter un parseur personnalisé spécialement pour SOAP s'exécute beaucoup plus rapidement que XmlLite. Gardez à l'esprit que l'analyseur WWS n'est pas destinée à remplacer XmlLite. Comme un analyseur XML d'usage général, il est difficile de battre, mais la couche WWS XML fournit aux développeurs avec fonctionnalités très spécifiques visant à sérialiser efficacement des données en / hors du message SOAP à l'aide le sous-ensemble du langage XML requis par SOAP.
À part les fonctions et structures de données qui appartiennent logiquement à ces trois couches, l'API WWS fournit un certain nombre de fonctionnalités qui sont communes à toutes les couches, y compris la gestion des erreurs, terminaison asynchrone, l'annulation, la gestion de la mémoire et bien plus encore. Dans la mesure où j'espace limité et que vous souhaitez aider vous commencez à travailler rapidement, je vais pour limiter le reste de cet article à l'utilisation du modèle de service pour la création d'un client de service Web et le serveur. Dans un futur article, je vais Explorez plus profondément les autres parties de WWS.
Mise en route
Pour commencer, je vais utiliser la définition de service Web minimale à partir de de la figure 4. Ce document WSDL définit les types, les messages, les opérations, les points de terminaison et les liaisons de canal du service. La première chose à faire est exécutez-le via Wsutil.exe comme suit :
Wsutil.exe Calculator.wsdl
Cela produira un fichier d'en-tête appelé Calculator.wsdl.h et un fichier source C appelée Calculator.wsdl.c.
La figure 4 de la définition du Service Web
<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>
Avant de nous examinons ce qui a été généré, nous devons obtenir certains éléments de base effectuées, indépendamment de si vous implémentez le client ou le serveur. La première chose que vous avez besoin est un moyen d'exprimer des informations d'erreur. L'API WWS expose des informations détaillées sur l'erreur à la fois pour ses propres fonctions ainsi que des erreurs SOAP via un objet d'erreur. Cet objet en cours d'une API C, est représenté par un handle opaque et un ensemble de fonctions. Dans ce cas, WS_ERROR 1 représente un handle d'un objet d'erreur et WsCreateError est la fonction qui le crée. L'objet est libérée en appelant la fonction WsFreeError. L'objet d'erreur peut stocker un nombre de chaînes avec différents niveaux d'informations sur une erreur. Pour récupérer ces, vous devez d'abord déterminer combien de chaînes sont présents. Pour cela en appelant la fonction WsGetErrorProperty, en lui donnant le handle à l'objet erreur et spécifiant la constante WS_ERROR_PROPERTY_STRING_COUNT. À l'aide de ces informations, vous appelez la fonction WsGetErrorString, en lui donnant le handle pour l'objet d'erreur ainsi que l'index de base zéro de la chaîne à extraire. Vous pouvez également utiliser les fonctions API pour remplir vos propres objets d'erreur. Naturellement, un peu C++ iront long permet de rendre ce plus simple et plus fiable. La figure 5 fournit un wrapper d'objet erreur simple. Vous pouvez énumérer facilement les chaînes dans un objet d'erreur, comme illustré figure 6 .
La figure 5 Erreur objet 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;
}
};
La figure 6 énumérer des chaînes dans un objet Error
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);
}
La prochaine chose que nous aurons besoin est un objet de tas. L'objet de tas fournit un contrôle précis sur l'allocation de mémoire lors de la production ou de la consommation de messages et lors de l'avoir à allouer diverses autres structures d'API. Il simplifie également le modèle de programmation dans la mesure où il n'est pas nécessaire pour vous de connaître précisément la quantité de mémoire est requise pour une fonction particulière réussir. De nombreuses anciennes fonctions dans le Kit de développement logiciel (SDK) de Windows seront, par exemple, soit vous obliger à deviner combien stockage est nécessaire ou pour appeler au préalable une fonction d'une façon particulière pour déterminer la quantité de mémoire, vous devez allouer de la fonction réussisse. L'utilisation de l'objet de tas WWS supprime tout ce supplémentaire codage et offre un moyen pratique pour contrôler l'utilisation de la mémoire de l'API. Cela également est pratique d'un point de vue de la sécurité dans lequel vous souhaiterez peut-être spécifier des limites attendus sur combien l'API peut allouer. WS_HEAP 1 représente un handle à un objet de tas et WsCreateHeap est la fonction qui le crée. L'objet est libérée en appelant la fonction WsFreeHeap. Une fois qu'il est créé, vous pouvez allouer de la mémoire à partir du tas à l'aide de la fonction WsAlloc, mais pour cet article nous allons simplement passer le handle à l'objet de tas à d'autres fonctions d'API pour les utiliser si nécessaire.
La figure 7 fournit un wrapper d'objet de tas simple. Vous pouvez cette donnée, pour créer un objet de tas limité à 250 octets comme suit :
WsHeap heap;
HR(heap.Create(250, // max size
0, // trim size
0, // properties
0, // property count
error));
Notez comment je suis en passant l'objet d'erreur à la méthode Create de l'objet de tas. Doit rien se poser lors de la création de l'objet de tas, je pourrai interroger l'objet d'erreur pour déterminer la cause.
La figure 7 wrapper d'objet du tas
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;
}
};
Le client
Côté client du modèle de service de centre de l'objet de proxy de service. Le code source généré inclut une fonction appelée CalculatorBinding_CreateServiceProxy. Le nom est dérivé de celle du point de terminaison ou nom de la liaison définie dans le document WSDL. Cette fonction crée un objet de proxy de service et renvoie un WS_SERVICE_PROXY 1 représentant un handle opaque vers cet objet. L'objet est libérée en appelant la fonction WsFreeServiceProxy. Une fois créé, votre application peut ouvrir le point de terminaison de service à l'aide de la fonction WsOpenServiceProxy et ensuite effectuer des appels via le proxy de service au service Web. Ce que fait exactement WsOpenServiceProxy dépend de la couche de transport utilisé. Vous devez également veiller à fermer le proxy de service avant de libérer de l'objet à l'aide de la fonction WsCloseServiceProxy. Naturellement, tout ce nettoyage peut être joliment encapsulé dans une classe simple fournie dans de la figure 8. Cette donnée, vous pouvez créer et ouvrir le proxy de service, comme dans de la figure 9.
De la figure 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;
}
};
La figure 9 créer et ouvrir le proxy de service
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));
La structure WS_ENDPOINT_ADDRESS est utilisée pour adresser des messages envoyés. Vous utiliserez généralement cette structure lors de la programmation au niveau de la couche du canal. Dans ce cas, nous remplir uniquement la partie de l'URL et le proxy de service s'occupe du reste.
À ce stade, nous pouvez utiliser une autre fonction générée CalculatorBinding_Add à savoir, unsurprisingly représente opération d'ajout du service Web. Il n'est pas vraiment obtenir beaucoup plus simple que ceci :
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);
Une fois que vous avez terminé interagir avec le service Web vous souhaitez simplement fermer le canal de communication du proxy de service :
HR(serviceProxy.Close(0, // async context
error));
Le serveur
Alors que le modèle de programmation côté client postes de charge sur le proxy de service, le serveur au lieu de cela crée et gère un ordinateur hôte service qui fournit le runtime nécessaire pour écouter sur les différents points de terminaison selon les informations de canal fourni. Là encore, car nous sommes à l'aide du modèle de service, disparaît de la plupart des détails et nous sommes reste uniquement pour créer le point de terminaison de service et ordinateur hôte. WWS fera le reste.
La première étape consiste à créer le point de terminaison de service. Le modèle de service s'occupe de cela sous la forme d'une autre fonction générée, à savoir CalculatorBinding_CreateServiceEndpoint, comme le montre la de la figure 10. Création du point de terminaison nécessite de spécifier l'adresse sur laquelle le point de terminaison va écouter. Ceci est fourni par le WS_STRING structure, qui est une chaîne Unicode préfixée par sa longueur et n'est pas nécessaire pour être null s'est arrêté. Dans la mesure où le point de terminaison est responsable de l'exécution des requêtes, vous devez fournir un tableau de pointeurs de fonction pour les opérations exposées par le service de mappage. La structure de CalculatorBindingFunctionTable générée est utilisée à cet effet. Enfin, le point de terminaison est représenté par la structure WS_SERVICE_ENDPOINT et alloué dans le tas fourni.
Figure 10 de la 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));
L'étape suivante consiste à créer le service ordinateur hôte. WS_SERVICE_HOST 1 représente un handle à un objet d'ordinateur hôte de service et WsCreateServiceHost est la fonction qui le crée. L'objet est libérée en appelant la fonction WsFreeServiceHost. La fonction WsCreateServiceHost crée l'objet d'ordinateur hôte service reçoit une liste de points de terminaison. À ce stade, vous pouvez démarrer les écouteurs sur tous les points de terminaison à l'aide de la fonction WsOpenServiceHost. Arrêter la communication à l'aide de la fonction WsCloseServiceHost. Comme avec le proxy de service, veillez à fermer l'ordinateur hôte service prior to libérer. Là encore, tout ce nettoyage peut être joliment encapsulé dans une classe simple fournie dans de la figure 11. Vous pouvez cette donnée, pour démarrer le service Web comme suit :
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));
Dans ce cas, il est uniquement un point de terminaison unique mais vous voyez avec quelle facilité il serait pour ajouter des points de terminaison supplémentaires pour l'ordinateur hôte de service. Lorsqu'il est temps pour arrêter le service, il vous souhaitez de fermer des canaux de communication d'ordinateur hôte service.
HR(serviceHost.Close(0, // async context
error));
En fait mise en œuvre de l'opération de service Web est sur la partie la plus simple :
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;
}
La signature de la fonction AddCallback est également fournie par le code source généré, doivent avoir des doutes sur la façon de le spécifier.
Et c'est tout je dispose d'espace pour ce mois, mais vous devez maintenant avoir une bonne idée des fonctionnalités et avantages dont l'API de Services Web de Windows. Comme vous pouvez le voir, les développeurs C++ ont enfin une pile SOAP moderne droite de la zone. Il offre la meilleure possible de performances et d'utilisation de la mémoire et est un plaisir à utiliser avec un peu d'aide de C++.
La figure 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;
}
};
Qui est à l'aide de IT ?
Diverses équipes chez Microsoft ont déjà démarré l'adoption de l'API Windows Web Services (WWS) dans leurs propres produits ou technologies. Dans de nombreux cas, cela remplace piles SOAP personnalisées et certains choisi même remplacer les implémentations commerciales telles que Windows Communication Foundation (WCF) avec WWS. Voici quelques exemples.
Les Services Web de Microsoft sur périphériques (WSD) API permet aux développeurs d'écrire des clients et serveurs basés sur le profil de périphériques pour les Services Web (DPWS). Dans Windows 7, l'API WSD a démarré à l'aide de WWS pour la création et la canonisation XML dans les messages SOAP qui envoie le runtime WSD. Les plans d'équipe WSD pour développer leur utilisation de WWS comme ils ajoutent de nouvelles fonctionnalités et refactoriser du code existant.
Windows CardSpace est l'implémentation Microsoft d'un système d'identité basé sur les normes de service Web. Implémenté à l'origine avec WCF, il est en cours réécrite à l'aide de code natif et WWS pour répondre aux exigences d'entreprise très strictes sur la taille du programme d'installation téléchargeable et du runtime jeu de travail.
Microsoft Forefront Threat Management Gateway (TMG) est une plate-forme de sécurité fournissant des pare-feu et de fonctionnalités pour sécuriser et améliorer les performances des réseaux de mise en cache. Sa fonctionnalité de filtrage URL utilise WWS pour se connecter au service de réputation de Microsoft pour classer les URL.
Enfin, le client Windows Public Key Infrastructure (PKI, Public Key Infrastructure) offre une gestion de cycle de vie automatique des certificats avec l'inscription automatique, ainsi que l'inscription de certificat par utilisateur et d'application. Windows 7 introduit un nouvel ensemble de services Web permettant l'inscription de certificat se termine sans les limitations de traditional de LDAP et DCOM. Le client PKI utilise WWS pour toutes les opérations, y compris un nouveau protocole de stratégie d'inscription de certificat (MS-XCEP) et une extension de WS-Trust pour l'inscription de certificats (MS-WSTEP). Le client WWS communique avec un nouvel ensemble de services de certificats Active Directory dans Windows Server 2008 R2 qui sont implémentées avec WCF, ainsi qu'avec les services Web fournis par les émetteurs de certificats publics.
Kenny Kerr fait figure d’artisan informatique spécialisé dans le développement de logiciels pour Windows. Ce qu’il aime par dessus tout, c’est l’écriture et la formation des développeurs en matière de programmation et de conception des logiciels. Vous pouvez contacter Kerr à weblogs.asp.net/kennykerr .