Bungiorno a tutti.

Mi sono iscritto oggi al forum per chiedere aiuto sull'implementazione di un client WS con il C++ Builder 6.0. In pratica sono 6 anni che lavoro con il programma di sviluppo della Borland senza grossi intoppi (mi sono trovato diverse volte in difficoltà per componenti nuovi mai utilizzati per i quali sono sempre riuscito a trovare una più che abbondante documentazione in rete che mi ha aiutato a risolvere i problemi).

Le nuove tecnologie ci forniscono strumenti per potenziare i propri programmi che non sono sempre così semplici da utilizzare (poi ci si mette il cliente che vuole le modifiche per ieri, il datore di lavoro che non fornisce documentazione adeguata e non da la possibilità di corsi di formazione; credo, comunque, che questi siano problemi comuni a molte aziende).

Ho bisogno di creare un client WS con C++ Builder 6.0 verso un web service creato con .Net (Visual Studio 2005).

La documentazione che trovo in rete si riferisce in gran parte a Delphi ed è poca anche in quel caso.

Ho fatto tutto ciò che la documentazione trovata mi suggeriva di fare:

_ creato una nuova applicazione
_ File-> New-> Other-> Web Services-> WSDL importer

Il 'WSDL Importer' ha creato i file .cpp e .h con l'implementazione delle funzioni da usare per interrogare il web service e la struttura delle classi e dei metodi da utilizzare.

Questo è il file .h che è stato creato:

// ************************************************** ********************** //
// The types declared in this file were generated from data read from the
// WSDL File described below:
// WSDL : http://192.168.1.105/CommonRegistry/...stry.asmx?WSDL
// Encoding : utf-8
// Codegen : [wfDebug,wfVerbose,wfSkipHttpBindings,wfIgnoreSchem aErrors,wfSkipUnusedTypes]
// Version : 1.0
// (30/03/2009 13.46.48 - $Revision: 1.0.1.0.1.82 $)
// ************************************************** ********************** //

#ifndef CommonRegistryH
#define CommonRegistryH

#include <System.hpp>
#include <InvokeRegistry.hpp>
#include <XSBuiltIns.hpp>
#include <SoapHTTPClient.hpp>

namespace NS_CommonRegistry {

class TRegistryData;

enum TRegistryStatus
{
NoError,
SSRRDidNotRespond,
BadParameters
};

class TRegistryStatus_TypeInfoHolder : public TObject {
TRegistryStatus __instanceType;
public:
__published:
__property TRegistryStatus __propType = { read=__instanceType };
};


// ************************************************** ********************** //
// Namespace : http://www.policlinico.local/
// ************************************************** ********************** //
class TRegistryData : public TRemotable {
private:
AnsiString FASS_COGNOME;
AnsiString FASS_NOME;
TXSDateTime* FASS_NASC_DATA;
TXSDateTime* FDataValidazioneSSRR;
int FIDAnagrafica;
TXSDateTime* FDataValidazioneSSRRMedicoCurante;
int FIDAnagraficaMedicoCurante;
public:
__fastcall ~TRegistryData();
__published:
__property AnsiString ASS_COGNOME = { read=FASS_COGNOME, write=FASS_COGNOME };
__property AnsiString ASS_NOME = { read=FASS_NOME, write=FASS_NOME };
__property TXSDateTime* ASS_NASC_DATA = { read=FASS_NASC_DATA, write=FASS_NASC_DATA };
__property TXSDateTime* DataValidazioneSSRR = { read=FDataValidazioneSSRR, write=FDataValidazioneSSRR };
__property int IDAnagrafica = { read=FIDAnagrafica, write=FIDAnagrafica };
__property TXSDateTime* DataValidazioneSSRRMedicoCurante = { read=FDataValidazioneSSRRMedicoCurante, write=FDataValidazioneSSRRMedicoCurante };
__property int IDAnagraficaMedicoCurante = { read=FIDAnagraficaMedicoCurante, write=FIDAnagraficaMedicoCurante };
};

typedef DynamicArray<TRegistryData*> ArrayOfTRegistryData;

// ************************************************** ********************** //
// Namespace : http://www.policlinico.local/
// soapAction: http://www.policlinico.local/%operationName%
// transport : http://schemas.xmlsoap.org/soap/http
// binding : CommonRegistrySoap
// service : CommonRegistry
// port : CommonRegistrySoap
// URL : http://192.168.1.105/CommonRegistry/CommonRegistry.asmx
// ************************************************** ********************** //
__interface INTERFACE_UUID("{06B12636-2E41-1A74-7D4F-B7D22FA2C654}") CommonRegistrySoap : public IInvokable
{
public:
virtual void SearchByCRA(const AnsiString pCRA, const bool ForceReload, const TRegistryStatus status, ArrayOfTRegistryData& SearchByCRAResult, TRegistryStatus& ResultStatus) = 0;
virtual void SearchByCF(const AnsiString pCF, const bool ForceReload, const TRegistryStatus status, ArrayOfTRegistryData& SearchByCFResult, TRegistryStatus& ResultStatus) = 0;
virtual void SearchByCNDN(const AnsiString pLastName, const AnsiString pFirstName, const TXSDateTime* pDateOfBirth, const bool ForceReload, const TRegistryStatus status, ArrayOfTRegistryData& SearchByCNDNResult, TRegistryStatus& ResultStatus) = 0;
virtual TRegistryData* InsertPaziente(const TRegistryData* pData) = 0;
};
typedef DelphiInterface<CommonRegistrySoap> _di_CommonRegistrySoap;

_di_CommonRegistrySoap GetCommonRegistrySoap(bool useWSDL=false, AnsiString addr="");
_di_CommonRegistrySoap GetCommonRegistrySoap(bool useWSDL=false, AnsiString addr="", THTTPRIO* HTTPRIO=NULL);

#endif // __CommonRegistry_h__

}; // NS_CommonRegistry

#if !defined(NO_IMPLICIT_NAMESPACE_USE)
using namespace NS_CommonRegistry;
#endif

Il codice del file .cpp

// ************************************************** ********************** //
// The types declared in this file were generated from data read from the
// WSDL File described below:
// WSDL : http://192.168.1.105/CommonRegistry/...stry.asmx?WSDL
// Encoding : utf-8
// Codegen : [wfDebug,wfVerbose,wfSkipHttpBindings,wfIgnoreSchem aErrors,wfSkipUnusedTypes]
// Version : 1.0
// (30/03/2009 13.46.48 - $Revision: 1.0.1.0.1.82 $)
// ************************************************** ********************** //

#include <vcl.h>
#pragma hdrstop

#if !defined(CommonRegistryH)
#include "CommonRegistry.h"
#endif

namespace NS_CommonRegistry {

_di_CommonRegistrySoap GetCommonRegistrySoap(bool useWSDL, AnsiString addr)
{
static const char* defWSDL= "http://192.168.1.105/CommonRegistry/CommonRegistry.asmx?WSDL";
static const char* defURL = "http://192.168.1.105/CommonRegistry/CommonRegistry.asmx";
static const char* defSvc = "CommonRegistry";
static const char* defPrt = "CommonRegistrySoap";
if (addr=="")
addr = useWSDL ? defWSDL : defURL;
THTTPRIO* rio = new THTTPRIO(0);
if (useWSDL) {
rio->WSDLLocation = addr;
rio->Service = defSvc;
rio->Port = defPrt;
} else {
rio->URL = addr;
}
_di_CommonRegistrySoap service;
rio->QueryInterface(service);
if (!service)
delete rio;
return service;
}

_di_CommonRegistrySoap GetCommonRegistrySoap(bool useWSDL, AnsiString addr, THTTPRIO* HTTPRIO)
{
static const char* defWSDL= "http://192.168.1.105/CommonRegistry/CommonRegistry.asmx?WSDL";
static const char* defURL = "http://192.168.1.105/CommonRegistry/CommonRegistry.asmx";
static const char* defSvc = "CommonRegistry";
static const char* defPrt = "CommonRegistrySoap";
if (addr=="")
addr = useWSDL ? defWSDL : defURL;
THTTPRIO* rio = HTTPRIO ? HTTPRIO : new THTTPRIO(0);
if (useWSDL) {
rio->WSDLLocation = addr;
rio->Service = defSvc;
rio->Port = defPrt;
} else {
rio->URL = addr;
}
_di_CommonRegistrySoap service;
rio->QueryInterface(service);
if (!service && !HTTPRIO)
delete rio;
return service;
}

__fastcall TRegistryData::~TRegistryData()
{
delete FASS_NASC_DATA;
delete FDataValidazioneSSRR;
delete FDataValidazioneSSRRMedicoCurante;
}

// ************************************************** ********************** //
// This routine registers the interfaces and types used by invoke the SOAP
// Service.
// ************************************************** ********************** //
static void RegTypes()
{
/* CommonRegistrySoap */
InvRegistry()->RegisterInterface(__interfaceTypeinfo(CommonRegis trySoap), L"http://www.policlinico.local/", L"utf-8");
InvRegistry()->RegisterDefaultSOAPAction(__interfaceTypeinfo(Com monRegistrySoap), L"http://www.policlinico.local/%operationName%");
RemClassRegistry()->RegisterXSInfo(GetClsMemberTypeInfo(__typeinfo(TR egistryStatus_TypeInfoHolder)), L"http://www.policlinico.local/", L"TRegistryStatus");
/* TRegistryData */
RemClassRegistry()->RegisterXSClass(__classid(TRegistryData), L"http://www.policlinico.local/", L"TRegistryData");
RemClassRegistry()->RegisterXSInfo(__arrayTypeinfo(ArrayOfTRegistryDa ta), L"http://www.policlinico.local/", L"ArrayOfTRegistryData");
}
#pragma startup RegTypes 32

}; // NS_CommonRegistry

Programma principale

Nel programma principale ho aggiunto un bottone e, nell'evento click, ho aggiunto il codice seguendo le indicazioni trovate nella documentazione in rete.

La stessa cosa si può fare in 3 modi differenti:

_ 1° modo:

ArrayOfTRegistryData data;
TRegistryStatus ResultStatus = NoError;
TRegistryStatus RequestStatus = NoError;

_di_CommonRegistrySoap soap = GetCommonRegistrySoap(true, "");

if (soap)
soap->SearchByCRA("ABCDEF", false, RequestStatus, data, ResultStatus);

_ 2° modo (usa un componente THTTPRio aggiunto alla form):

ArrayOfTRegistryData data;
TRegistryStatus ResultStatus = NoError;
TRegistryStatus RequestStatus = NoError;

_di_CommonRegistrySoap soap = GetCommonRegistrySoap(true, "", CommonRegistryRio);

if (soap)
soap->SearchByCRA("ABCDEF", false, RequestStatus, data, ResultStatus);

_ 3° modo. Usa lo stesso componente THTTPRio usato nel modo precedente con tutte le proprietà valorizzato. Nel secondo metodo, le proprietà non sono richieste e vengono valorizzate dalla funzione GetCommonRegistry.

ArrayOfTRegistryData data;
TRegistryStatus ResultStatus = NoError;
TRegistryStatus RequestStatus = NoError;

_di_CommonRegistrySoap soap = NULL;
CommonRegistryRio->QueryInterface(soap);
if (soap)
{
soap->SearchByCRA("ABCDEF", false, RequestStatus, data, ResultStatus);
}

In tutti i modi testati, il programma viene compilato senza errori ma in esecuzione, quando chiamo la funzione SearchByCRA, ottengo questo errore:

Impossibile elaborare la richiesta. ---> Riferimento a un oggetto non impostato su un'istanza di oggetto.

Generalmente questo tipo di errore si ottiene quando si crea un oggetto senza instanziarlo con il new.

Per questo motivo ho modificato il codice in questo modo (la modifica è stata testata con tutte e 3 le modalità di chiamata del WS):

ArrayOfTRegistryData *data = new ArrayOfTRegistryData();

TRegistryStatus *ResultStatus = new TRegistryStatus();
TRegistryStatus *RequestStatus = new TRegistryStatus();

*RequestStatus = NoError;
*ResultStatus = NoError;

_di_CommonRegistrySoap soap = GetCommonRegistrySoap(true, "", CommonRegistryRio);

soap->SearchByCRA("ABCDEF", false, *RequestStatus, *data, *ResultStatus);

delete [] data;

Modificando il modo in cui vengono istanziati gli oggetti, ho cambiato anche i parametri passati alla SearchByCRA altrimenti il builder mi avrebbe ritornato uno o più errori di compilazione.

Ottengo sempre lo stesso errore. Cosa sto sbagliando? Dimentico qualche cosa?

Il web service .Net funziona senza problemi (per verificarne il funzionamento ho creato un client WS con .Net).

Ringrazio sin d'ora chiunque possa darmi una mano a capire cosa sto sbagliando.

Saluti.

Massimiliano