Windows Web Services API : Step-By-Step per creare un Web Service


Nel mio post precedente abbiamo visto come scrivere un client C/C++ capace di invocare un servizio scritto in WCF.


In questo post vedremo come scrivere l’equivalente del servizio WCF in codice unmanaged tramite le WWSAPI.


A differenza della realizzazione di un client il server comporta ovviamente la scrittura a priori del WSDL e degli XSD !!! Questo perchè il linguaggio C/C++ non è così ricco di metadati come il framework .NET !! Quindi per gli amanti del contract-first nessun problema, mentre per i più pigri è sempre possibile definire l’interfaccia in WCF e quindi scaricare i metadati via svcutil /t:metadata 🙂 (come abbiamo fatto per il client)…


A questo punto possiamo avventurarci nella scrittura del nostro primo Web Service in C/C++



  1. Creiamo un nuovo progetto di tipo Win32 Console Application.

  2. Andiamo nella configurazione del progetto e selezioniamo All Configuration in alto a sinistra nella finestra di Property Page.

    1. Selezioniamo Configuration Properties->C/C++ –> Precompiled Headers e alla voce Create/Use Precompiled Header impostiamo : Not Using Precompiled Headers.

    2. Selezioniamo Configuration Properties->Linker->Input e alla voce Additional Dependencies scriviamo WebServices.lib.

  3. Premiamo OK alla finestra di configurazione.

  4. Includiamo i .h e .c presenti nella directory WSMetadata.

  5. Aggiungiamo al progetto i files .h e .c presenti nella directory WSMetadata.

  6. Aggiungiamo l’include ai file .h inseriti nel progetto e a <webservices.h>:

    #include <webservices.h>

    #include "..\\wsmetadata\\mariofontanapublicdemos.com.CalculatorService.Bindings.wsdl.h"
    #include "..\\wsmetadata\\mariofontanapublicdemos.com.CalculatorService.wsdl.h"
    #include "..\\wsmetadata\\mariofontanapublicdemos.com.CalculatorService.xsd.h"
    #include "..\\wsmetadata\\schemas.microsoft.com.2003.10.Serialization.xsd.h"


  7. Compiliamo…

  8. Copiamo il codice riportato sotto e poi sempre animati da fiducia… compiliamo 🙂

#include <iostream>
#include <conio.h>


// helper routine to print error object
void PrintError(HRESULT errorCode, WS_ERROR* error)
{
    printf("Failure: errorCode=0x%lx\n", errorCode);


    if (errorCode == E_INVALIDARG || errorCode == WS_E_INVALID_OPERATION)
    {
        // Correct use of the APIs should never generate these errors
        printf("The error was due to an invalid use of an API.  This is likely due to a bug in the program.\n");
        //DebugBreak();
    }


    HRESULT hr = NOERROR;
    if (error != NULL)
    {
        ULONG errorCount;
        hr = WsGetErrorProperty(error, WS_ERROR_PROPERTY_STRING_COUNT, &errorCount, sizeof(errorCount));
        if (FAILED(hr))
        {
            goto Exit;
        }
        for (ULONG i = 0; i < errorCount; i++)
        {
            WS_STRING string;
            hr = WsGetErrorString(error, i, &string);
            if (FAILED(hr))
            {
                goto Exit;
            }
            printf("%.*s\n", string.length, string.chars);
        }
    }
Exit:
    if (FAILED(hr))
    {
        printf("Could not get error string (errorCode=0x%lx)\n", hr);
    }
}


//questa è la vera e propria funzione callback che implementa il metodo Add


HRESULT CALLBACK Add(
    __in const WS_OPERATION_CONTEXT* context,
    __in int a,
    __in int b,
    __out __int64* result,
    __in_opt const WS_ASYNC_CONTEXT* asyncContext,
    __in_opt WS_ERROR* error)
{
    UNREFERENCED_PARAMETER(context);
    UNREFERENCED_PARAMETER(asyncContext);
    UNREFERENCED_PARAMETER(error);


    *result = a + b;
    printf ("%d + %d = %X\n", a, b, *result);
    fflush(stdout);
    return NOERROR;
}


int _tmain(int argc, _TCHAR* argv[])
{


    HRESULT hr;
    WS_ERROR* error;
    hr = WsCreateError(NULL,0, &error);
    if (FAILED(hr)) return -1;


    WS_HEAP* heap;
    hr= WsCreateHeap(1024,0,NULL,0,&heap, error);
    if(FAILED(hr))
    {
        WsFreeError(error);
        return -1;
    } 

   
wprintf(L"Hosting del mio primo Web Service in C/C++...\n");


    BasicHttpBinding_ICalculatorFunctionTable Functions = {Add};


    WS_STRING url = WS_STRING_VALUE (L"http://localhost:8080/NativeCalculatorService");
    WS_HTTP_BINDING_TEMPLATE templateValue = {};
    WS_SERVICE_ENDPOINT* serviceEndpoint;
    hr = BasicHttpBinding_ICalculator_CreateServiceEndpoint (&templateValue,url,&Functions,NULL,NULL,0,heap,&serviceEndpoint,error);   
    if(FAILED(hr))
    {
        PrintError(hr,error);
        WsFreeHeap(heap);
        WsFreeError(error);
        return -1;
    }


    WS_SERVICE_HOST* host = NULL;
    const WS_SERVICE_ENDPOINT* serviceEndpoints[1];
    serviceEndpoints[0] = serviceEndpoint;


    hr = WsCreateServiceHost(serviceEndpoints, 1,NULL, 0, &host, error);
    if(FAILED(hr))
    {
        PrintError(hr,error);
        WsFreeHeap(heap);
        WsFreeError(error);
        return -1;
    }


    hr = WsOpenServiceHost(host,NULL,error);
    if(FAILED(hr))
    {
        PrintError(hr,error);
        WsFreeServiceHost(host);
        WsFreeHeap(heap);
        WsFreeError(error);
        return -1;
    }


    wprintf(L"Premi Enter per chiudere il servizio...\n");
    _getch();


    WsCloseServiceHost(host, NULL, error);
    WsFreeServiceHost(host);


    WsFreeHeap(heap);
    WsFreeError(error);


    return 0;


}


a questo punto Ctrl+F5 e lanciamo il nostro Web Service e torniamo sul codice client dove andremo a commentare il

#define _WCF


in modo da chiamare il Web Service che abbiamo appena creato. Come si può notare il codice client è il medesimo (salvo l’url) e possiamo invocare indistintamente Web Services scritti in WCF e in C/C++.


La schematizzazione del codice è la seguente :


image


--Mario

Comments (2)

  1. If you speak Italian, there are two great resources to learn more about connecting your native C/C++

  2. Sono appena tornato dal Delphi Day 2009, il secondo per me, dopo quello del 2007. Marco Cantù è stato

Skip to main content