WSDL 和服务协定

Wsutil.exe 实用工具根据提供的 WSDL 元数据以及用户创作的 XML 架构描述的数据类型定义和说明生成 C 语言存根。

下面是一个示例 WSDL 文档和 XML 架构,作为以下讨论的基础:

<?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>

此示例使用单个方法 SimpleMethod 提供协定 ISimpleService。 “SimpleMethod”具有两个类型为 整数的输入参数,从客户端发送到服务的b。 同样,SimpleMethod 具有两个类型为 整数的输出参数,bc,这些参数在成功完成后会返回到客户端。 在 SAL 批注 C 语法中,方法定义如下所示:

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

在此定义中,ISimpleService 是包含单个服务作的服务协定:SimpleMethod。

输出头文件包含外部引用的定义和说明。 这包括:

  • 全局元素类型的 C 结构定义。
  • 当前文件中定义的作原型。
  • WSDL 文件中指定的协定的函数表原型。
  • 当前文件中指定的所有函数的客户端代理和服务存根原型。
  • 当前文件中定义的全局架构元素的 WS_ELEMENT_DESCRIPTION 数据结构。
  • 当前文件中指定的所有消息的 WS_MESSAGE_DESCRIPTION 数据结构。
  • 当前文件中指定的所有协定的 WS_CONTRACT_DESCRIPTION 数据结构。

生成一个全局结构,以封装应用程序可以引用的架构类型和服务模型类型的所有全局说明。 该结构是使用规范化文件名命名的。 在此示例中,Wsutil.exe 生成一个名为“example_wsdl”的全局定义结构,其中包含所有 Web 服务说明。 结构定义在存根文件中生成。

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;

对于 XML 架构文档(XSD)中的全局元素定义,会为每个元素生成一个 WS_ELEMENT_DESCRIPTION 原型以及相应的 C 类型定义。 SimpleMethod 和 SimpleMethodResponse 的元素说明的原型生成为上述结构中的成员。 C 结构按如下方式生成:

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

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

与全局复杂类型类似,Wsutil.exe 生成类型 C 结构定义(如上所示)而不匹配元素说明。

对于 WSDL 输入,Wsutil.exe 生成以下原型和定义:

  • 为消息说明生成 WS_MESSAGE_DESCRIPTION 原型。 服务模型和消息层可以使用此说明。 消息说明结构是全局结构中名为“messagename”的字段。 在此示例中,消息说明生成为 WSDL 文件中指定的ISimpleService_SimpleMethod_InputMessage结构中的ISimpleService_SimpleMethod_InputMessage字段。
  • WS_CONTRACT_DESCRIPTION 原型是为合同说明生成的。 此说明由服务模型使用。 协定说明结构是全局结构中名为“contractname”的字段。 在此示例中,合同说明生成为结构“_example_wsdl”中的DefaultBinding_ISimpleService字段。

作和类型规范对代理和存根都是常见的,它们都是在两个文件中生成的。 仅当在同一文件中生成代理和存根时,Wsutil.exe 才会生成一个副本。

标识符生成

上面列出的自动生成的 C 结构是根据 WSDL 文件中指定的名称创建的。 XML NCName 通常不被视为有效的 C 标识符,并且名称会根据需要规范化。 不转换十六进制值,将转换为下划线“_”字符,以改进可读性,如“:”、“/”和“.”。

存根的标头

对于服务协定中的每个作,将生成一个名为“<operationname>Callback”的回调例程。 (例如,示例服务协定中的作“SimpleMethod”具有名为“SimpleMethodCallback”的生成回调。

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

对于每个 WSDL portType Wsutil.exe 生成一个函数表,该表表示 portTypeportType 上的每个作 都有一个对应的函数指针,指向函数表中存在的回调。

struct ISimpleServiceMethodTable
{
  ISimpleService_SimpleMethodCallback SimpleMethod;
};

将为所有作生成代理原型。 原型名称是在服务协定的 WSDL 文件中指定的作名称(在本例中为 SimpleMethod)。

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

仅本地说明原型生成

代理和stub 文件包含全局定义结构的定义,包括包含仅限本地说明的结构和客户端代理/服务存根实现的原型和定义。

存根文件本地的所有原型和定义都作为封装结构的一部分生成。 此总体本地描述结构提供序列化层和服务模型所需的说明的清晰层次结构。 本地说明结构具有类似于如下所示的原型:

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;

引用其他文件中的定义

本地定义可以引用另一个文件中生成的说明。 例如,消息可以在从 WSDL 文件生成的 C 代码文件中定义,但消息元素可以在从 XSD 文件生成的 C 代码文件中的其他地方定义。 在这种情况下,Wsutil.exe 从包含消息定义的文件中生成对全局元素的引用,如下所示:

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

全局元素说明

对于 wsdl:type 或 XSD 文件中定义的每个全局元素,GlobalElement 字段中有一个名为 elementName 的匹配字段。 在此示例中,将生成名为 SimpleMethod 的结构:

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

元素说明所需的其他说明作为包含结构的一部分生成。 如果元素是简单的类型元素,则只有一个 WS_ELEMENT_DESCRIPTION 字段。 如果元素类型为结构,则所有相关字段和结构说明将作为元素结构的一部分生成。 在此示例中,SimpleMethod 元素是包含两个字段的结构,一个b。 Wsutil.exe 生成结构,如下所示:

...
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;
...

嵌入式结构和嵌入元素会根据需要生成为子结构。

Wsutil.exe 为指定 wsdl:service 下定义的每个 portType 值生成 WSDL 节下的字段。

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

Wsutil.exe 生成一个字段 f,其中包含作所需的所有说明、指向每个方法的每个作说明的指针的符号数组,以及指定 portType的一个 WS_CONTRACT_DESCRIPTION

作所需的所有说明均在指定的 portType下的 operationName 字段中生成。 其中包括 WS_ELEMENT_DESCRIPTION 字段以及输入和输出参数的子结构。 同样,输入消息和可选输出消息的 WS_MESSAGE_DESCRIPTION 字段随以下项一起包含:WS_PARAMETER_DESCRIPTION 所有作参数的列表字段以及作本身的 WS_OPERATION_DESCRIPTION 字段。 在此示例中,生成 SimpleMethod 说明的代码结构,如下所示:

...
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;
...

在各种说明中使用的名称和命名空间作为类型 WS_XML_STRING的字段生成。 所有这些字符串都是作为每文件常量字典的一部分生成的。 字符串列表和 WS_XML_DICTIONARY 字段(以下示例中名为 听写)生成为 fileNameLocal 结构的字典字段的一部分。

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

WS_XML_STRING数组生成为一系列 WS_XML_STRING类型的字段,以用户友好名称命名。 生成的存根使用各种说明中的用户友好名称来提高可读性。

WSDL作的客户端代理

Wsutil.exe 为所有作生成客户端代理。 应用程序可以使用前缀命令行选项覆盖方法签名。

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
  );      
}

作调用方必须传入有效的 参数。 使用 参数中指定的WS_HEAP值分配输出参数。 调用函数可以重置或释放堆以释放所有输出参数的内存。 如果作失败,则可以从可选错误对象(如果可用)中检索其他详细信息错误信息。

Wsutil.exe 为绑定中所述的所有作生成服务存根。

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 );
}

以上部分介绍仅包含存根文件本地所有定义的本地结构的原型。 后续部分介绍说明的定义。

WSDL 定义生成

Wsutil.exe 生成一个名为 *<file_name>类型 *localDefinitions 的常量静态(常量静态)结构,<service_name>该结构包含所有本地唯一定义。

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

支持以下 WSDL 说明:

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

处理 wsdl:operation 和 wsdl:message

WSDL 文档中指定的每个作都通过 Wsutil.exe映射到服务作。 该工具为服务器和客户端生成服务作的单独定义。

<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>

输入和输出消息数据元素的布局由工具评估,以生成基础结构的序列化元数据,以及与输入和输出消息关联的生成的服务作的实际签名。

特定 portType 中每个作的元数据都有输入和输出消息,其中每个消息都映射到 WS_MESSAGE_DESCRIPTION。 在此示例中,portType 中映射到 inputMessageDescription 的作的输入和输出消息,以及 WS_OPERATION_DESCRIPTION 上的 outputMessageDescription(可选)。

对于每个 WSDL 消息,该工具将生成引用 WS_ELEMENT_DESCRIPTION 定义的 WS_MESSAGE_DESCRIPTION,如下所示:

... 
{    // 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
...

消息说明是指输入元素说明。 由于元素是全局定义的,因此消息说明引用全局定义而不是本地静态元素。 同样,如果元素在另一个文件中定义,Wsutil.exe 将生成对该文件中全局定义的结构的引用。 例如,如果在另一个 example.xsd 文件中定义了 SimpleMethodResponse,则 Wsutil.exe 生成以下内容:

...
{    // 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
...

每个消息说明都包含所有消息数据元素的作和特定元素说明(类型为 WS_ELEMENT_DESCRIPTION的字段)。 对于 RPC 样式的消息或包含多个部分的消息,将创建包装元素来封装其他信息。

RPC 样式支持

Wsutil.exe 根据 SOAP 1.2 规范的 WSDL 1.1 绑定扩展支持文档样式和 RPC 样式作。 RPC 和文本样式作标记为WS_RPC_LITERAL_OPERATION。 服务模型忽略 RPC/文本作中响应正文包装元素的名称。

Wsutil.exe 本机不支持编码样式作。 为编码消息生成WS_XML_BUFFER参数,开发人员必须直接填充不透明缓冲区。

多个消息部件支持

Wsutil.exe 支持一条消息中的多个消息部件。 可以按如下所示指定多部分消息:

<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 将为消息元素生成 WS_STRUCT_TYPE 字段。 如果消息使用文档样式表示,则 Wsutil.exe 生成具有结构类型的包装元素。 包装元素没有名称或特定命名空间,包装器结构包含所有部分中的所有元素作为字段。 包装元素仅供内部使用,不会在消息正文中序列化它。

如果消息使用 RPC 或文本样式表示形式,则 Wsutil.exe 根据 WSDL SOAP 扩展规范创建作名称为元素名称和指定命名空间即服务命名空间的包装元素。 元素的结构包含表示消息部件中指定的类型的字段数组。 包装元素映射到消息正文中的实际顶部元素,如 SOAP 规范中所示。

在服务器端,每个作都会导致生成的服务器服务作的 typedef。 此 typedef 用于引用函数表中的作,如前所述。 每个作还会导致生成存根函数,该存根函数代表委托调用实际方法。

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
  );

对于 SimpleMethod作,上面定义了 SimpleMethodOperation typedef。 请注意,生成的方法具有一个展开的参数列表,其中包含 SimpleMethod作的输入和输出消息的消息部分作为命名参数。

在客户端上,每个作都映射到代理服务作。

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);

处理 wsdl:binding

WWSAPI 服务模型支持SOAP 绑定扩展。 对于每个绑定,都有关联的 portType

仅在 soap 绑定扩展中指定的传输是公告的。 应用程序需要在创建通道时提供传输信息。 目前,我们支持WS_HTTP_BINDING和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>

在我们的示例 WSDL 文档中,ISimpleService 只有一个 portType。 提供的 SOAP 绑定指示 HTTP 传输,该传输指定为WS_HTTP_BINDING。 请注意,此结构没有静态修饰,因为此结构需要可供应用程序使用。

处理 wsdl:portType

WSDL 中的每个 portType 由一个或多个作组成。 该作应与 wsdl:binding 中指示的 SOAP 绑定扩展保持一致。

<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>

在此示例中,ISimpleService portType 仅包含 SimpleMethod作。 这与绑定节一致,其中只有一个映射到 SOAP作的 WSDL作。

由于 ISimpleService portType 只有一个作 -- SimpleMethod -- 相应的函数表仅包含 SimpleMethod 即服务作。

就元数据而言,每个 portType 都由 Wsutil.exe 映射到 WS_CONTRACT_DESCRIPTIONportType 中的每个作都映射到 WS_OPERATION_DESCRIPTION

在此示例中,portType 该工具为 ISimpleService 生成 WS_CONTRACT_DESCRIPTION。 此协定说明包含 ISimpleService portType 上可用的特定作数,以及表示 ISimpleService portType 上定义的单个作的 WS_OPERATION_DESCRIPTION 数组。 由于 ISimpleService portType for ISimpleService 上只有一个作,因此只有一个 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       ...

处理 wsdl:service

WsUtil.exe 使用服务查找绑定/端口类型,并生成描述类型、消息、端口类型定义等的协定结构。 协定说明可从外部访问,它们作为通过生成的标头指定的全局定义结构的一部分生成。

WsUtil.exe 支持 wsdl:port 中定义的 EndpointReference 扩展。 终结点引用在 WS-ADDRESSING 中定义为描述服务的 终结点 信息的方法。 作为 WS_XML_STRING保存的输入终结点引用扩展文本以及匹配 WS_ENDPOINT_ADDRESS_DESCRIPTION 在全局结构的 endpointReferences 节中生成。

<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
}

使用 WsUtil 生成的元数据创建 WS_ENDPOINT_ADDRESS

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, 

客户端代理或服务存根中的常量字符串作为WS_XML_STRING类型的字段生成,并且代理或存根文件中的所有字符串都有一个常量字典。 字典中的每个字符串作为本地结构的字典部分中的字段生成,以提高可读性。

... // 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,
  },
}
...

处理 wsdl:type

Wsutil.exe 仅支持 wsdl:type 规范中的 XML 架构 (XSD) 文档。 一种特殊情况是消息端口指定全局元素定义。 有关在这些情况下使用的启发式的更多详细信息,请参阅以下部分。

参数处理启发式

在服务模型中,WSDL 消息映射到方法中的特定参数。 Wsutil.exe 具有两种参数生成样式:在第一个样式中,作有一个输入消息的参数,一个参数用于输出消息(如有必要):在第二种样式中,Wsutil.exe 使用启发法将输入消息和输出消息的字段映射到作中的不同参数并扩展结构中的字段。 输入和输出消息都需要具有结构类型消息元素才能生成第二种方法。

Wsutil.exe 从输入和输出消息生成作参数时使用以下规则:

  • 对于具有多个消息部分的输入和输出消息,每个消息部件是作中的单独参数,消息部件名称为参数名称。
  • 对于包含一个消息部件的 RPC 样式消息,消息部件是作中的参数,消息部件名称为参数名称。
  • 对于包含一个消息部分的文档样式输入和输出消息:
    • 如果消息部件的名称为“parameters”,并且元素类型为结构,则结构中的每个字段都被视为一个单独的参数,字段名称为参数名称。
    • 如果消息部件名称不是“parameters”,则消息是作中的参数,其消息名称用作相应的参数名称。
  • 对于具有 nillable 元素的文档样式输入和输出消息,消息将映射到一个参数,消息部件名称为参数名称。 添加了一个额外的间接级别,以指示指针可以 NULL
  • 如果字段仅出现在输入消息元素中,则该字段将被视为 [in] 参数。
  • 如果字段仅出现在输出消息元素中,则该字段将被视为 [out] 参数。
  • 如果输入消息和输出消息中有一个具有相同名称和同一类型的字段,则该字段将被视为 [in,out] 参数。

以下工具用于确定参数的方向:

  • 如果字段仅出现在输入消息元素中,则该字段仅被视为参数。
  • 如果字段仅出现在输出消息元素中,则该字段将被视为仅输出参数。
  • 如果某个字段的名称和类型相同,并且出现在输入消息和输出消息中,则该字段将被视为 in,out 参数。

Wsutil.exe 仅支持已排序的元素。 如果 Wsutil.exe 不能将 in 参数和输出参数合并到单个参数列表中,则它拒绝对 [in,out] 参数的无效排序。 后缀可能会添加到参数名称,以避免名称冲突。

<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 考虑 tns:SimpleMethod 和 tns:SimpleMethodResponse ato 参数中的字段,如以下参数定义所示:

<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 从上述列表中的字段展开参数列表,并在以下代码示例中生成 ParamStruct 结构。 服务模型运行时可以使用此结构将参数传递给客户端和服务器存根。

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

此结构仅用于描述客户端和服务器端的堆栈帧。 消息说明或消息说明引用的元素说明没有更改。

  // 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

作为一般规则,为所有 [out] 和 [in,out] 参数添加一个级别的间接。

无参数作

对于文档和文本作,Wsutil.exe 将作视为具有一个输入参数和一个输出参数(如果:

  • 输入或输出消息包含多个部分。
  • 只有一个消息部分,消息部件名称不是“parameters”。

.. 在上面的示例中,假设消息部分命名 为 ParamIn“,ParamOut,则方法签名将变为以下代码:

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 为作说明生成版本签名,以便 WsCall 和服务器端服务模型引擎可以检查生成的说明是否适用于当前平台。

此版本信息作为 WS_OPERATION_DESCRIPTION 结构的一部分生成。 版本号可以被视为联合臂选择器,以使结构可扩展。 目前,versionID 设置为 1,没有后续字段。 未来的 versiosn 可能会递增版本号,并根据需要包含更多字段。 例如,Wsutil.exe 当前基于版本 ID 生成以下代码:

{ // 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

将来,可以按如下所示进行扩展:

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

安全

请参阅 Wsutil 编译器工具 主题中的安全部分。