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" https://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