Este artigo foi traduzido por máquina.

Windows com C++

Serviços Web do Windows

Kenny Kerr

Baixar o código de exemplo

Um dos principais motivos que muitos desenvolvedores flocked para Microsoft .NET Framework e Java para um grau menor, foi o fato de que ele tornou muito mais fácil escrever software para a Internet. Se você estivesse escrevendo um cliente ou servidor de aplicativo HTTP, o .NET Framework teve abordados com classes para fazer solicitações HTTP e processamento XML facilmente. Você mesmo pode gerar clientes SOAP de documentos WSDL e implementar servidores SOAP com o ASP.NET. Como os padrões ao redor de Web services matured, a Microsoft desenvolveu o Windows Communications Foundation (WCF), também criado no .NET Framework, para tornar mais fácil de usar os padrões da Web cada vez mais complexos para tratamento de transportes diferentes, como TCP e UDP e fornecer opções de segurança mais versátil.

Desenvolvedores de C++, entretanto, foram deixados imaginando se foi mesmo prático usar C++ para escrever aplicativos. Microsoft tinha fornecido algumas soluções temporárias na forma de classes ATL e um Kit de ferramentas baseadas em COM, mas no final que esses não puderam manter com o progresso que as pilhas SOAP gerenciadas tinham feitas e, portanto, foram amplamente abandonadas.

Apesar do foco aparentemente funcionária no .NET Framework, os desenvolvedores da Microsoft ainda não esquecido sobre o desenvolvedor de C++. Na verdade, muitas delas ainda são e continuarão a ser apaixonados desenvolvedores de C++. Como resultado, soluções que ajudam os desenvolvedores de C++ são uma parte fundamental da estratégia da Microsoft para a plataforma Windows. Obviamente, muitas APIs direcionadas a desenvolvedores de C++ também acabam baseia muitas estruturas gerenciadas.

Embora haja muitos enumerar aqui, algumas são a pena mencionar. O Windows HTTP Services (WinHTTP) API fornece uma solução poderosa e flexível para escrever clientes HTTP. Você pode ler mais sobre WinHTTP na minha coluna de agosto de 2008. O servidor HTTP (HTTP.sys) API fornece as bases para a criação de alto desempenho HTTP servidores sem depender de um servidor Web completo como o Internet Information Services (IIS). Na verdade, o próprio IIS é baseado nessa mesma API. E claro XmlLite API fornece um analisador XML pequeno e rápido para código nativo. Você pode ler mais sobre XmlLite no meu artigo de recurso de abril de 2007.

Considerando tudo isso, é ainda muito uma tarefa desanimadora para escrever um servidor ou cliente SOAP. Embora SOAP iniciado logoff “ simples ” (que é o que significa o “ S ” de), ela não permaneça dessa maneira por muito tempo. WinHTTP, HTTP.sys e XmlLite podem obter você um longo caminho manipulando o transporte HTTP e analisar o XML, mas ainda há uma tonelada de código para gravar para manipular a camada de comunicação: coisas como formatação e interpretando cabeçalhos SOAP, para não mencionar suporte outros transportes como TCP ou UDP. Mesmo se alguma forma, você poderia gerenciar tudo isso, você ainda estiver à esquerda com análise envelopes SOAP em vez de ser capaz de tratar operações lógicas SOAP como chamadas de função.

Bem, essas preocupações são coisa do passado. Com a introdução da API de Serviços Web do Windows (WWS), os desenvolvedores de C++ não têm mais que se considerar cidadãos de segunda classe no mundo dos Serviços Web. WWS foi projetado desde o início até ser uma implementação de código de nativo completamente do SOAP, incluindo suporte para muitos de WS-* protocolos. WWS estritamente falando, é, exposto por meio de uma C API, tornando a interoperabilidade com outros idiomas e runtimes muito simples, mas é desenvolvedor de C++ que provavelmente irá se beneficiar da maioria. Na verdade, com uma pequena ajuda do C++, ele pode ser uma alegria real usar — como você deverá ver neste artigo.

Arquitetura e princípios

WWS incorpora tudo o que não são bibliotecas de classes. Ele é projetado para código nativo. Ele é projetado para apresentar um número mínimo de dependências. Ele é projetado para usar como pouca memória como possíveis. E é projetado para ser rápido. Realmente rápido. A equipe responsável pelo desenvolvimento WWS executa testes de desempenho em cada nova compilação comparando com o WCF e RPC. RPC é usado como uma classificação de linha de base, desde que nada poderia ser mais rápido, mas ele oferece uma maneira confiável de rastreamento regressões de velocidade. No entanto, ele é, ilumina quando comparar WCF e WWS. Figura 1 mostra uma comparação do conjunto de trabalho para um cliente usando o WCF e WWS respectivamente. Que é uma margem bastante drástica, mas talvez não que surpreendente quando você achar que o .NET Framework está envolvido. Figura 2, no entanto, deve ser surpreendente se você, como muitos outros, considere WCF para ser o estado da arte. Ele mostra a taxa de transferência de operações por segundo para um servidor usando o WCF e WWS respectivamente. WWS é mais de duas vezes mais rápido! Não obter me errado: Não há nada errado com WCF ou as classes, mas quando você precisar algo pequeno e rápido, é difícil de vencer C++ e código nativo. Mas você sabe que já!


Figura 1 Comparando Client Working Set (inferior é melhor)

O runtime WWS é empacotado em WebServices.dll, que está incluído no Windows 7 e Windows Server 2008 R2. Também está disponível como uma atualização do sistema para o Windows XP e posterior. Funções exportadas de WebServices.dll representam API WWS e obter acesso a eles por vinculando WebServices.lib e incluindo o arquivo de cabeçalho WebServices.h do SDK do Windows. Até aqui, tudo bem. Mas o que o API aparência? Bem, ao contrário de APIs COM estilo como o do XmlLite ou Direct2D, este C API requer que você imagine um conjunto de objetos que residem nos bastidores e só esperando para separar e camadas lógicas. Let’s primeiro dê uma olhada ele em termos de camadas. Figura 3 ilustra as camadas da funcionalidade exposto pela API WWS, com cada camada criando após um abaixo dela. Cada camada é representada por um número de funções e estruturas e fornece um conjunto de abstrações. Como você pode imaginar, pode tornar o aplicativo usar de qualquer uma das camadas, mas na maior parte que deseja continuar com o modelo de serviço que fornece o modelo de programação mais simples e oculta muitos detalhes para você. A camada de transportes é apenas um lembrete que será alguns protocolo de rede sob ele todos lá. WWS usará WinHTTP, HTTP.sys ou Winsock, dependendo do transporte selecionado e ele é usado para implementar um cliente ou servidor.


Figura 2 Comparando Server Throughput (superior é melhor)


Figura 3 em camadas API

Como seu nome sugere, a camada do modelo de serviço inteiramente abstrai trocas de mensagens SOAP e modela operações de serviço Web lógicas como chamadas de função. Ele não, no entanto, autônomo, mas depende do uso de uma ferramenta de linha de comando chamado Wsutil.exe do SDK do Windows. Dado um arquivo WSDL, essa ferramenta irá gerar um arquivo de cabeçalho, bem como um arquivo de origem C com a maioria do código necessário para ambos se conectar a um serviço Web de determinado descrição e como implementar um serviço da Web, tomando cuidado para garantir a ligação de canal está configurado corretamente e mensagens formatadas corretamente. Isso é de longe a abordagem mais simples e fornece um modelo de programação é muito parecido com o que você espera de RPC tradicional.

A camada do canal, por outro lado, expõe as mensagens enviadas e recebidas em um determinado transporte, mas ainda opcionalmente protege você de ter que realmente formatar mensagens de si mesmo. A vantagem é que são blindado do transporte específico e codificação que é usado. A camada do canal é onde você pode controlar informações de ligação e onde você pode proteger sua comunicação para autenticação ou privacidade.

Camada XML expõe a formatação de mensagens e serialização de dados. Você tem acesso completo ao conteúdo de mensagens mas blindado de codificação específico se você estiver se comunicando com texto, binário ou MTOM. Talvez você se surpreenda ao ouvir WWS tem seu próprio analisador XML. Por que ele não usa apenas XmlLite? Embora o XmlLite é certamente leves e muito rápida, não é bastante perfeita para um número de motivos. O motivo mais óbvio é WWS precisa oferecer suporte a diferentes codificações enquanto XmlLite suporta somente texto. Mensagens SOAP também normalmente são codificadas usando UTF-8, enquanto o XmlLite expõe todas as propriedades com seqüências de caracteres Unicode e isso introduz custo desnecessário ao copiar valores. WWS também tem metas de consumo de memória muito estrito (há realmente uma API para isso, como veremos mais adiante) que não podem ser atendidas com XmlLite. No final, a equipe WWS foi capaz de implementar um analisador personalizado especificamente para SOAP é consideravelmente mais rápido que XmlLite. Tenha em mente que o analisador WWS não se destina a substituir XmlLite. Como uma analisador XML de finalidade geral, é difícil de vencer, mas a camada WWS XML fornece aos desenvolvedores com recursos muito específicos voltados Serializando dados eficientemente de mensagem SOAP usando o subconjunto de XML exigido pelo SOAP e.

Além de funções e estruturas de dados que pertencem a essas três camadas logicamente, a API WWS fornece um número de instalações que são comuns a todas as camadas, incluindo o tratamento de erros, conclusão assíncrona, cancelamento, gerenciamento de memória e mais. Porque eu tiver espaço limitado e deseja ajudar você a começar rapidamente, vou limitar o restante deste artigo para o uso do modelo de serviço para a criação de um servidor e cliente de Web service. Em um futuro artigo, Falarei sobre mais profundamente outras partes do WWS.

Introdução

Para começar, usarei a definição de serviço Web mínima da do Figura 4. Este documento WSDL define tipos, mensagens, operações, pontos de extremidade e ligações de canal do serviço. A primeira coisa a fazer é executá-lo por meio de Wsutil.exe como segue:

Wsutil.exe Calculator.wsdl

Isso produzirá um arquivo de cabeçalho chamado Calculator.wsdl.h e um arquivo de origem C chamado Calculator.wsdl.c.

Figura 4 do Web Service Definition

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

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

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

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

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

</wsdl:definitions>

Antes vamos dar uma olhada no que foi gerado, precisamos obter algumas bases feito, independentemente de você esteja implementando o cliente ou o servidor. A primeira coisa que você precisará é uma maneira de expressar informações de erro. A API WWS expõe informações de erro rich tanto para suas próprias funções, bem como falhas SOAP por meio de um objeto de erro. Sendo uma C API, esse objeto é representado por um identificador opaco e um conjunto de funções. Nesse caso, WS_ERROR 1 representa um identificador para um objeto de erro e WsCreateError é a função que o cria. O objeto é liberado chamando a função WsFreeError. O objeto de erro pode armazenar um número de seqüências de caracteres com diferentes níveis de informações sobre um erro. Para recuperar essas, você precisará primeiro determinar quantos seqüências estão presentes. Isso é feito chamando a função WsGetErrorProperty, dando a alça para o objeto de erro e especificando a constante WS_ERROR_PROPERTY_STRING_COUNT. Armado com essas informações, você chamar a função WsGetErrorString, dando a alça do objeto erro, bem como o índice baseado em zero da seqüência de caracteres para recuperar. Você também pode usar as funções da API para preencher seus próprios objetos de erro. Naturalmente, um pouco C++ irá um longo caminho para tornar isso mais simples e mais confiável. Figura 5 fornece um wrapper de objeto de erro simples. Você pode facilmente enumerar seqüências de caracteres em um objeto de erro, como mostrado na Figura 6 de .

Figura 5 do wrapper do objeto Error

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

Figura 6 enumerar strings em um objeto 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);
}

A próxima coisa que precisaremos é um objeto de heap. O objeto heap fornece controle preciso sobre alocação de memória ao produzir ou consumir mensagens e quando precisar alocar várias estruturas de API. Ele também simplifica o modelo de programação desde que não é necessário para que você conhece precisamente quanta memória é necessária para qualquer função específica tenha êxito. Por exemplo, serão muitas funções no SDK do Windows mais antigas requerem que você adivinhar o quanto de armazenamento é necessária ou primeiro chamar uma função de determinada maneira para determinar quanta memória deve alocar para a função tenha êxito. O uso do objeto de heap WWS remove todos os essa codificação extra e fornece uma boa forma de controlar o uso de memória da API. Isso também é útil de uma perspectiva de segurança onde você pode querer especificar limites esperados em quanto a API pode alocar. WS_HEAP 1 representa um identificador para um objeto de heap e WsCreateHeap é a função que o cria. O objeto é liberado chamando a função WsFreeHeap. Uma vez criado, você pode alocar memória do heap usando a função WsAlloc, mas para este artigo vai apenas transmitimos a alça para o objeto de heap para outras funções de API para uso conforme necessário.

Figura 7 fornece um wrapper de objeto simples de heap. Considerando isso, você pode criar um objeto de heap limitado a 250 bytes como segue:

WsHeap heap;

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

Observe como eu estou passando o objeto de erro para o heap método do objeto criar. Deve nada ir errado ao criar o objeto de heap, poderei interrogue o objeto de erro para descobrir a causa.

Figura 7 do heap objeto wrapper

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

O cliente

Centraliza o lado cliente do modelo de serviço no objeto de proxy de serviço. O código-fonte gerado inclui uma função chamada CalculatorBinding_CreateServiceProxy. O nome é derivado do que o ponto de extremidade ou ligação nome definido no documento WSDL. Esta função cria um objeto de proxy de serviço e retorna um WS_SERVICE_PROXY 1 representando um identificador opaco para esse objeto. O objeto é liberado chamando a função WsFreeServiceProxy. Uma vez criado, seu aplicativo pode abrir o ponto de extremidade de serviço usando a função WsOpenServiceProxy e fazer chamadas via proxy de serviço para o serviço da Web. O que faz exatamente WsOpenServiceProxy é dependente de transporte que está sendo usado. Você também deve ter cuidado para fechar o proxy do serviço antes de liberar o objeto usando a função WsCloseServiceProxy. Naturalmente, todos essa faxina podem ser perfeitamente empacotados no backup uma classe simples fornecida do Figura 8. Considerando isso, você pode criar e abrir o proxy de serviço, como mostrado no do Figura 9.

Figura 8 do invólucro do proxy de serviço

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

Figura 9 criar e abrir Proxy 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));

Estrutura WS_ENDPOINT_ADDRESS é usada para endereçar mensagens sendo enviadas. Você normalmente usará essa estrutura quando programação na camada do canal. Nesse caso, estamos preencher apenas a parte do URL e o proxy de serviço cuida do resto.

Neste ponto, podemos usar outra função gerada especificamente CalculatorBinding_Add, que representa unsurprisingly adicionar operação do serviço. Realmente não obter muito mais fácil do que isso:

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

Depois que terminar interagindo com o serviço da Web você apenas precisa fechar o canal de comunicação do proxy de serviço:

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

O servidor

Enquanto o modelo de programação do lado cliente centros no proxy de serviço, o servidor em vez disso, cria e gerencia um host de serviço que fornece o runtime necessário para escutar os vários pontos de extremidade com base nas informações fornecidas canal. Novamente, porque estamos usando o modelo de serviço, a maioria dos detalhes é abstraída fora e nós estiver deixados somente para criar o ponto de extremidade de serviço e o host. WWS fará o resto.

A primeira etapa é criar o ponto de extremidade do serviço. O modelo de serviço se encarrega de isso no formulário de outra função gerado, especificamente CalculatorBinding_CreateServiceEndpoint, como do Figura 10 mostra. Criar ponto de extremidade requer especificando o endereço no qual o ponto de extremidade irá escutar. Isso é fornecido pelo WS_STRING finalizado estrutura, que é uma seqüência de caracteres Unicode de comprimento de prefixo e não é necessário ser nulo. Porque o ponto de extremidade é responsável por atender solicitações, você precisa fornecer uma tabela de mapeamento para operações expostas pelo serviço de ponteiros de função. A estrutura CalculatorBindingFunctionTable gerada é usada para essa finalidade. Finalmente, o ponto de extremidade é representado por estrutura WS_SERVICE_ENDPOINT e alocado no heap fornecido.

Figura 10 do 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));

A próxima etapa é criar o host de serviço. WS_SERVICE_HOST 1 representa um identificador para um objeto de host de serviço e WsCreateServiceHost é a função que o cria. O objeto é liberado chamando a função WsFreeServiceHost. A função WsCreateServiceHost cria o objeto de host do serviço fornecido uma lista de pontos de extremidade. Neste ponto, você pode iniciar os ouvintes em todos os pontos de extremidade usando a função WsOpenServiceHost. Pare comunicação usando a função WsCloseServiceHost. Como com o proxy de serviço, certifique-se de fechar o host de serviço antes de liberar a ele. Novamente, todos essa faxina podem ser perfeitamente empacotados no backup uma classe simples fornecida do Figura 11. Considerando isso, você pode iniciar o serviço da Web como segue:

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

Nesse caso, há apenas um único ponto de extremidade, mas você pode ver como é fácil seria adicionar pontos de extremidade adicionais para o host de serviço. Quando é hora de parar o serviço, basta fechar canais de comunicação do host de serviço.

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

Implementar realmente a operação de serviço da Web é sobre a parte mais fácil:

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

A assinatura de função AddCallback também é fornecida pelo código fonte gerado, deve você tiver qualquer dúvida sobre como especificar a ele.

E isso é tudo o que tenho espaço para este mês, mas agora você deve ter uma boa idéia dos recursos e benefícios da API de serviços da Web do Windows tem a oferecer. Como você pode ver, os desenvolvedores de C++ finalmente tem uma pilha SOAP moderna à direita da caixa do. Ele oferece o melhor possível desempenho e o uso da memória e é um prazer para usar com uma pequena ajuda do C++.

Figura 11 do invólucro do host de serviço

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

Quem está usando IT?

Várias equipes da Microsoft já começaram a adotar o Windows Web Services (WWS) API dentro de seus próprios produtos ou tecnologias. Em muitos casos, isso substitui pilhas SOAP personalizados internos e até mesmo alguns escolheu substituir WWS implementações comerciais como o Windows Communication Foundation (WCF). Aqui estão apenas alguns exemplos.

O Microsoft Web Services em dispositivos (WSD) API permite aos desenvolvedores escrever clientes e servidores com base no perfil de dispositivos para Web Services (DPWS). No Windows 7, a API WSD foi iniciado usando WWS para a criação e canonização do XML em mensagens SOAP que o runtime WSD envia. A equipe WSD planeja expandir o seu uso WWS como eles adicionar novos recursos e refatorar código existente.

O Windows CardSpace é a implementação da Microsoft de um sistema de identidade baseado em padrões de serviço da Web. Originalmente implementada com o WCF, ele está sendo reconfigurado usando código nativo e WWS para atender aos requisitos de negócios muito rígida no tamanho do instalador baixável e o tempo de execução do conjunto de trabalho.

O Microsoft Forefront Threat Management Gateway (TMG) é uma plataforma de segurança fornecendo firewall e cache recursos para proteger e melhorar o desempenho de redes. Seu recurso de filtragem de URL usa WWS para se conectar ao serviço de reputação da Microsoft para categorizar URLs.

Finalmente, o cliente Infrastructure de chave pública (PKI) fornece gerenciamento de ciclo de vida automática de certificados com inscrição automática, bem como registro de certificado orientados de usuário e aplicativo. Windows 7 introduz um novo conjunto de serviços da Web que permitem a inscrição de certificado ser concluída sem limitações 
traditional do LDAP e DCOM. O cliente PKI faz uso de WWS para todas as operações, incluindo um novo protocolo de diretiva de inscrição de certificados (MS-XCEP) e uma extensão de WS-Trust para inscrição de certificado (MS-WSTEP). Cliente WWS se comunica com um novo conjunto do Active Directory Certificate Services no Windows Server 2008 R2 que são implementadas com o WCF, bem como com Web services fornecidos por emissores de certificado público.

Kenny Kerr é profissional de fabricação de software especializado no desenvolvimento para o Windows. Ele adora escrever e ensinar programação e design de softwares aos desenvolvedores. Você pode entrar Kerr em weblogs.asp.net/kennykerr de .