Partager via


Contrats de service et WSDL

L’utilitaire Wsutil.exe génère un stub de langage C en fonction des métadonnées WSDL fournies, ainsi que des définitions et descriptions des types de données pour les types de données décrits par les schémas XML créés par l’utilisateur.

Voici un exemple de document WSDL et de schéma XML qui sert de base à la discussion suivante :

<?xml version="1.0" encoding="utf-8"?>
<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://Example.org" 
xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:xs="http://www.w3.org/2001/XMLSchema" 
xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl" targetNamespace="http://Example.org" 
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
 <wsdl:types>
  <xs:schema xmlns:tns="http://Example.org" elementFormDefault="qualified" 
  targetNamespace="http://Example.org" xmlns:xs="http://www.w3.org/2001/XMLSchema">
   <xs:element name="SimpleMethod">
    <xs:complexType>
     <xs:sequence>
      <xs:element name="a" type="xs:int" />
      <xs:element name="b" type="xs:int" />
     </xs:sequence>
    </xs:complexType>
   </xs:element>
   <xs:element name="SimpleMethodResponse">
    <xs:complexType>
     <xs:sequence>
      <xs:element name="b" type="xs:int" />
      <xs:element name="c" type="xs:int" />
     </xs:sequence>
    </xs:complexType>
   </xs:element>
  </xs:schema>
 </wsdl:types>
 <wsdl:message name="ISimpleService_SimpleMethod_InputMessage">
  <wsdl:part name="parameters" element="tns:SimpleMethod" />
 </wsdl:message>
 <wsdl:message name="ISimpleService_SimpleMethod_OutputMessage">
  <wsdl:part name="parameters" element="tns:SimpleMethodResponse" />
 </wsdl:message>
 <wsdl:portType name="ISimpleService">
  <wsdl:operation name="SimpleMethod">
   <wsdl:input wsaw:Action="http://Example.org/ISimpleService/SimpleMethod" 
   message="tns:ISimpleService_SimpleMethod_InputMessage" />
   <wsdl:output wsaw:Action="http://Example.org/ISimpleService/SimpleMethodResponse" 
   message="tns:ISimpleService_SimpleMethod_OutputMessage" />
  </wsdl:operation>
 </wsdl:portType>
 <wsdl:binding name="DefaultBinding_ISimpleService" type="tns:ISimpleService">
  <soap:binding transport="http://schemas.xmlsoap.org/soap/http" />
  <wsdl:operation name="SimpleMethod">
   <soap:operation soapAction="http://Example.org/ISimpleService/SimpleMethod" 
   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="SimpleService">
  <wsdl:port name="ISimpleService" binding="tns:DefaultBinding_ISimpleService">
   <soap:address location="http://Example.org/ISimpleService" />
  </wsdl:port>
 </wsdl:service>
</wsdl:definitions>

Cet exemple fournit un contrat, ISimpleService, avec une seule méthode, SimpleMethod. « SimpleMethod » a deux paramètres d’entrée de type entier, un et b, qui sont envoyés du client au service. De même, SimpleMethod a deux paramètres de sortie de type entier, b et c, qui sont retournés au client après la réussite de l’exécution. Dans la syntaxe C annotée SAL, la définition de méthode apparaît comme suit :

void SimpleMethod(__in int a, __inout int *  b, __out int * c );

Dans cette définition, ISimpleService est un contrat de service avec une seule opération de service : SimpleMethod.

Le fichier d’en-tête de sortie contient des définitions et des descriptions de référence externe. Cela inclut les éléments suivants :

  • Définitions de structure C pour les types d’éléments globaux.
  • Prototype d’opération tel que défini dans le fichier actif.
  • Prototype de table de fonctions pour les contrats spécifiés dans le fichier WSDL.
  • Prototypes de proxy client et de stub de service pour toutes les fonctions spécifiées dans le fichier actif.
  • Structure de données WS_ELEMENT_DESCRIPTION pour les éléments de schéma globaux définis dans le fichier actif.
  • Structure de données WS_MESSAGE_DESCRIPTION pour tous les messages spécifiés dans le fichier actif.
  • Structure de données WS_CONTRACT_DESCRIPTION pour tous les contrats spécifiés dans le fichier actif.

Une structure globale est générée pour encapsuler toutes les descriptions globales pour les types de schémas et les types de modèle de service auxquels l’application peut faire référence. La structure est nommée à l’aide d’un nom de fichier normalisé. Dans cet exemple, Wsutil.exe génère une structure de définitions globales nommée « example_wsdl » qui contient toutes les descriptions du service web. La définition de structure est générée dans le fichier stub.

typedef struct _example_wsdl
{
  struct {
    WS_ELEMENT_DESCRIPTION SimpleMethod;
    WS_ELEMENT_DESCRIPTION SimpleMethodResponse;
  } elements;
  struct {
    WS_MESSAGE_DESCRIPTION ISimpleService_SimpleMethod_InputMessage;
    WS_MESSAGE_DESCRIPTION ISimpleService_SimpleMethod_OutputMessage;
  } messages;
  struct {
    WS_CONTRACT_DESCRIPTION DefaultBinding_ISimpleService;
  } contracts;
} _example_wsdl;

extern const _stockquote_wsdl stockquote_wsdl;

Pour les définitions d’éléments globales dans le document de schéma XML (XSD), un prototype WS_ELEMENT_DESCRIPTION, ainsi que la définition de type C correspondante, sont générées pour chacun des éléments. Les prototypes des descriptions d’éléments pour SimpleMethod et SimpleMethodResponse sont générés en tant que membres dans la structure ci-dessus. Les structures C sont générées comme suit :

typedef struct SimpleMethod
{
  int   a;
  int   b;
} SimpleMethod;

typedef struct SimpleMethodResponse
{
  int   b;
  int   c;
} SimpleMethodResponse;

De même, pour les types complexes globaux, Wsutil.exe génère des définitions de structure de type C comme ci-dessus, sans descriptions d’éléments correspondantes.

Pour l’entrée WSDL, Wsutil.exe génère les prototypes et définitions suivants :

  • Un prototype WS_MESSAGE_DESCRIPTION est généré pour la description du message. Cette description peut être utilisée par le modèle de service ainsi que par la couche de messages. Les structures de description de message sont des champs nommés « messagename » dans la structure globale. Dans cet exemple, la description du message est générée en tant que champ ISimpleService_SimpleMethod_InputMessage dans la structure ISimpleService_SimpleMethod_InputMessage, comme spécifié dans le fichier WSDL.
  • WS_CONTRACT_DESCRIPTION prototype est généré pour la description du contrat. Cette description est utilisée par le modèle de service. Les structures de description de contrat sont des champs nommés « contractname » dans la structure globale. Dans cet exemple, la description du contrat est générée en tant que champ DefaultBinding_ISimpleService dans la structure « _example_wsdl ».

Les spécifications d’opération et de type sont communes au proxy et au stub et sont générées dans les deux fichiers. Wsutil.exe génère une copie uniquement si le proxy et le stub sont générés dans le même fichier.

Génération d’identificateur

Les structures C générées automatiquement répertoriées ci-dessus sont créées en fonction du nom spécifié dans le fichier WSDL. Un nom NC XML n’est généralement pas considéré comme un identificateur C valide et les noms sont normalisés selon les besoins. Les valeurs hexadécimal ne sont pas converties, et les lettres courantes telles que « : », « / » et « . » sont converties en trait de soulignement « _ » pour améliorer la lisibilité.

En-tête du stub

Pour chaque opération du contrat de service, une routine de rappel nommée «<nom_opération>callback » est générée. (Par exemple, l’opération « SimpleMethod » dans l’exemple de contrat de service a un rappel généré nommé « SimpleMethodCallback ».

typedef HRESULT (CALLBACK *SimpleMethodCallback) (
  const WS_OPERATION_CONTEXT * context,
  int a, int *b, int *c,
  const WS_ASYNC_CONTEXT *asyncContext,
  WS_ERROR * error);

Pour chaque portType WSDL Wsutil.exe génère une table de fonctions représentant portType. Chaque opération sur le portType a un pointeur de fonction correspondant vers le rappel présent dans la table de fonctions.

struct ISimpleServiceMethodTable
{
  ISimpleService_SimpleMethodCallback SimpleMethod;
};

Les prototypes de proxy sont générés pour toutes les opérations. Le nom du prototype est le nom de l’opération (dans ce cas, « SimpleMethod ») spécifié dans le fichier WSDL pour le contrat de service.

HRESULT WINAPI SimpleMethod(WS_CHANNEL *channel,
  WS_HEAP *heap,
  int a,
  int *b,
  int *c,
  const WS_ASYNC_CONTEXT * asyncContext,
  WS_ERROR * error );

Génération de prototype de description locale uniquement

Les fichiers proxy etstub contiennent la définition de la structure des définitions globales, y compris les prototypes et les définitions des structures contenant des descriptions locales uniquement et des implémentations de stubs de proxy/de service client.

Tous les prototypes et définitions qui sont locaux dans le fichier stub sont générés dans le cadre d’une structure encapsulante. Cette structure de description locale globale fournit une hiérarchie claire des descriptions requises par la couche de sérialisation et le modèle de service. La structure de description locale comporte des prototypes similaires à ceux présentés ci-dessous :

struct _filenameLocalDefinitions
{
  struct {
  // schema related output for all the embedded 
  // descriptions that needs to describe global complex types.
  } globalTypes;
  // global elements.
  struct {
  // schema related output, like field description
  // structure description, element description etc.
  ...
  } globalElements;
  struct {
  // messages and related descriptions
  } messages;
  struct {
  // contract and related descriptions.
  } contracts;
  struct {
  // XML dictionary entries.
  } dictionary;
} _filenameLocalDefinitions;

Référencement des définitions à partir d’autres fichiers

Les définitions locales peuvent référencer des descriptions générées dans un autre fichier. Par exemple, le message peut être défini dans le fichier de code C généré à partir du fichier WSDL, mais l’élément de message peut être défini ailleurs dans le fichier de code C généré à partir du fichier XSD. Dans ce cas, Wsutil.exe génère une référence à l’élément global à partir du fichier qui contient la définition du message, comme illustré ci-dessous :

{  // WS_MESSAGE_DESCRIPTION
...
(WS_ELEMENT_DESRIPTION *)b_xsd.globalElement.<elementname>
  };

Descriptions d’éléments globaux

Pour chaque élément global défini dans un fichier wsdl :type ou XSD, il existe un champ correspondant nommé elementName à l’intérieur du champ GlobalElement. Dans cet exemple, une structure nommée SimpleMethod est générée :

typedef struct _SimpleServiceLocal
{
  struct  // global elements
  {
    struct // SimpleMethod
    {
    ...
    WS_ELEMENT_DESCRIPTION SimpleMethod;
    } SimpleMethod;
    ...
  } globalElements;
}

D’autres descriptions requises par la description de l’élément sont générées dans le cadre de la structure conteneur. Si l’élément est un élément de type simple, il n’y a qu’un seul champ WS_ELEMENT_DESCRIPTION. Si le type d’élément est une structure, tous les champs et descriptions de structure associés sont générés dans le cadre de la structure d’élément. Dans cet exemple, l’élément SimpleMethod est une structure contenant deux champs, un et b. Wsutil.exe génère la structure comme suit :

...
struct // SimpleMethod
{
  struct // SimpleMethod structure
  {
    WS_FIELD_DESCRIPTION a;
    WS_FIELD_DESCRIPTION b;
    WS_FIELD_DESCRIPTION * SimpleMethodFields [2];
    WS_STRUCT_DESCRIPTION structDesc;
  } SimpleMethoddescs; // SimpleMethod
  WS_ELEMENT_DESCRIPTION elementDesc;
} SimpleMethod;
...

Les structures incorporées et les éléments incorporés sont générés en tant que sous-structures si nécessaire.

Wsutil.exe génère un champ sous la section WSDL pour chacune des valeurs portType définies sous le wsdl :service spécifié.

...
struct { // WSDL
    struct { // portTypeName
        struct { // operationName
        } operationName;
    ...
    WS_OPERATION_DESCRIPTION* operations[numOperations];
    WS_CONTRACT_DESCRIPTION contractDesc;
    } portTypeName;
}
...

Wsutil.exe génère un champ f qui contient toutes les descriptions nécessaires à l’opération, un tableau de pointeurs vers chacune des descriptions des opérations pour chaque méthode, et un WS_CONTRACT_DESCRIPTION pour le portType spécifié.

Toutes les descriptions requises par les opérations sont générées à l’intérieur du champ operationName sous le portTypespécifié. Il s’agit notamment du champ WS_ELEMENT_DESCRIPTION ainsi que de la sous-structure des paramètres d’entrée et de sortie. De même, les champs WS_MESSAGE_DESCRIPTION pour le message d’entrée et le message de sortie facultatif sont inclus avec le ; WS_PARAMETER_DESCRIPTION champ de liste pour tous les paramètres de l’opération et le champ WS_OPERATION_DESCRIPTION pour l’opération elle-même. Dans cet exemple, la structure de code de la description SimpleMethod est générée comme indiqué ci-dessous :

...
struct // messages
{
  WS_MESSAGE_DESCRIPTION ISimpleService_SimpleMethod_InputMessage;
  WS_MESSAGE_DESCRIPTION ISimpleService_SimpleMethod_OutputMessage;
} messages;
struct // contracts
{
  struct // DefaultBinding_ISimpleService
  {
    struct // SimpleMethod
    {
      WS_PARAMETER_DESCRIPTION params[3];
      WS_OPERATION_DESCRIPTION SimpleMethod;
    } SimpleMethod;
    WS_OPERATION_DESCRIPTION* operations[1];
    WS_CONTRACT_DESCRIPTION contractDesc;
  } DefaultBinding_ISimpleService;
} contracts;
...

Les noms et espaces de noms utilisés dans différentes descriptions sont générés en tant que champs de type WS_XML_STRING. Toutes ces chaînes sont générées dans le cadre d’un dictionnaire constant par fichier. La liste des chaînes et du champ WS_XML_DICTIONARY (nommé dict dans l’exemple ci-dessous) sont générées dans le cadre du champ dictionnaire de la structure fileNameLocal.

struct { // fileNameLocal
...
  struct { // dictionary
    struct { // XML string list
      WS_XML_STRING firstFieldName;
      WS_XML_STRING firstFieldNS;
      ...
    } xmlStrings;
  WS_XML_DICTIONARY dict;
  } dictionary;
}; // fileNameLocal;

Le tableau de WS_XML_STRINGs est généré sous la forme d’une série de champs de type WS_XML_STRING, nommés avec des noms conviviaux. Le stub généré utilise les noms conviviaux dans différentes descriptions pour améliorer la lisibilité.

Proxy client pour les opérations WSDL

Wsutil.exe génère un proxy client pour toutes les opérations. Les applications peuvent remplacer la signature de méthode à l’aide d’une option de ligne de commande de préfixe.

HRESULT WINAPI bindingName_SimpleMethod(WS_SERVICE_PROXY *serviceProxy,
  WS_HEAP *heap,
  int a,
  int *b,
  int *c,
  const WS_CALL_PROPERTY* callProperties,
  ULONG callPropertyCount,
  const WS_ASYNC_CONTEXT * asyncContext,
  WS_ERROR * error )
{
  void* argList[] = {&a, &b, &c};
  return WsCall(_serviceProxy,
    (WS_OPERATION_DESCRIPTION*)&example_wsdlLocalDefinitions.contracts.DefaultBinding_ISimpleService.SimpleMethod.SimpleMethod,
    (void **)&_argList,
    callProperties,
    callPropertyCount,
    heap,
    asyncContext,
    error
  );      
}

L’appelant d’opération doit passer un paramètre tas valide. Les paramètres de sortie sont alloués à l’aide de la valeur WS_HEAP spécifiée dans le paramètre tas. La fonction appelante peut réinitialiser ou libérer le tas pour libérer de la mémoire pour tous les paramètres de sortie. Si l’opération échoue, des informations d’erreur détaillées supplémentaires peuvent être récupérées à partir de l’objet d’erreur facultatif s’il est disponible.

Wsutil.exe génère un stub de service pour toutes les opérations décrites dans la liaison.

HRESULT CALLBACK ISimpleService_SimpleMethodStub(
  const WS_OPERATION_CONTEXT *context,
  void * stackStruct,
  void * callback,
  const WS_ASYNC_CONTEXT * asyncContext,
  WS_ERROR *error )
{
  SimpleMethodParamStruct *pstack = (SimpleMethodParamStruct *) stackstruct;
  SimpleMethodOperation operation = (SimpleMethodOperation)callback;
  return operation(context, pstack->a, &(pstack->b), &(pstack->c ), asyncContext, error );
}

La section ci-dessus décrit le prototype de la structure locale contenant toutes les définitions locales au fichier stub uniquement. Les sections suivantes décrivent les définitions des descriptions.

Génération de définition WSDL

Wsutil.exe génère une structure statique constante (const static) nommée *<file_name>*LocalDefinitions de type *<service_name>*Local qui contient toutes les définitions locales uniquement.

const static _SimpleServiceLocal example_wsdlLocalDefinitions =
{
  {  // global types
  ...
  }, // global types
  {  // global elements
  ...
  }, // global elements
  {  // messages
  ...
  }, //messages
  ...
  {  // dictionary
  ...
  }, // dictionary
},

Les descriptions WSDL suivantes sont prises en charge :

  • wsdl :service
  • wsdl :binding
  • wsdl :portType
  • wsdl :operation
  • wsdl :message

Traitement de wsdl :operation et wsdl :message

Chaque opération spécifiée dans le document WSDL est mappée à une opération de service par Wsutil.exe. L’outil génère des définitions distinctes des opérations de service pour le serveur et le client.

<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://Example.org" 
xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:xs="http://www.w3.org/2001/XMLSchema" 
xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl" targetNamespace="http://Example.org" 
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
 <wsdl:portType name="ISimpleService">
  <wsdl:operation name="SimpleMethod">
   <wsdl:input wsaw:Action="http://Example.org/ISimpleService/SimpleMethod" 
   message="tns:ISimpleService_SimpleMethod_InputMessage" />
   <wsdl:output wsaw:Action="http://Example.org/ISimpleService/SimpleMethodResponse" 
   message="tns:ISimpleService_SimpleMethod_OutputMessage" />
  </wsdl:operation>
 </wsdl:portType>
 <wsdl:message name="ISimpleService_SimpleMethod_InputMessage">
  <wsdl:part name="parameters" element="tns:SimpleMethod" />
 </wsdl:message>
 <wsdl:message name="ISimpleService_SimpleMethod_OutputMessage">
  <wsdl:part name="parameters" element="tns:SimpleMethodResponse" />
 </wsdl:message>
</wsdl:definitions>

La disposition des éléments de données de message d’entrée et d’expiration est évaluée par l’outil pour générer les métadonnées de sérialisation de l’infrastructure, ainsi que la signature réelle de l’opération de service résultante à laquelle les messages d’entrée et de sortie sont associés.

Les métadonnées de chaque opération d’un portType spécifique ont une entrée et éventuellement un message de sortie, chacun de ces messages est mappé à un WS_MESSAGE_DESCRIPTION. Dans cet exemple, l’entrée et le message de sortie sur l’opération dans le portType mappé à inputMessageDescription et éventuellement le outputMessageDescription sur le WS_OPERATION_DESCRIPTION respectivement.

Pour chaque message WSDL, l’outil génère WS_MESSAGE_DESCRIPTION qui fait référence à la définition WS_ELEMENT_DESCRIPTION, comme illustré ci-dessous :

... 
{    // message description for ISimpleService_SimpleMethod_InputMessage
  (WS_XML_STRING*)&example_wsdlLocalDefinitions.dictionary.xmlStrings.DefaultBinding_ISimpleServiceISimpleService_SimpleMethod_InputMessageactionName,
  (WS_ELEMENT_DESCRIPTION*)&(WS_ELEMENT_DESCRIPTION*)&example_wsdl.globalElements.SimpleMethodReponse
},  // message description for ISimpleService_SimpleMethod_InputMessage
...

La description du message fait référence à la description de l’élément d’entrée. Étant donné que l’élément est défini globalement, la description du message fait référence à la définition globale au lieu de l’élément statique local. De même, si l’élément est défini dans un autre fichier, Wsutil.exe génère une référence à la structure globale définie dans ce fichier. Par exemple, si SimpleMethodResponse est défini dans un autre fichier example.xsd, Wsutil.exe génère à la place les éléments suivants :

...
{    // message description for ISimpleService_SimpleMethod_InputMessage
(WS_XML_STRING*)&example_wsdlLocalDefinitions.dictionary.xmlStrings.DefaultBinding_ISimpleServiceISimpleService_SimpleMethod_InputMessageactionName,
(WS_ELEMENT_DESCRIPTION*)&(WS_ELEMENT_DESCRIPTION*)&example_xsd.globalElements.SimpleMethodReponse
},  // message description for ISimpleService_SimpleMethod_InputMessage
...

Chaque description du message contient l’action et la description d’élément spécifique (champ de type WS_ELEMENT_DESCRIPTION) pour tous les éléments de données de message. Dans le cas d’un message de style RPC ou d’un message avec plusieurs parties, un élément wrapper est créé pour encapsuler les informations supplémentaires.

Prise en charge du style RPC

Wsutil.exe prend en charge le style de document ainsi que les opérations de style RPC en fonction de la spécification WSDL 1.1 Binding Extension for SOAP 1.2. Les opérations de style RPC et littéral sont marquées comme WS_RPC_LITERAL_OPERATION. Le modèle de service ignore le nom de l’élément wrapper du corps de la réponse dans les opérations RPC/littérales.

Wsutil.exe ne prend pas en charge les opérations de style encodage en mode natif. Le paramètre WS_XML_BUFFER est généré pour l’encodage des messages, et les développeurs doivent remplir directement la mémoire tampon opaque.

Prise en charge de plusieurs parties de message

Wsutil.exe prend en charge plusieurs parties de message dans un message. Un message en plusieurs parties peut être spécifié comme suit :

<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://Example.org" 
xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:xs="http://www.w3.org/2001/XMLSchema" 
xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl" targetNamespace="http://Example.org" 
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
 <wsdl:message name="ISimpleService_MutipleParts_InputMessage">
  <wsdl:part name="part1" element="tns:SimpleElement1" />
  <wsdl:part name="part2" element="tns:SimpleElement2" />
 </wsdl:message>
</wsdl:definitions>

Wsutil.exe génère un champ WS_STRUCT_TYPE pour l’élément de message si le message contient plusieurs parties. Si le message est représenté à l’aide du style de document, Wsutil.exe génère un élément wrapper avec un type de struct. L’élément wrapper n’a pas de nom ni d’espace de noms spécifique, et la structure wrapper contient tous les éléments de toutes les parties sous forme de champs. L’élément wrapper est destiné uniquement à une utilisation interne et ne sera pas sérialisé dans le corps du message.

Si le message utilise la représentation de style RPC ou littéral, Wsutil.exe crée un élément wrapper avec le nom de l’opération comme nom d’élément et l’espace de noms spécifié en tant qu’espace de noms de service en fonction de la spécification de l’extension SOAP WSDL. La structure de l’élément contient un tableau de champs représentant les types spécifiés dans les parties de message. L’élément wrapper est mappé à l’élément supérieur réel dans le corps du message, comme indiqué dans la spécification SOAP.

Côté serveur, chaque opération entraîne un typedef de l’opération de service serveur résultante. Ce typedef est utilisé pour faire référence à l’opération dans la table de fonctions, comme décrit précédemment. Chaque opération entraîne également la génération d’une fonction stub qui nfrastructure appelle au nom du délégué à la méthode réelle.

typedef HRESULT (CALLBACK *SimpleMethodCallback) (
  const WS_OPERATION_CONTEXT* context,
  unsigned int  a,
  unsigned int * b,
  unsigned int * c,
  const WS_ASYNC_CONTEXT* asyncContext,
  WS_ERROR* error
  );

Pour l’opération SimpleMethod, le typedef SimpleMethodOperation est défini ci-dessus. Notez que la méthode générée a une liste d’arguments développée avec la partie de message pour le message d’entrée et de sortie pour l’opération SimpleMethod en tant que paramètres nommés.

Côté client, chaque opération est mappée à une opération de service proxy.

HRESULT WINAPI SimpleMethod (
  WS_SERVICE_PROXY* serviceProxy,
  ws_heap *heap,
  unsigned int  a,
  unsigned int * b,
  unsigned int * c,
  const WS_ASYNC_CONTEXT* asyncContext,
  WS_ERROR* error);

Traitement wsdl :binding

Le modèle de service WWSAPI prend en charge l’extension de liaisonSOAP. Pour chaque liaison, il existe un portType associé.

Le transport spécifié dans l’extension de liaison soap est consultatif uniquement. L’application doit fournir des informations de transport lors de la création d’un canal. Actuellement, nous prenons en charge les liaisons WS_HTTP_BINDING et WS_TCP_BINDING.

<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://Example.org" 
xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:xs="http://www.w3.org/2001/XMLSchema" 
xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl" targetNamespace="http://Example.org" 
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
 <wsdl:binding name="DefaultBinding_ISimpleService" type="tns:ISimpleService">
  <soap:binding transport="http://schemas.xmlsoap.org/soap/http" />
  <wsdl:operation name="SimpleMethod">
   <soap:operation soapAction="http://Example.org/ISimpleService/SimpleMethod" 
   style="document" />
   <wsdl:input>
    <soap:body use="literal" />
   </wsdl:input>
   <wsdl:output>
    <soap:body use="literal" />
   </wsdl:output>
  </wsdl:operation>
 </wsdl:binding>
</wsdl:definitions>

Dans notre exemple de document WSDL, nous n’avons qu’un seul portType pour ISimpleService. La liaison SOAP fournie indique le transport HTTP, qui est spécifié en tant que WS_HTTP_BINDING. Notez que cette structure n’a pas de décoration statique, car cette structure doit être disponible pour l’application.

Traitement wsdl :portType

Chaque portType dans WSDL est constitué d’une ou plusieurs opérations. L’opération doit être cohérente avec l’extension de liaison SOAP indiquée dans wsdl :binding.

<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://Example.org" 
xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:xs="http://www.w3.org/2001/XMLSchema" 
xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl" targetNamespace="http://Example.org" 
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
 <wsdl:portType name="ISimpleService">
  <wsdl:operation name="SimpleMethod">
   ...
  </wsdl:operation>
 </wsdl:portType>
</wsdl:definitions>

Dans cet exemple, iSimpleService portType contient uniquement l’opération SimpleMethod. Cela est cohérent avec la section de liaison où il n’existe qu’une seule opération WSDL qui est mappée à une action SOAP.

Étant donné que iSimpleService portType n’a qu’une seule opération -- SimpleMethod -- la table de fonctions correspondante contient uniquement SimpleMethod en tant qu’opération de service.

En termes de métadonnées, chaque portType est mappé par Wsutil.exe à un WS_CONTRACT_DESCRIPTION. Chaque opération dans un portType est mappée à un WS_OPERATION_DESCRIPTION.

Dans cet exemple, portType l’outil génère WS_CONTRACT_DESCRIPTION pour ISimpleService. Cette description de contrat contient le nombre spécifique d’opérations disponibles sur iSimpleService portType ainsi qu’un tableau de WS_OPERATION_DESCRIPTION représentant les opérations individuelles définies sur le portType pour ISimpleService. Étant donné qu’il n’existe qu’une seule opération sur le portType ISimpleService pour ISimpleService, il n’existe qu’une seule définition WS_OPERATION_DESCRIPTION.

...  part of LocalDefinitions structure
{    // array of operations for DefaultBinding_ISimpleService
(WS_OPERATION_DESCRIPTION*)&example_wsdlLocalDefinitions.contracts.DefaultBinding_ISimpleService.SimpleMethod.SimpleMethod,
},    // array of operations for DefaultBinding_ISimpleService
{    // contract description for DefaultBinding_ISimpleService
1,
(WS_OPERATION_DESCRIPTION**)example_wsdlLocalDefinitions.contracts.DefaultBinding_ISimpleService.operations,
WS_HTTP_CHANNEL_BINDING,
},  // end of contract description for DefaultBinding_ISimpleService
},    // DefaultBinding_ISimpleService       ...

Traitement wsdl :service

WsUtil.exe utilise les services pour rechercher des liaisons/porttypes et génère une structure de contrat qui décrit les types, les messages, les définitions de porttype, et ainsi de suite. Les descriptions de contrat sont accessibles en externe et sont générées dans le cadre de la structure de définition globale spécifiée par le biais de l’en-tête généré.

WsUtil.exe prend en charge les extensions EndpointReference définies dans wsdl :port. La référence de point de terminaison est définie dans WS-ADDRESSING comme moyen de décrire point de terminaison informations d’un service. Le texte de l’extension de référence de point de terminaison d’entrée enregistré en tant que WS_XML_STRING, ainsi que les WS_ENDPOINT_ADDRESS_DESCRIPTION correspondants sont générés dans la section endpointReferences de la structure globale.

<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://Example.org" 
xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:xs="http://www.w3.org/2001/XMLSchema" 
xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl" targetNamespace="http://Example.org" 
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
 <wsdl:service name="SimpleService">
  <wsdl:port name="ISimpleService" binding="tns:DefaultBinding_ISimpleService">
   <soap:address location="http://Example.org/ISimpleService" />
   <wsa:EndpointReference>
    <wsa:Address>http://example.org/wcfmetadata/WSHttpNon</wsa:Address>
   </wsa:EndpointReference> 
  </wsdl:port>
 </wsdl:service>
</wsdl:definitions>
  const _example_wsdl example_wsdl =
  {
  ... // global element description
  {// messages
  {    // message description for ISimpleService_SimpleMethod_InputMessage
  (WS_XML_STRING*)&example_wsdlLocalDefinitions.dictionary.xmlStrings.ISimpleService_SimpleMethod_InputMessageactionName,
  (WS_ELEMENT_DESCRIPTION*)&example_wsdl.globalElements.SimpleMethod,
},    // message description for ISimpleService_SimpleMethod_InputMessage
{    // message description for ISimpleService_SimpleMethod_OutputMessage
(WS_XML_STRING*)&example_wsdlLocalDefinitions.dictionary.xmlStrings.ISimpleService_SimpleMethod_OutputMessageactionName,
  (WS_ELEMENT_DESCRIPTION*)&example_wsdl.globalElements.SimpleMethodResponse,
},    // message description for ISimpleService_SimpleMethod_OutputMessage
}, // messages
{// contracts
{   // DefaultBinding_ISimpleService
1,
(WS_OPERATION_DESCRIPTION**)example_wsdlLocalDefinitions.contracts.DefaultBinding_ISimpleService.operations,
WS_HTTP_CHANNEL_BINDING,
},    // end of DefaultBinding_ISimpleService
    }, // contracts
    {
        {
            {   // endpointAddressDescription
                WS_ADDRESSING_VERSION_0_9,
            },                    
            (WS_XML_STRING*)&xml_string_generated_in_stub // endpointReferenceString
        }, //DefaultBinding_ISimpleService
    }, // endpointReferences
}

Pour créer WS_ENDPOINT_ADDRESS à l’aide des métadonnées générées par WsUtil :

WsCreateReader      // Create a WS_XML_READER
Initialize a WS_XML_READER_BUFFER_INPUT
WsSetInput          // Set the encoding and input of the reader to generate endpointReferenceString
WsReadType        // Read WS_ENDPOINT_ADDRESS from the reader
    // Using WS_ELEMENT_TYPE_MAPPING, WS_ENDPOINT_ADDRESS_TYPE and generated endpointAddressDescription, 

Les chaînes constantes dans le proxy client ou le stub de service sont générées en tant que champs de type WS_XML_STRING, et il existe un dictionnaire constant pour toutes les chaînes du fichier proxy ou stub. Chaque chaîne du dictionnaire est générée en tant que champ dans la partie dictionnaire de la structure locale pour une meilleure lisibilité.

... // dictionary part of LocalDefinitions structure
{    // xmlStrings
  { // xmlStrings
    WS_XML_STRING_DICTIONARY_VALUE("a",&example_wsdlLocalDefinitions.dictionary.dict, 0), 
    WS_XML_STRING_DICTIONARY_VALUE("http://Sapphire.org",&example_wsdlLocalDefinitions.dictionary.dict, 1), 
    WS_XML_STRING_DICTIONARY_VALUE("b",&example_wsdlLocalDefinitions.dictionary.dict, 2), 
    WS_XML_STRING_DICTIONARY_VALUE("SimpleMethod",&example_wsdlLocalDefinitions.dictionary.dict, 3),
    ...
  },  // end of xmlStrings
  {   // SimpleServicedictionary
    // 45026280-d5dc-4570-8195-4d66d13bfa34
    { 0x45026280, 0xd5dc, 0x4570, { 0x81, 0x95, 0x4d,0x66, 0xd1, 0x3b, 0xfa, 0x34 } },
    (WS_XML_STRING*)&example_wsdlLocalDefinitions.dictionary.xmlStrings,
    stringCount,
    TRUE,
  },
}
...

Traitement wsdl :type

Wsutil.exe prend uniquement en charge les documents de schéma XML (XSD) dans la spécification wsdl :type. Un cas particulier est lorsqu’un port de message spécifie une définition d’élément globale. Pour plus d’informations sur les heuristiques utilisées dans ces cas, consultez la section suivante.

Heuristique de traitement des paramètres

Dans le modèle de service, les messages WSDL sont mappés à des paramètres spécifiques dans une méthode. Wsutil.exe a deux styles de génération de paramètres : dans le premier style, l’opération a un paramètre pour le message d’entrée et un paramètre pour le message de sortie (si nécessaire) ; dans le deuxième style, Wsutil.exe utilise une heuristique pour mapper et développer des champs dans les structures pour les messages d’entrée et les messages de sortie vers différents paramètres de l’opération. Les messages d’entrée et de sortie doivent avoir des éléments de message de type structure pour générer cette deuxième approche.

Wsutil.exe utilise les règles suivantes lors de la génération de paramètres d’opération à partir des messages d’entrée et de sortie :

  • Pour les messages d’entrée et de sortie avec plusieurs parties de message, chaque partie de message est un paramètre distinct dans l’opération, avec le nom du composant de message comme nom de paramètre.
  • Pour le message de style RPC avec une partie de message, le composant de message est un paramètre dans l’opération, avec le nom du composant message comme nom de paramètre.
  • Pour les messages d’entrée et de sortie de style document avec une partie de message :
    • Si le nom d’une partie de message est « parameters » et que le type d’élément est une structure, chaque champ de la structure est traité comme un paramètre distinct dont le nom de champ est le nom du paramètre.
    • Si le nom de la partie de message n’est pas « parameters », le message est un paramètre dans l’opération avec le nom du message utilisé comme nom de paramètre correspondant.
  • Pour les messages d’entrée et de sortie de style de document avec un élément nillable, le message est mappé à un paramètre, avec le nom du composant de message comme nom de paramètre. Un niveau supplémentaire d’indirection est ajouté pour indiquer que le pointeur peut être NULL.
  • Si un champ apparaît uniquement dans l’élément de message d’entrée, le champ est traité comme un paramètre [in].
  • Si un champ apparaît uniquement dans l’élément de message de sortie, le champ est traité comme un paramètre [out].
  • S’il existe un champ portant le même nom et le même type qui apparaît à la fois dans le message d’entrée et le message de sortie, le champ est traité comme un paramètre [in,out].

Les outils suivants sont utilisés pour déterminer la direction des paramètres :

  • Si un champ apparaît uniquement dans l’élément de message d’entrée, le champ est traité comme étant uniquement dans le paramètre.
  • Si un champ s’affiche uniquement dans l’élément de message de sortie, le champ est traité comme un paramètre out uniquement.
  • S’il existe un champ portant le même nom et le même type qui apparaît dans le message d’entrée et le message de sortie, le champ est traité comme un paramètre in,out.

Wsutil.exe prend uniquement en charge les éléments séquencés. Il rejette l’ordre non valide en ce qui concerne les paramètres [in,out] si Wsutil.exe ne peut pas combiner les paramètres dans les paramètres et les paramètres sortants dans une seule liste de paramètres. Les suffixes peuvent être ajoutés aux noms de paramètres pour éviter la collision de noms.

<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://Example.org" 
xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:xs="http://www.w3.org/2001/XMLSchema" 
xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl" targetNamespace="http://Example.org" 
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
 <wsdl:message name="ISimpleService_SimpleMethod_InputMessage">
  <wsdl:part name="parameters" element="tns:SimpleMethod" />
 </wsdl:message>
 <wsdl:message name="ISimpleService_SimpleMethod_OutputMessage">
  <wsdl:part name="parameters" element="tns:SimpleMethodResponse" />
 </wsdl:message>
</wsdl:definitions>

Wsutil.exe considère les champs dans tns :SimpleMethod et tns :SimpleMethodResponse ato être des paramètres, comme indiqué dans les définitions de paramètres ci-dessous :

<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://Example.org" 
xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:xs="http://www.w3.org/2001/XMLSchema" 
xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl" targetNamespace="http://Example.org" 
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
 <wsdl:types>
  <xs:schema xmlns:tns="http://Example.org" elementFormDefault="qualified" 
  targetNamespace="http://Example.org" xmlns:xs="http://www.w3.org/2001/XMLSchema">
   <xs:import namespace="http://Example.org" />
   <xs:element name="SimpleMethod">
    <xs:complexType>
     <xs:sequence>
      <xs:element name="a" type="xs:unsignedInt" />
      <xs:element name="b" type="xs:unsignedInt" />
     </xs:sequence>
    </xs:complexType>
   </xs:element>
   <xs:element name="SimpleMethodResponse">
    <xs:complexType>
     <xs:sequence>
      <xs:element name="b" type="xs:unsignedInt" />
      <xs:element name="c" type="xs:unsignedInt" />
     </xs:sequence>
    </xs:complexType>
   </xs:element>
  </xs:schema>
 </wsdl:types>
</wsdl:definitions>

Wsutil.exe développe la liste des paramètres à partir des champs de la liste ci-dessus et génère la structure ParamStruct dans l’exemple de code suivant. L’exécution du modèle de service peut utiliser cette structure pour passer des arguments aux stubs client et serveur.

typedef struct SimpleMethodParamStruct {
  unsigned int   a;  
  unsigned int   b;
  unsigned int   c;
} ;

Cette structure est utilisée uniquement pour décrire le cadre de pile côté client et serveur. Il n’y a aucune modification apportée à la description du message ou aux descriptions d’éléments référencées par la description du message.

  // following are local definitions for the complex type
  { // field description for a
  WS_ELEMENT_FIELD_MAPPING,
  (WS_XML_STRING*)&example_wsdlLocalDefinitions.dictionary.xmlStrings.aLocalName,
(WS_XML_STRING*)&example_wsdlLocalDefinitions.dictionary.xmlStrings.aNamespace,
  WS_INT32_TYPE,
  0,
  WsOffsetOf(_SimpleMethod, a),
  0,
  0,
  },    // end of field description for a
  { // field description for b
  WS_ELEMENT_FIELD_MAPPING,
  (WS_XML_STRING*)&example_wsdlLocalDefinitions.dictionary.xmlStrings.bLocalName,
(WS_XML_STRING*)&example_wsdlLocalDefinitions.dictionary.xmlStrings.aNamespace,
  WS_INT32_TYPE,
  0,
  WsOffsetOf(_SimpleMethod, b),
  0,
  0,
  },    // end of field description for b
  {    // fields description for _SimpleMethod
  (WS_FIELD_DESCRIPTION *)&example_wsdlLocalDefinitions.globalElements.SimpleMethod._SimpleMethoddescs.a,
  (WS_FIELD_DESCRIPTION *)&example_wsdlLocalDefinitions.globalElements.SimpleMethod._SimpleMethoddescs.b,
  },
  {  // structure definition
  sizeof(_SimpleMethod),
  __alignof(_SimpleMethod),
  (WS_FIELD_DESCRIPTION**)&example_wsdlLocalDefinitions.globalElements.SimpleMethod._SimpleMethoddescs._SimpleMethodFields,
  WsCountOf(example_wsdlLocalDefinitions.globalElements.SimpleMethod._SimpleMethoddescs._SimpleMethodFields),
  (WS_XML_STRING*)&example_wsdlLocalDefinitions.dictionary.xmlStrings._SimpleMethodTypeName,
(WS_XML_STRING*)&example_wsdlLocalDefinitions.dictionary.xmlStrings.aNamespace,
  0,
  },   // struct description for _SimpleMethod
  // following are global definitions for the out parameter
  ...
  {  // element description
  (WS_XML_STRING*)&example_wsdlLocalDefinitions.dictionary.xmlStrings._SimpleMethodTypeName,
  (WS_XML_STRING*)&example_wsdlLocalDefinitions.dictionary.xmlStrings.aNamespace,
  WS_STRUCT_TYPE,
  (void *)&example_wsdlLocalDefinitions.globalElements.SimpleMethod._SimpleMethoddescs.structDesc,
  },
  {    // message description for ISimpleService_SimpleMethod_InputMessage
  (WS_XML_STRING*)&example_wsdlLocalDefinitions.dictionary.xmlStrings.ISimpleService_SimpleMethod_InputMessageactionName,
(WS_ELEMENT_DESCRIPTION*)&example_wsdl.globalElements.SimpleMethod,
  },    // message description for ISimpleService_SimpleMethod_InputMessage

En règle générale, un niveau d’indirection est ajouté pour tous les paramètres [out] et [in,out].

Opération sans paramètre

Pour les opérations de document et littéral, Wsutil.exe traite l’opération comme ayant un paramètre d’entrée et un paramètre de sortie si :

  • Le message d’entrée ou de sortie comporte plusieurs parties.
  • Il n’existe qu’une partie de message et le nom de la partie de message n’est pas « parameters ».

.. Dans l’exemple ci-dessus, en supposant que les parties de message sont nommées ParamIn" et ParamOut, la signature de méthode devient le code suivant :

typedef struct SimpleMethod{
unsigned int a;
unsigned int b;
};

typedef struct SimpleMethodResponse {
unsigned int b;
unsigned int c;
};

typedef  struct ISimpleService_SimpleMethodParamStruct
{
SimpleMethod  * SimpleMethod;
SimpleMethodResponse  * SimpleMethodResponse;
} ISimpleService_SimpleMethodParamStruct;

Wsutil.exe génère une signature de version pour la description de l’opération, de sorte que le moteur de modèle de service côté serveur et WsCall puisse vérifier si la description générée s’applique à la plateforme actuelle.

Ces informations de version sont générées dans le cadre de la structure WS_OPERATION_DESCRIPTION. Le numéro de version peut être traité comme un sélecteur de bras d’union pour rendre la structure extensible. Actuellement, l’ID de version est défini sur1 sans champs suivants. Le versiosn futur peut incrémenter le numéro de version et inclure davantage de champs si nécessaire. Par exemple, Wsutil.exe génère actuellement le code suivant en fonction de l’ID de version :

{ // SimpleMethod
{ // parameter descriptions for SimpleMethod
{ WS_PARAMETER_TYPE_NORMAL, (USHORT)0, (USHORT)-1 },
{ WS_PARAMETER_TYPE_NORMAL, (USHORT)1, (USHORT)-1 },
{ WS_PARAMETER_TYPE_NORMAL, (USHORT)-1, (USHORT)1 },
},    // parameter descriptions for SimpleMethod
{    // operation description for SimpleMethod
1,
(WS_MESSAGE_DESCRIPTION*)&example_wsdl.messages.ISimpleService_SimpleMethod_InputMessage,
(WS_MESSAGE_DESCRIPTION*)&example_wsdl.messages.ISimpleService_SimpleMethod_OutputMessage,
3,
(WS_PARAMETER_DESCRIPTION*)example_wsdlLocalDefinitions.contracts.DefaultBinding_ISimpleService.SimpleMethod.params,
SimpleMethodOperationStub
}, //operation description for SimpleMethod
},  // SimpleMethod

À l’avenir, elle pourrait être développée comme suit :

WS_OPERATION_DESCRIPTION simpleMethodOperationDesc =
{
  2,
  &ISimpleService_SimpleMethod_InputputMessageDesc,
  &ISimpleService_SimpleMethod_OutputMessageDesc,
  WsCountOf(SimpleMethodParameters),
  SimpleMethodParameters,
  ISimpleService_SimpleMethod_Stub,
  &forwardToString;   // just as an example.
};

Sécurité

Consultez la section sécurité de l’outil compilateur Wsutil rubrique.