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++
Creiamo un nuovo progetto di tipo Win32 Console Application.
Andiamo nella configurazione del progetto e selezioniamo All Configuration in alto a sinistra nella finestra di Property Page.
- Selezioniamo Configuration Properties->C/C++ –> Precompiled Headers e alla voce Create/Use Precompiled Header impostiamo : Not Using Precompiled Headers.
- Selezioniamo Configuration Properties->Linker->Input e alla voce Additional Dependencies scriviamo WebServices.lib.
Premiamo OK alla finestra di configurazione.
Includiamo i .h e .c presenti nella directory WSMetadata.
Aggiungiamo al progetto i files .h e .c presenti nella directory WSMetadata.
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"Compiliamo…
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 :
--Mario
Comments
Anonymous
March 27, 2009
If you speak Italian, there are two great resources to learn more about connecting your native C/C++Anonymous
June 04, 2009
Sono appena tornato dal Delphi Day 2009, il secondo per me, dopo quello del 2007. Marco Cantù è stato