MobileMastering Internet Programming on Mobile Devices: Data Exchange Using an XML HTTP...

Mastering Internet Programming on Mobile Devices: Data Exchange Using an XML HTTP Interface

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

Two previous articles, “Mastering Internet Programming on Mobile Devices: First Steps” and “Mastering Internet Programming on Mobile Devices: An Asynchronous Data Exchange,” covered various WinInet-based communications. WinInet does a lot of black jobs for the application programmer, providing a relatively simple way of data transmission to remote hosts. A recent article discovers another aspect of Internet communications; this is the XML HTTP interface available under Pocket PC 2003 and later.

An Overview of IXMLHTTPRequest

The latest versions of MS XML shipped with Pocket PC 2003-flavoured devices that expose the XMLHTTP object, which in turn allow exchanging and manipulating data in XML format. Compared to its desktop brother, it’s poorly documented in the WinCE help system. Hence, I’ll shortly run through IXMLHTTPRequest‘s methods and properties. It is summarized in the following table. I’ve used C++ syntax, but VB developers will find only a few differences.

Programming Element Desciption
Methods
HRESULT open(BSTR bstrMethod,BSTR bstrUrl,VARIANT varAsync,VARIANT bstrUser,VARIANT bstrPassword) Initializes an MSXML.XMLHTTP request and specifies the method, URL, and authentication information for the request
HRESULT setRequestHeader(BSTR bstrHeader,BSTR bstrValue) Defines the specific HTTP header
HRESULT send(VARIANT varBody) Sends an HTTP request to the server and receives a response
HRESULT getResponseHeader(BSTR bstrHeader,BSTR *pbstrValue) Retrieves the value of an HTTP header from the response body
HRESULT getAllResponseHeaders(BSTR *pbstrHeaders)) Retrieves the values of all the HTTP headers
HRESULT abort(void) Cancels the current HTTP request
Get Properties
HRESULT get_status(long *plStatus) Represents the HTTP status code returned by a request
HRESULT get_statusText(BSTR *pbstrStatus) Represents the HTTP response status text
HRESULT get_responseXML(IDispatch **ppBody) Represents the parsed response entity body as a DOM XML document
HRESULT get_responseText(BSTR *pbstrBody) Represents the response entity body as a string
HRESULT get_responseBody(VARIANT *pvarBody) Represents the response entity body as an array of unsigned bytes
HRESULT get_responseStream(VARIANT *pvarBody) Represents the response entity body as an IStream object
HRESULT get_readyState(long *plState) Represents the state of the request
Put Properties
HRESULT put_onreadystatechange(IDispatch *pReadyStateSink) Specifies the event handler to be called when the readyState property changes

So, all the business looks relatively easy.

Common Working Flow

Similar to other WinInet scenarios, XMLHTTP usually can be used as follows:

  • Define and initialize an IXMLHTTPRequest interface instance
  • Call an open method to set up a request method (for example, “GET”), URL, sync/async mode, user, and password
  • Optionally call a setRequestHeader method to set up all required headers
  • Call a send method to send a request to the remote host
  • Obtain a response in the desired form; in other words, as a string, XML document, and so forth, by calling the appropriate get_XXX method
  • Release the IXMLHTTPRequest object

Following the announced process, the following snippet shows these simple steps:

static TCHAR* g_lpszEndpointURL = _T("http://someserver/alex.xml");

CComQIPtr<IXMLHTTPRequest,&__uuidof(IXMLHTTPRequest)> spXMLHTTP;
HRESULT hr = spXMLHTTP.CoCreateInstance(__uuidof(XMLHTTPRequest));
if ( SUCCEEDED(hr) )
{
   COleVariant vAsync,vUser,vPwd;
   vAsync = VARIANT_FALSE;
   hr = spXMLHTTP->open(CComBSTR(_T("GET")), g_lpszEndpointURL,
                        vAsync, vUser, vPwd);

   COleVariant vBody;
   hr = spXMLHTTP->send(vBody);

   long nStatus = 0;
   hr = spXMLHTTP->get_status(&nStatus);

   if ( nStatus == 200 )
   {
      CComBSTR bstrString;
      hr = spXMLHTTP->get_responseText(&bstrString);

      //process it as needed
      ...
   }
}

The preceding code just takes some XML file pointed to by the g_lpszEndpointURL parameter from the remote host. Obviously, you can use similar “GET” requests to receive some XML data from ASP pages. To call Web Services, the application should use the “POST” method. In this case, you will have to provide all SOAP enveloping manually. I will discuss it later in this article.

Receiving a Response as an XML DOM Document

Now, you can modify the previous sample to work with IXMLDOMDocument. The changes are really minimal:

static TCHAR* g_lpszEndpointURL = _T("http://someserver/alex.xml");

CComQIPtr<IXMLHTTPRequest,&__uuidof(IXMLHTTPRequest)> spXMLHTTP;
HRESULT hr = spXMLHTTP.CoCreateInstance(__uuidof(XMLHTTPRequest));
if ( SUCCEEDED(hr) )
{
   COleVariant vAsync,vUser,vPwd;
   vAsync = VARIANT_FALSE;
   hr = spXMLHTTP->open(CComBSTR(_T("GET")), g_lpszEndpointURL,
                        vAsync, vUser, vPwd);

   COleVariant vBody;
   hr = spXMLHTTP->send(vBody);

   long nStatus = 0;
   hr = spXMLHTTP->get_status(&nStatus);

   if ( nStatus == 200 )
   {

      CComQIPtr<IXMLDOMDocument,&__uuidof(IXMLDOMDocument)>
          spResponseXMLDoc;
      hr = spXMLHTTP->get_responseXML((IDispatch**)&spResponseXMLDoc);
      CComQIPtr<IXMLDOMNode,&__uuidof(IXMLDOMDocument)> spNode;
      hr = spResponseXMLDoc->
           selectSingleNode(_T("//GeneralInfo/Name"),&spNode);
      if( spNode != NULL )
      {
         CComBSTR bstrText;
         hr = spNode->get_text(&bstrText);
         TRACE(L"Text is %sn",bstrText);
      }
      ...
   }
}

The red lines fully explain themselves. Just declare a XML DOM Document object instance, and you’re set up. To avoid annoying stuff with COM objects declaration, creation, and releasing, all samples use smart pointers available, for example, in ATL CE. As a result, the code snippet obtains the initialized instance of the IXMLDOMDocument interface. The last action it does is running an XPath query to get some node text value.

Calling a Web Service via XMLHTTP

And finally, here’s how to communicate with a Web Servce by using an XMLHTTP object. Web Services get their data upon using a “POST” method, so you need to take some additional steps. That is not too complicated, but it takes several lines of code:

static TCHAR* g_lpszEndpointURL = _T("http://some.server.com/
                                      testservice");

static TCHAR* 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>
"    <UserLogin >"
"      <UserName>%s</UserName>"
"      <Password>%s</Password>"
"    </UserLogin>"
"  </soap:Body>"
"</soap:Envelope>");

CComQIPtr<IXMLHTTPRequest,&__uuidof(IXMLHTTPRequest)> spXMLHTTP;
HRESULT hr = spXMLHTTP.CoCreateInstance(__uuidof(XMLHTTPRequest));
if ( SUCCEEDED(hr) )
{
   COleVariant vAsync,vUser,vPwd;
   vAsync = VARIANT_FALSE;
   hr = spXMLHTTP->open(CComBSTR(_T("POST")), g_lpszEndpointURL,
                        vAsync, vUser, vPwd);

   hr = spXMLHTTP->setRequestHeader(_T("Content-Type"),
                                    _T("text/xml"));
   hr = spXMLHTTP->setRequestHeader(_T("SOAPAction"),
                                    _T("'http://tempuri/UserLogin'"));

   COleVariant vBody(g_lpszSOAPReq);
   hr = spXMLHTTP->send(vBody);

   long nStatus = 0;
   hr = spXMLHTTP->get_status(&nStatus);

   if ( nStatus == 200 )
   {
      CComQIPtr<IXMLDOMDocument,&__uuidof(IXMLDOMDocument)>
         spResponseXMLDoc;
         hr = spXMLHTTP->get_responseXML((IDispatch**)
                                         &spResponseXMLDoc);

      CComQIPtr<IXMLDOMNode,&__uuidof(IXMLDOMDocument)> spNode;
      hr = spResponseXMLDoc->
           selectSingleNode(_T("some XPath query"),&spNode);
      if( spNode != NULL )
      {
         CComBSTR bstrText;
         hr = spNode->get_text(&bstrText);
         TRACE(L"Text is %sn",bstrText);
      }
      ...
   }
}

In comparison with the previous sample, all you have changed is just a couple of lines marked in red. The request is opened with the “POST” method. Then, the sample code defines the values of the “Content-Type” and “SOAPAction” headers and request body. After receiving a successful response, data can be handled as needed.

Conclusion

I have discussed several simplified samples that give you a clue for implementing your own XMLHTTP-based modules. The usage of XMHTTP is covenient enough, so you can support it without too serious a headache. If you are not required to provide a sophisticated solution, it may be the best way to communicate with a remote HTTP host. Good luck!

About the Author

Alex Gusev started to play with mainframes in the end of the 1980s, using Pascal and REXX, but soon switched to C/C++ and Java on different platforms. When mobile PDAs seriously rose their heads in the IT market, Alex did it too. Now, he works at an international retail software company as a team leader of the Mobile R department, making programmers’ lives in the mobile jungles a little bit simpler.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories