Writing Web Services Client Applications using Visual C++
Abstract:
XML Web Services is rapidly becoming the technology of choice for distributed application and application-integration, over Internet. The caller application uses the standard protocol (such as HTTP or SMTP) to send specially-formatted XML messages (known as SOAP request envelope); the server application in turn does the processing and sends back the results also as specially formatted XML message (known as SOAP response envelope). The standards-based notion of Web Services enables these two applications; possibly residing on two totally different platforms, and written using varied programming languages and tools; to interact without and platform- or programming language-worries. XML Web Services is receiving more and more vendor and tools support, and the technology itself is maturing rapidly. Microsoft, IBM, and others are working to define the essential layers (such as security, transaction-support, Web Services reliability, coordination and routing) over Web Services.
This article illustrates writing XML Web Services client applications in Visual C++, using Microsoft XML Core Services (or MSXML), Microsoft SOAP Toolkit Version 3, and PocketSOAP.
The first example uses the MSXML XMLHTTP component to invoke a Web Service written using Apache SOAP.
The second example uses the MSXML ServerXMLHTTP component to invoke a Web Service written using (ASP).NET. This example illustrates calling a Web Service using HTTP GET, HTTP POST, and SOAP.
The third example uses Microsoft SOAP Toolkit Version 3 high-level API to invoke a Web Service written using GLUE.
The fourth example uses Microsoft SOAP Toolkit Version 3 low-level API to invoke a Web Service written using (ASP).NET.
The fifth, and final, C++ sample application illustrates using PocketSOAP to call a .NET Web Service.
All the sample applications in this article are Win32 console applications written using C++ and created using Microsoft Visual Studio 6.0. The first two applications use MSXML 4, next two (third and fourth) sample applications use SOAP Toolkit version 3, and the fifth application uses PocketSOAP. Be sure to download and install these libraries to successfully run the code samples provided with this article.
Author:
Darshan Singh (Managing Editor, PerfectXML.com)
Last Updated:
February 02, 2003
Download:
1. Using MSXML XMLHTTP MSXML, the premier XML processing component from Microsoft, is shipped with various versions of Internet Explorer (for example, IE 6.0 ships MSXML version 3 SP2); and is also available as a separate download from the MSDN Web site. If you are writing a C++ desktop client application and need to call a Web Service, one option is to use the XMLHTTP component inside MSXML. XMLHTTP, a COM wrapper around WinInet (the core HTTP API used by Internet Explorer), allows sending HTTP GET and POST requests and process the response. XMLHTTP is designed to be used on the client-side, and should not be used inside server applications to send HTTP requests to other servers. If you are writing a server-side application, and need to call a Web Service, refer to example 2 below, which uses ServerXMLHTTP. This first sample application uses MSXML XMLHTTP to POST a SOAP request to the Weather ?Temperature Web Service on the XMethods Web site.
The header file (XMLHTTP1.h):
#ifndef _XMLHTTP1_H_
#define _XMLHTTP1_H_
#include <atlbase.h>
#import <msxml4.dll> named_guids
using namespace MSXML2;
#define CHECK_HR(hr) { if (FAILED(hr)) { throw -1; } }
// SOAP Endpoint URL
static const TCHAR* const g_lpszSOAPEndpointURL =
_T("http://services.xmethods.net:80/soap/servlet/rpcrouter");
// SOAP Request to be posted
static const TCHAR* const g_lpszSOAPReq = _T(
"<SOAP-ENV:Envelope xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/' "
"xmlns:xsi='http://www.w3.org/1999/XMLSchema-instance' "
"xmlns:xsd='http://www.w3.org/1999/XMLSchema'> "
"<SOAP-ENV:Body> "
"<ns1:getTemp xmlns:ns1='urn:xmethods-Temperature' "
" SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'> "
"<zipcode xsi:type='xsd:string'>%s</zipcode> "
"</ns1:getTemp> "
"</SOAP-ENV:Body> "
"</SOAP-ENV:Envelope> ");
#endif // _XMLHTTP1_H_
The source code file (XMLHTTP1.cpp)
#include "stdafx.h"
#include "XMLHTTP1.h"
//------------------------------------------------------------------------------
// Function uses XMLHTTP to POST a SOAP request to the Temprature Web Service
// and then uses MSXML DOM to process the SOAP response XML text.
//------------------------------------------------------------------------------
float CallWebService(LPCTSTR szZipCode)
{
float fTemprature = -999;
USES_CONVERSION;
// SOAP Request Envelop to be posted
TCHAR szSOAPReq[MAX_PATH*2] = {0};
sprintf(szSOAPReq, g_lpszSOAPReq, szZipCode);
//printf(szSOAPReq);
// Create an instance of XMLHTTP Class
CComPtr<IXMLHTTPRequest> spXMLHTTP;
HRESULT hr = spXMLHTTP.CoCreateInstance(CLSID_XMLHTTP40);
CHECK_HR(hr);
// Initialize the Synchronous HTTP POST request
hr = spXMLHTTP->open(_bstr_t(_T("POST")), g_lpszSOAPEndpointURL, VARIANT_FALSE);
CHECK_HR(hr);
// Set the required Content-Type header
hr = spXMLHTTP->setRequestHeader(_bstr_t(_T("Content-Type")), _bstr_t(_T("text/xml")));
CHECK_HR(hr);
// Send the POST request, along with the SOAP request envelope text
hr = spXMLHTTP->send(_bstr_t(szSOAPReq));
CHECK_HR(hr);
if(200 == spXMLHTTP->status) //Success
{
// using MSXML DOM to process SOAP response XML text
CComQIPtr <IXMLDOMDocument2> spResponseXMLDoc;
CComPtr <IXMLDOMNode> spResultNode;
spResponseXMLDoc = spXMLHTTP->responseXML;
spResultNode = spResponseXMLDoc->selectSingleNode(_bstr_t(_T("//return")));
if(spResultNode.p != NULL)
{
fTemprature = spResultNode->nodeTypedValue;
}
}
else
{
printf(_T("\nError: %s\n"), W2A(spXMLHTTP->statusText));
}
return fTemprature;
}
// main, The entry point function
int main(int argc, char* argv[])
{
if(argc < 2)
{//insufficient parameters
printf(_T("\nXMLHTTP1.exe: Sample Web Service client to get the temprature for the given Zipcode.\n"));
printf(_T("\nUsage:\tXMLHTTP1.exe <US_Zipcode>\nExample: XMLHTTP1.exe 98007\n"));
return -1;
}
try
{
HRESULT hr = CoInitialize(NULL);
CHECK_HR(hr);
float fTemp = CallWebService((LPCTSTR)argv[1]);
if(fTemp != -999)
printf(_T("The temprature at zipcode %s is %.2f Fahrenheit.\nPress Enter to continue..."),
argv[1],fTemp);
else
printf(_T("\nError: Invalid Zipcode or failed to get the temprature at zipcode %s. "
"\nPress Enter to continue..."), argv[1]);
}
catch(int)
{
//TODO: Error handling
printf(_T("Exception raised!"));
}
CoUninitialize();
getchar();
return 0;
}
Output:
The header file declares the #import statement so that we can use MSXML4, and declares couple of constant strings (the SOAP endpoint URL and the SOAP request envelope). The main function in the CPP file calls a supporting function CallWebService and passes it the value of the command line parameter (the zip code). The supporting function CallWebService then formats the request SOAP envelope and posts it to the SOAP endpoint URL. The responseXML property contains the response SOAP envelope and the above application uses MSXML DOM and XPath to get the result value.
2. Using MSXML ServerXMLHTTP If you are writing a server-side application (such as an ISAPI DLL or a COM DLL to be loaded on the server side), and need to invoke a XML Web Service, you can use MSXML ServerXMLHTTP component. The following sample C++ application illustrates using MSXML ServerXMLHTTP to invoke a .NET Web Service, either by sending HTTP GET request, or HTTP POST request, or posting SOAP and processing the SOAP response. This sample application invokes the Weather Info XML Web Service to get the detailed weather information for any given valid US zip code.
The header file (ServerXMLHTTP1.h):
#ifndef _SERVERXMLHTTP1_H_
#define _SERVERXMLHTTP1_H_
#include <atlbase.h>
#import <msxml4.dll> named_guids
using namespace MSXML2;
#define CHECK_HR(hr) { if (FAILED(hr)) { throw -1; } }
// --------------------------------------------------------
static const TCHAR* const g_lpszGetURL =
_T("http://www.ejse.com/WeatherService/Service.asmx/GetWeatherInfo?zipCode=%s");
// --------------------------------------------------------
static const TCHAR* const g_lpszPostURL =
_T("http://www.ejse.com/WeatherService/Service.asmx/GetWeatherInfo");
// --------------------------------------------------------
static const TCHAR* const g_lpszSOAPEndpointURL =
_T("http://www.ejse.com/WeatherService/Service.asmx");
static const TCHAR* const g_lpszSOAPAction =
_T("http://ejse.com/WeatherService/GetWeatherInfo");
static const TCHAR* const g_lpszSOAPReq =
_T("<?xml version='1.0' encoding='utf-8'?> "
"<soap:Envelope xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' "
" xmlns:xsd='http://www.w3.org/2001/XMLSchema' "
" xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'>"
" <soap:Body>"
" <GetWeatherInfo xmlns='http://ejse.com/WeatherService/'>"
" <zipCode>%s</zipCode>"
" </GetWeatherInfo>"
" </soap:Body>"
"</soap:Envelope>");
// --------------------------------------------------------
static const TCHAR* const g_lpszXPathSelNS =
_T("xmlns:ns1='http://ejse.com/WeatherService/'");
// --------------------------------------------------------
//TODO: Move to resource file
static const TCHAR* const g_lpszOutputMsg =
_T("Weather Information - Last Updated %s\n\n"
"\tLocation: %s (%s)\n\tTemprature: %s\n\tForecast: %s\n\t"
"Visibility: %s\n\tHumidity: %s\n\tPressure: %s\n");
// --------------------------------------------------------
#endif // _SERVERXMLHTTP1_H_
The source code file (ServerXMLHTTP1.cpp)
#include "stdafx.h"
#include "ServerXMLHTTP1.h"
// Utility function
void GetXPathExprValue(CComQIPtr<IXMLDOMDocument2> &spResponseXMLDoc,
LPCTSTR lpszXPathExpr,
LPSTR lpszResultValue)
{
USES_CONVERSION;
CComPtr <IXMLDOMNode> spResultNode;
spResultNode = spResponseXMLDoc->selectSingleNode(_bstr_t(lpszXPathExpr));
if(spResultNode.p != NULL)
_tcscpy(lpszResultValue, W2A(_bstr_t(spResultNode->nodeTypedValue)));
}
//------------------------------------------------------------------------------
// Function uses ServerXMLHTTP to send a GET/POST/SOAP request to a .NET Web Services,
// and then uses MSXML DOM to process the response XML text.
//
// Illustrates calling a .NET Web Service either by sending a GET request,
// or a HTTP POST or by using the SOAP method.
//
// iMethod parameter:
// 0 : (Default) HTTP GET
// 1 : HTTP POST
// 2 : SOAP
//------------------------------------------------------------------------------
void CallWebService(LPCTSTR szZipCode, int iMethod = 0)
{
float fTemprature = -999;
USES_CONVERSION;
// Create an instance of ServerXMLHTTP Class
CComPtr<IServerXMLHTTPRequest> spServerXMLHTTP1;
HRESULT hr = spServerXMLHTTP1.CoCreateInstance(CLSID_ServerXMLHTTP40);
CHECK_HR(hr);
TCHAR szGetURL[MAX_PATH*2]={0};
TCHAR szPostValue[MAX_PATH*2]={0};
TCHAR szSOAPReq[MAX_PATH*2]={0};
int iPostDataLen =0;
TCHAR szDataLen[10]={0};
switch(iMethod)
{
case 0: // HTTP GET
sprintf(szGetURL, g_lpszGetURL, szZipCode);
// Initialize the Synchronous HTTP GET request
hr = spServerXMLHTTP1->open(_bstr_t(_T("GET")), szGetURL, VARIANT_FALSE);
CHECK_HR(hr);
// Send the HTTP GET request
hr = spServerXMLHTTP1->send();
CHECK_HR(hr);
break;
case 1: // HTTP POST
_tcscpy(szPostValue, _T("zipCode="));
_tcscat(szPostValue, szZipCode);
iPostDataLen = _tcslen(szPostValue);
itoa(iPostDataLen, szDataLen, 10);
// Initialize the Synchronous HTTP GET request
hr = spServerXMLHTTP1->open(_bstr_t(_T("POST")), g_lpszPostURL, VARIANT_FALSE);
CHECK_HR(hr);
spServerXMLHTTP1->setRequestHeader(_T("Content-Type"),
_T("application/x-www-form-urlencoded"));
spServerXMLHTTP1->setRequestHeader(_T("Content-Length"), szDataLen);
// Send the HTTP POST request, along with the SOAP request envelope text
hr = spServerXMLHTTP1->send(szPostValue);
CHECK_HR(hr);
break;
case 2: // SOAP
sprintf(szSOAPReq, g_lpszSOAPReq, szZipCode);
hr = spServerXMLHTTP1->open(_bstr_t(_T("POST")), g_lpszSOAPEndpointURL, VARIANT_FALSE);
CHECK_HR(hr);
// Set the required SOAPAction and Content-Type headers
hr = spServerXMLHTTP1->setRequestHeader(_T("SOAPAction"), g_lpszSOAPAction);
CHECK_HR(hr);
hr = spServerXMLHTTP1->setRequestHeader(_bstr_t(_T("Content-Type")),
_bstr_t(_T("text/xml")));
CHECK_HR(hr);
// Send the POST request, along with the SOAP request envelope text
hr = spServerXMLHTTP1->send(_bstr_t(szSOAPReq));
CHECK_HR(hr);
break;
}
if(200 == spServerXMLHTTP1->status) //Success
{
// using MSXML DOM to process the response XML text
CComQIPtr <IXMLDOMDocument2> spResponseXMLDoc;
spResponseXMLDoc = spServerXMLHTTP1->responseXML;
spResponseXMLDoc->setProperty(_bstr_t(_T("SelectionNamespaces")), g_lpszXPathSelNS);
TCHAR szLastUpdated[MAX_PATH] = {0};
TCHAR szLocation[MAX_PATH] = {0};
TCHAR szReportedAt[MAX_PATH] = {0};
TCHAR szTemprature[MAX_PATH] = {0};
TCHAR szForecast[MAX_PATH] = {0};
TCHAR szVisibility[MAX_PATH] = {0};
TCHAR szHumidity[MAX_PATH] = {0};
TCHAR szPressure[MAX_PATH] = {0};
GetXPathExprValue(spResponseXMLDoc, _T("//ns1:LastUpdated"), szLastUpdated);
GetXPathExprValue(spResponseXMLDoc, _T("//ns1:Location"), szLocation);
GetXPathExprValue(spResponseXMLDoc, _T("//ns1:ReportedAt"), szReportedAt);
GetXPathExprValue(spResponseXMLDoc, _T("//ns1:Temprature"), szTemprature);
GetXPathExprValue(spResponseXMLDoc, _T("//ns1:Forecast"), szForecast);
GetXPathExprValue(spResponseXMLDoc, _T("//ns1:Visibility"), szVisibility);
GetXPathExprValue(spResponseXMLDoc, _T("//ns1:Humidity"), szHumidity);
GetXPathExprValue(spResponseXMLDoc, _T("//ns1:Pressure"), szPressure);
printf(g_lpszOutputMsg, szLastUpdated, szReportedAt, szLocation,
szTemprature, szForecast, szVisibility, szHumidity, szPressure);
}
else
{
printf(_T("\nError: %s\n"), W2A(spServerXMLHTTP1->statusText));
}
}
// main, The entry point function
int main(int argc, char* argv[])
{
if(argc < 2)
{//insufficient parameters
printf(_T("\nServerXMLHTTP1.exe: Sample Web Service client to get "
" the weather information for a valid given U.S. zipcode\n"));
printf(_T("\nUsage:\tServerXMLHTTP1.exe <US_Zipcode> [<Method (0=GET, 1=POST, 2=SOAP)>]\n"
"Examples:\n\tServerXMLHTTP1.exe 98007\n\t"
"ServerXMLHTTP1.exe 98007 1"));
printf(_T("\n\nPress Enter to continue..."));
getchar();
return -1;
}
try
{
HRESULT hr = CoInitialize(NULL);
CHECK_HR(hr);
// The Method (GET[0]/POST[1]/SOAP[2])
int iCallMethod = 0;
if(argc >= 3 && argv[2] != NULL)
iCallMethod = atoi(argv[2]);
CallWebService((LPCTSTR)argv[1], iCallMethod);
}
catch(int)
{
//TODO: Error handling
printf(_T("Exception raised!"));
}
CoUninitialize();
printf(_T("\n\nPress Enter to continue..."));
getchar();
return 0;
}
Output:
The above sample application uses MSXML ServerXMLHTTP component to call a XML Web Service either by sending a HTTP GET, HTTP POST or a SOAP request. The iMethod parameter to the CallWebService supporting function decides which method to use while invoking the Web Service.
The header file imports the MSXML type library, and declares few strings used for sending the Web Service request and to process the response XML. The CPP source code file, first creates an instance of ServerXMLHTTP class, and then based on iMethod parameter, the switch statement sends the HTTP GET, POST, or SOAP request, and if the request was successful (HTTP status code = 200), responseXML DOM object and XPath expressions are used to get the returned weather information.
3. Using Microsoft SOAP Toolkit 3.0 (High-Level API) The Microsoft SOAP Toolkit is a set of COM components used to: invoke Web Services on the client-side, map Web Service method calls to the COM object methods, and to marshal and unmarshal (that is to create, transmit, read and process) SOAP messagesIn this article we'll keep the focus on invoking the Web Services using this toolkit. The SOAP toolkit offers high-level API and low-level API. As the names suggest, the high-level API hides the SOAP-messaging related details and you feel like you are calling a COM object; whereas, with the low-level API, you exactly know how the SOAP request and response messages will look like. To download the SOAP Toolkit and for more information on this, visit msdn.microsoft.com/soap.
The following sample application uses the Microsoft SOAP Toolkit version 3.0 high-level API to invoke Domain Name Checker Web Service to see if the specified domain name is availabe or not. Note that the SOAP Tookit has the dependancy on the MSXML, and hence the following header file imports MSXML typelib in addition to the SOAP toolkit type library.
The header file (SOAPToolkit1.h):
#ifndef _SOAPTOOLKIT1_H_
#define _SOAPTOOLKIT1_H_
#include "atlbase.h"
#import <msxml4.dll>
using namespace MSXML2;
#import "C:\Program Files\Common Files\MSSoap\Binaries\mssoap30.dll" named_guids \
exclude("IStream", "IErrorInfo", "ISequentialStream", "_LARGE_INTEGER", \
"_ULARGE_INTEGER", "tagSTATSTG", "_FILETIME")
using namespace MSSOAPLib30;
#define CHECK_HR(hr) { if (FAILED(hr)) { throw -1; } }
// -----------------------------------------------------------------------
static const TCHAR* const g_lpszWSDL_URL =
_T("http://services.xmethods.net/soap/urn:xmethods-DomainChecker.wsdl");
// -----------------------------------------------------------------------
#endif // _SOAPTOOLKIT1_H_
The source code file (SOAPToolkit1.cpp)
#include "stdafx.h"
#include "SOAPToolkit1.h"
void CallWebService(_bstr_t bstrDomainName)
{
USES_CONVERSION;
CComPtr<ISoapClient> spSOAPClient;
HRESULT hr = spSOAPClient.CoCreateInstance(CLSID_SoapClient30);
CHECK_HR(hr);
hr = spSOAPClient->MSSoapInit(_bstr_t(g_lpszWSDL_URL),
L"", L"", L"");
CHECK_HR(hr);
// Call the Web Service method
WCHAR *pwcMethodName = L"checkDomain";
DISPID dispidFn = 0;
hr = spSOAPClient->GetIDsOfNames(IID_NULL, &pwcMethodName, 1,
LOCALE_SYSTEM_DEFAULT, &dispidFn);
CHECK_HR(hr);
unsigned int uArgErr;
VARIANT varg[1];
varg[0].vt = VT_BSTR;
varg[0].bstrVal = bstrDomainName;
DISPPARAMS params;
params.cArgs = 1;
params.rgvarg = varg;
params.cNamedArgs = 0;
params.rgdispidNamedArgs = NULL;
_variant_t result;
uArgErr = -1;
EXCEPINFO excepInfo;
memset(&excepInfo, 0, sizeof(excepInfo));
hr = spSOAPClient->Invoke(dispidFn, IID_NULL, LOCALE_SYSTEM_DEFAULT,
DISPATCH_METHOD, ¶ms, &result, &excepInfo, &uArgErr);
CHECK_HR(hr);
if(result.vt = VT_BSTR)
{
printf(_T("Domain %s is %s."), W2A(bstrDomainName), W2A(result.bstrVal));
printf(_T("\n\nPress Enter to continue..."));
getchar();
}
}
int main(int argc, char* argv[])
{
if(argc < 2)
{//insufficient parameters
printf(_T("\nSOAPToolkit1.exe: Sample Web Service client to check "
" whether a domain name is available or not\n"));
printf(_T("\nUsage:\tSOAPToolkit1.exe <DomainName>\n"
"Examples:\n\tSOAPToolkit1.exe microsoft.com\n\t"
"SOAPToolkit1.exe mywebsiteurl.com"));
printf(_T("\n\nPress Enter to continue..."));
getchar();
return -1;
}
try
{
HRESULT hr = CoInitialize(NULL);
CHECK_HR(hr);
CallWebService(_bstr_t(argv[1]));
}
catch(int)
{
//TODO: Error handling
printf(_T("Exception raised!"));
}
CoUninitialize();
return 0;
}
Output:
The header file imports MSXML and SOAP Toolkit type libraries, and declares the Web Service WSDL URL string.
The SoapClient30 class is used to invoke the Web Services and this class is the basis of the SOAP toolkit high-level API. The CPP source code file above creates an instance of SoapClient30 class, followed by calling MSSoapInit member function, passing it the WSDL URL.
The MSSoapInit member function is used to initialize the SoapClient30 object using the WSDL file, and once we do this, the SoapClient30 instance can be used to call the Web Service method, like any other COM object that implements IDispatch interface. Once the SoapClient30 object is initialized using the WSDL file, IDispatch methods (GetIDsOfNames and Invoke) can be called on this object for any of the Web Service method. The above code illustrates this for the checkDomain Web Service method.
4. Using Microsoft SOAP Toolkit 3.0 (Low-Level API) The SOAP Toolkit low-level API provides the absolute control over the SOAP message serialization and transport. The SoapSerializer30 class is used to create the SOAP message. As in this example, we'll be calling a Web Service over HTTP, we use the HttpConnector30 class instance to send the SOAP request message and receive the SOAP response message, over HTTP. And finally, the SoapReader30 class is used to read the SOAP response message. The SoapReader30 class offers various methods and properties to easily read/parse/extract the information from the SOAP message. The following sample application uses Microsoft SOAP Toolkit 3.0 low-level API classes to invoke the SearchMusicTeachers Web Service, to find out the music teachers details in the given US zip code. Only Zip code value is accepted as the command line parameter, other values (such as instrument, result count restriction, radius, skill level, etc.) are hard-coded in this sample application.
The source code file (SOAPToolkitLowLevel.cpp):
#include "stdafx.h"
#include <stdio.h>
#include "atlbase.h"
#import <msxml4.dll>
using namespace MSXML2;
#import "C:\Program Files\Common Files\MSSoap\Binaries\mssoap30.dll" \
exclude("IStream", "IErrorInfo", "ISequentialStream", "_LARGE_INTEGER", \
"_ULARGE_INTEGER", "tagSTATSTG", "_FILETIME")
using namespace MSSOAPLib30;
void CallWebService(_bstr_t ZipCode)
{
ISoapSerializerPtr Serializer;
ISoapReaderPtr Reader;
ISoapConnectorPtr Connector;
HRESULT hr = S_OK;
hr = Connector.CreateInstance(__uuidof(HttpConnector30));
Connector->Property[_T("EndPointURL")] =
_T("http://www.PerfectXML.net/WebServices/MusicTeachers/MusicTeachers.asmx?wsdl");
hr = Connector->Connect();
Connector->Property[_T("SoapAction")] =
_T("http://www.PerfectXML.com/NETWebSvcs/MusicTeachers/FindMusicTeachers");
hr = Connector->BeginMessage();
hr = Serializer.CreateInstance(__uuidof(SoapSerializer30));
hr = Serializer->Init(_variant_t((IUnknown*)Connector->InputStream));
hr = Serializer->StartEnvelope(_T("soap"),_T("NONE"),_T(""));
hr = Serializer->StartBody(_T(""));
hr = Serializer->StartElement(_T("FindMusicTeachers"),
_T("http://www.PerfectXML.com/NETWebSvcs/MusicTeachers/"),
_T("NONE"),_T(""));
hr = Serializer->StartElement(_T("ZipCode"),
_T("http://www.PerfectXML.com/NETWebSvcs/MusicTeachers/"),
_T("NONE"),_T(""));
hr = Serializer->WriteString(ZipCode);
hr = Serializer->EndElement();
hr = Serializer->StartElement(_T("Instrument"),
_T("http://www.PerfectXML.com/NETWebSvcs/MusicTeachers/"),
_T("NONE"),_T(""));
hr = Serializer->WriteString(_T("0"));
hr = Serializer->EndElement();
hr = Serializer->StartElement(_T("SkillLevel"),
_T("http://www.PerfectXML.com/NETWebSvcs/MusicTeachers/"),
_T("NONE"),_T(""));
hr = Serializer->WriteString(_T("0"));
hr = Serializer->EndElement();
hr = Serializer->StartElement(_T("Style"),
_T("http://www.PerfectXML.com/NETWebSvcs/MusicTeachers/"),
_T("NONE"),_T(""));
hr = Serializer->WriteString(_T("0"));
hr = Serializer->EndElement();
hr = Serializer->StartElement(_T("Radius"),
_T("http://www.PerfectXML.com/NETWebSvcs/MusicTeachers/"),
_T("NONE"),_T(""));
hr = Serializer->WriteString(_T("10"));
hr = Serializer->EndElement();
hr = Serializer->StartElement(_T("RestrictResultsCount"),
_T("http://www.PerfectXML.com/NETWebSvcs/MusicTeachers/"),
_T("NONE"),_T(""));
hr = Serializer->WriteString(_T("5"));
hr = Serializer->EndElement();
hr = Serializer->EndElement();
hr = Serializer->EndBody();
hr = Serializer->EndEnvelope();
hr = Connector->EndMessage();
hr = Reader.CreateInstance(__uuidof(SoapReader30));
hr = Reader->Load(_variant_t((IUnknown*)Connector->OutputStream), _T(""));
CComQIPtr<IXMLDOMDocument2> spResponseXMLDOM;
spResponseXMLDOM = Reader->Dom;
//TODO: Process the response SOAP XML and display the results
//For now, just printing the response XML text as it is.
USES_CONVERSION;
printf(_T("Response: %s\n"), (const char*)W2A(spResponseXMLDOM->xml));
printf(_T("\n\nPress Enter to continue..."));
getchar();
}
int main(int argc, char* argv[])
{
CoInitialize(NULL);
if(argc < 2)
{//insufficient parameters
printf(_T("\nSOAPToolkitLowLevel.exe: Sample Web Service client to find out "
" music teachers within 10-mile radius of a valid given U.S. zipcode\n"));
printf(_T("\nUsage:\tSOAPToolkitLowLevel.exe <US_Zipcode>\n"
"Examples:\n\tSOAPToolkitLowLevel.exe 98007\n\t"
"SOAPToolkitLowLevel.exe 60195"));
printf(_T("\n\nPress Enter to continue..."));
getchar();
return -1;
}
CallWebService(_bstr_t(argv[1]));
CoUninitialize();
return 0;
}
Output:
We do not make use of the header file in this sample application. The sample source code CPP file above, imports the MSXML and SOAP Toolkit type libraries. The supporting function CallWebService (which is called from the main function), creates instances of SoapSerializer, HttpConnector, and SoapReader classes. The HttpConnector instance is initialized by setting the EndPointURL and the SoapAction properties, and the Connect method is used to initialize the HttpConnector instance by using the WSDL.
Note how the SoapSerializer class is used to precisely create the SOAP request message. This class provides the full control over how the message should look like, by allowing us to specify the namespace value, namespaces prefixes to use, encoding style, and other structural details of the SOAP envelope. The SoapSerializer Init method is used above to associate the request envelope with the transport input stream. When the SOAP request envelope is fully created using the SoapSerializer members, the HttpConnector EndMessage method is called to indicate that the request envelope is ready, and to actually post the SOAP message and receive the response, synchronously.
The HttpConnector OutputStream is used to load the response message into the the SoapReader object. In this particular example, we just access the Dom property on the Reader, to load the response SOAP envelope into a MSXML DOMDocument object. You may parse and process this DOMDocument object to get the individual result values. For brevity, we just print the response XML text as it is in the above example.
5. Using PocketSOAP v1.4.1 PocketSOAP is a COM component that can be used to invoke Web Services. The following sample C++ application uses PocketSOAP v1.4.1 COM component to invoke SalesRankNPrice sample Web Service, to find out the Amazon.com sales rank and the price for any given ISBN value.
The source code file (PocketSOAP1.cpp):
#include "stdafx.h"
#include "atlbase.h"
#import "C:\Program Files\SimonFell\PocketSOAP\pSOAP32.dll" named_guids
using namespace PocketSOAP;
void CallWebService(_bstr_t bstrISBN)
{
CComPtr <ISOAPEnvelope> spEnvelope;
HRESULT hr = spEnvelope.CoCreateInstance(CLSID_CoEnvelope);
spEnvelope->SetMethod(_T("GetAmazonSalesRankNPrice"),
_T("http://www.PerfectXML.com/NETWebSvcs/BookService"));
spEnvelope->Parameters->Create(_T("ISBN"),
bstrISBN,
_T("http://www.PerfectXML.com/NETWebSvcs/BookService"),
_T(""), _T(""));
CComPtr <IHTTPTransportAdv> spHTTPTransport;
hr = spHTTPTransport.CoCreateInstance(CLSID_HTTPTransport);
spHTTPTransport->SOAPAction =
L"http://www.PerfectXML.com/NETWebSvcs/BookService/GetAmazonSalesRankNPrice";
_bstr_t bstrEnvelope = spEnvelope->Serialize();
hr = spHTTPTransport->Send(
_T("http://www.perfectxml.net/WebServices/SalesRankNPrice/BookService.asmx"),
bstrEnvelope);
CComVariant cvTransport(spHTTPTransport);
spEnvelope->Parse(cvTransport, _T(""));
bstrEnvelope = spEnvelope->Serialize();
USES_CONVERSION;
printf(_T("Response:\n%s"), W2A(bstrEnvelope));
printf(_T("\n\nPress Enter to continue..."));
getchar();
}
int main(int argc, char* argv[])
{
if(argc < 2)
{//insufficient parameters
printf(_T("\nPocketSOAP1.exe: Sample Web Service client to find out "
" Amazon.com Sales Rank and Price for a given ISBN.\n"));
printf(_T("\nUsage:\tPocketSOAP1.exe <ISBN>\n"
"Examples:\n\tPocketSOAP1.exe 1861005318\n\t"
"PocketSOAP1.exe 0735712867"));
printf(_T("\n\nPress Enter to continue..."));
getchar();
return -1;
}
HRESULT hr = CoInitialize(NULL);
CallWebService(_bstr_t(argv[1]));
CoUninitialize();
return 0;
}
Output:
The above C++ code begins by importing the PocketSOAP type library, and then includes the supporting function CallWebService definition. This function uses the ISOAPEnvelope interface methods to initialize the SOAP request message (by defining the Web Service method to call, namespace to use, and defining the parameters). Next, it uses the IHTTPTransportAdv interface methods to set the SOAPAction, and to actually post the SOAP request. The resultant message is parsed and loaded into the ISOAPEnvelope object, which is then serialized as a string and written out to the console.
Summary
The goal of this article was to show you some of the ways in which you can build Web Services client applications, specifically using Visual C++. The first example illustrated using MSXML XMLHTTP for this purpose; next application used ServerXMLHTTP to invoke a Web Service; third sample application used Microsoft SOAP Toolkit high-level API, while the fourth application presented the code that used Microsoft SOAP Toolkit low-level API. Final, and fifth application, illustrated using PocketSOAP COM library to call a Web Service method.
Download:
Related Links:
PerfectXML MSXML Focus Section
Visual Basic Developer's Guide to SOAP
Microsoft & Web Services (SOAP)
C++ Web services client using Systinet WASP Server for C++
Simplified SOAP Development with SOAP::Lite - Part I
About the author:
Darshan Singh is the founder of PerfectXML.com, the XML community Web site. As an author, he has co-authored various books published by Wrox Press and has written various articles for PerfectXML.com, and other technical sites such as ASPToday and MSDN. Darshan can be reached at darshan@PerfectXML.com.
Get our Newsletter:
E-mail address:
Discount Subscription Offer
10% Discount + Two FREE Issues of Web Services Journal
10% Discount + Two FREE Issues of XML-Journal
Featured Vendors
PerfectXML.com is a proud winner of
Best Educational Web Site (Finalist)