开发SmartPhone应用程序以及调用WebService

Orange SPV是第一款进入市场的运行Smartphone 2002的移动电话,现在我们可以开始开发Smartphone应用程序了。本文我们建立了一个可以使用ASP.NET Web服务的Smartphone客户端来提供地理信息。本文假定读者有Web服务、ASP.NET和Win32编程的基本知识。

.NET框架组件使我们有了一组类帮助使用Web服务,从分析WSDL文档到自动建立代理类再到程序依赖。这使大多数依靠Web服务的编程比较琐碎,可是对于Smartphone来说简单框架组件(Compact Framework)还不能使用,因此我们需要作其它的选择。

典型的COM能够使用Soap工具包,对Smartphone来说它也不能使用,但是我们将使用它来帮助跟踪调用。

我们可以依靠Web服务编程仍然不使用这些技术,为了实现它,我们需要查看Web服务的格式和传送。在.NET 1.0 Web服务中使用两种标准来通讯:SOAP和WSDL。

Web服务基础


如果没有基于SOAP的API帮助使用Web服务,我们就需要直接处理SOAP消息和WSDL文档指定的格式。请注意使用WSDL使用的好处之一是类型元素中的概要信息。这些信息指定了SOAP正文的格式,启动WSDL察觉客户端(例如.NET)来定义SOAP包可以进行串并转换的类。

对于Smartphone来说,我们必须构造自己的SOAP消息以确保它们可以被Web服务接受。这些消息通过HTTP POST发送到服务器,我们的客户端等待SOAP响应。这需要按次序分析、检查soap错误,返回提取的值。看起来这需要大量的工作,但是在实际中它与Soap 工具包提供的低层API相近。

手动地从纯WSDL构造SOAP消息是复杂的事务(如map.wsdl显示)。这需要定位正确的操作元素,找到输入消息,映射正确类型。如果Web服务用的是ASP.NET,简单的途径是查看产生的asmx页,选择Webmethod,并查看SOAP请求示例(图1)。



图1 从ASP.NET产生的Web服务文档


如果你不能访问这些信息,有另一个机制用于支持WSDL的客户端调用Web服务,并跟踪SOAP包。SOAP工具包提供了一个工具Trace Utility(MsSoapT3.exe),它对调试Web服务很有用。



图2 Trace Utility,来自Soap工具包3.0


Smartphone Web服务


在上面的请求中很容易看出怎样构造SOAP消息。在这个例子中,我建立了一个SoapWriter类提供写SOAP消息的低层功能。

      
      
       
       #include "Soap.h"
SoapWriter *pSoap = new SoapWriter();
pSoap->StartEnvelope();
pSoap->StartBody();
pSoap->StartElement(L"GetLatLong", L"http://mapmobile");
pSoap->WriteElementString(L"addressLine", L"new bond street");
pSoap->WriteElementString(L"city", L"bath");
pSoap->StartElement(L"postCode");
pSoap->EndElement(L"postCode");
pSoap->WriteElementString(L"country", L"UK");
pSoap->EndElement(L"GetLatLong");
pSoap->EndBody();
pSoap->EndEnvelope();
pSoap->FinalizeSoap();
      
      


为了向Web服务发送SOAP请求,我将使用WinInet API。WinInet一般用于HTTP和FTP通讯。它为Internet访问提供了高层次API而没有采用WinSock 编程。幸运的是Web服务使用SOAP调用,在我们的例子中使用HTTP调用。

为了使发送SOAP请求更加容易,我建立了SoapConnector类,它允许我们发送SOAP请求和载入响应信息。一旦连接到了Web服务,客户端就可能作多个调用,使用存取程序检索SOAP响应信息。

      
      
       
       #include "Soap.h"
SoapWriter *pSoap = new SoapWriter();
//在此处建立SOAP 请求
SoapConnector *pCon = new SoapConnector();
pCon->Init();
//连接到服务器和Web服务
pCon->Connect(L"http://chungw02:8080/mapmobile/map.asmx");
//同SOAP 消息和SoapAction 一起调用
pCon->Invoke(pSoap, L"http://mapmobile/GetLatLong");
//提取响应信息
int iLen = 0;
pCon->GetSoapLength(&iLen);
TCHAR *pResponse = new TCHAR[iLen+1];
pCon->GetSoap(&pResponse);
      
      


客户端接收到的XML文件接着被载入DOM分析器并返回提取的值。本例中,我建立了一个将msxml中的IXMLDOMDocument接口暴露的包装类,这需要COM存在。

      
      
       
       #include "Soap.h"
//读取SOAP 响应信息
pSoapReader = new SoapReader();
pSoapReader->Init();
//通过传递SoapConnector或者Xml字符串载入SOAP响应信息
pSoapReader->LoadXml(pCon);
      
      


一旦它被载入了,可以通过m_pDom成员访问DOM,选择节点值或运行XPath查询。

      
      
       
       MSXML::IXMLDOMNode *pNode = NULL;
MSXML::IXMLDOMNodeList *pNodeList = NULL;
MSXML::IXMLDOMNode *pTextNode = NULL;
TCHAR *lpNodeValue = NULL;
TCHAR *XPath = new TCHAR[50];
_tcscpy(XPath, L"/soap:Envelope/soap:Body/Node"); 
VARIANT vNodeVal;
HRESULT hr;
//使用前面建立的pSoapReader
try {
   //选择节点
   hr = pSoapReader ->m_pDom->selectSingleNode(XPath, &pNode);
   if (FAILED(hr))
      __leave;
   if (pNode == NULL)
      __leave;
   //获取子节点
   hr = pNode->get_childNodes(&pNodeList);
   if (FAILED(hr))
      __leave;
   //获取下一个节点
   hr = pNodeList->get_item(0, &pTextNode);
   if (FAILED(hr))
      __leave;
   //获取文本节点值
   VariantInit(&vNodeVal);
   hr = pTextNode->get_nodeValue(&vNodeVal);
   if (FAILED(hr)) {
      VariantClear(&vNodeVal);
      __leave;
   }
//将值指定给lpNodeValue
   lpNodeValue = TCHAR[SysStringLen(vNodeVal.bstrVal) + 1];
   _tcscpy(lpNodeValue, vNodeVal.bstrVal);
   VariantClear(&vNodeVal);
   //使用lpNodeValue处理事务
}
__finally {
   if (pNode != NULL)
      pNode->Release();
   if (pNodeList != NULL)
      pNodeList->Release();
   if (pTextNode != NULL)
      pTextNode->Release();
   if (lpNodeValue !=NULL)
      delete[] lpNodeValue;   
}
      
      


作为选择,我提供了一个辅助方法SelectSingleTextNode来提取文本节点。它分配了TCHAR大小空间来适应文本节点的值。

      
      
       
       //注意这些有辅助方法SelectSingleTextNode分配 
TCHAR *latitude = NULL;
TCHAR *longitude = NULL;
//使用前面建立的pSoapReader
pSoapReader->SelectSingleTextNode(L"/soap:Envelope/soap:Body/GetLatLongResponse
/latitude", &latitude);
pSoapReader->SelectSingleTextNode(L"/soap:Envelope/soap:Body/GetLatLongResponse
/longitude", &longitude);
//使用返回值处理事务
//清除
if (latitude != NULL)
delete[] latitude;
if (longitude != NULL)
delete[] longitude;
      
      


即将调用的Web服务返回包含文本节点数据的SOAP消息,因此SelectSingleTextNode方法提供了从Web服务调用中提取数据的一个有用的机制。如果你经常需要提取数据的不同方式,例如属性数据和评估节点集合,SoapReader类可以扩充包含其它的辅助方法。
到此为止SoapWriter、SoapConnector和SoapReader提供了构造、调用和从Web服务读取响应信息的功能。为了用这些类实现客户端,我们需要了解SOAP消息的格式。下面将看到建立Web服务和Smartphone客户端。

MapMobile和MapPoint .NET Web服务


例程包含一个能从Web服务检索地图信息并在Smartphone上的显示的客户端。地理搜索和地图数据由MapPoint .NET提供,它是一个商用Web服务。尽管Smartphone终端用户并没有MapPoint .NET帐号,我建立了一个叫MapMobile的包装Web服务来依靠MapPoint .NET进行调用和认证。对于Smartphone 2002来说没有摘要(digest)认证提供者,因此如果我要直接与MapPoint .NET对话,需要手工实现认证头(header)。包装的其它原因是包含了改变地理数据提供者的能力,而不需要影响终端用户,例如从MapPoint .NET 2.0升级到3.0。



图3. MapMobile结构


我们的包装服务暴露了两个简单的Web服务方法,使客户端能够定位和显示地图。第一个调用返回地址的经度和纬度,第二个调用返回包含匹配的GIF图象的字节数组。

      
      
       
       [WebMethod]
[SoapHeader("_phoneNumber", Direction=SoapHeaderDirection.In)]
public void GetLatLong(string addressLine, string city, string postCode, string 
country, out double latitude, out double longitude)
[WebMethod]
[SoapHeader("_phoneNumber", Direction=SoapHeaderDirection.In)]            
public byte[] GetMap(double latitude, double longitude, double zoom, int width, 
int height, string tag)
      
      


为了防止MapMobile Web服务的无限制调用,所有的调用都包含一个SOAP头,它包含了Smartphone的电话号码。在服务器端,检测头信息是否为有效的数字。如果失败了,SOAP故障将返回客户端。这种认证机制只供例子使用,不禁止有效号码的欺骗。由于提供了很多安全选项,它们依靠你的Web服务,你可以选择不认证,使用数字署名,或使用新全局XML Web服务结构(Global XML Web Services Architecture,GXA)。

SmartMap客户端


客户端应用程序提供了一个输入屏幕用于输入地址。接着调用MapMobile Web服务并检索存储在文件系统中的地图。这使用户可以查看下载的最新地图。



图4. SmartMap用户界面


我们的客户端应用程序包含一个启动屏幕(主窗体类)和三个对话框(输入屏幕、Web服务调用状态和地图显示)。起初启动屏幕显示,跟着显示Find Location对话框。设备电话号码也被提取和保存,用于soap头中。在仿真程序中这些调用不会工作,因此在我们的例子中,如果调用失败就将数字设为1234567890以供测试。

      
      
       
       TCHAR *g_PhoneNumber = new TCHAR[40];
SMS_ADDRESS pAddr;
//获取电话号码
SmsGetPhoneNumber(&pAddr);
_tcscpy(g_PhoneNumber, pAddr.ptsAddress);
      
      


当用户选择Search菜单选项,应用程序为MapMobile Web服务构造正确的SOAP调用。这包括包含设备电话号码的SOAP头。

      
      
       
       SoapWriter *pSoap = new SoapWriter();
sw->StartEnvelope();
//构造头
sw->StartHeader();
sw->StartHeaderElement(L"MapMobileHeader", L"http://mapmobile");
sw->WriteHeaderElementString(L"PhoneNumber", g_PhoneNumber);
sw->EndHeaderElement(L"MapMobileHeader");
sw->EndHeader();
//Soap正文
sw->StartBody();
//此处构造SOAP正文
sw->EndBody();
sw->EndEnvelope(); 
sw->FinalizeSoap();
      
      


这完成后SoapConnector将被载入SoapReader中用于提取64位编码数据。

      
      
       
       TCHAR *map64;
//分配内存到目标变量
hrSearch = 
pSoapReader->SelectSingleTextNode(L"/soap:Envelope/soap:Body/GetMapResponse/GetMap
Result", &map64);
      
      


我们需要不断检查HRESULT,如果SoapReader失败了,就检查响应信息是否包含Soap故障。

      
      
       
       TCHAR &soapfault;
hrSearch = 
pSoapReader->SelectSingleTextNode(L"/soap:Envelope/soap:Body/soap:Fault
/faultstring", &soapfault);
if (SUCCEEDED(hrSearch))            
   MessageBox(hDlg, soapfault, L"Soap Error", MB_OK | MB_ICONWARNING);
else
   MessageBox(hDlg, L"Call failed", L"Error", MB_OK | MB_ICONWARNING);
      
      




图5. Soap故障传回客户端


如果所有调用成功完成,64位字符串将解码成为一个字符数组并作为SmartMap.gif保存。我们希望该文件在电话关闭也保存在My Documents文件夹中。但是固化存储器的文件结构可以被OEM的改变,因此调用SHGetSpecialFolderPath API来检索这些信息。

      
      
       
       CHAR szMapFile[MAX_PATH];
SHGetSpecialFolderPath(hwnd, szMapFile, CSIDL_PERSONAL , FALSE);
_tcscat(szMapFile, L"//SmartMap.gif");
//在Orange SPV上 szMapFile包含在 /IPSM/My Document/SmartMap.gif
//在模拟器上为: /My Document/SmartMap.gif
      
      


最后建立map对话框,它包含一个图象控件(IDC_IMAGE)。



图6.选择其它数据


当该对话框载入后,保存的地图图象将从文件系统中读入并且STM_SETIMAGE传递到对话框句柄。该图象使用IMGDECMP.dll图象库载入,它时CE 3.0平台的一部分。

      
      
       
       HBITMAP g_hbm = NULL;
ReadPictureFile(hDlg, szMapFile, & g_hbm);
SendDlgItemMessage(hDlg, IDC_IMAGE, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM) g_hbm);
      
      




图7.选择其它数据


这时SmartMap客户端几乎已经完成。如果可能的话,用户需要能查看最新地图。为了实现功能,使用了一个弹出菜单,在Find Location对话框的Option按钮中。如果文件存在就会捕获一个WM_INITMENUPOPUP消息,由此弹出菜单被激活或者禁止。

      
      
       
       //菜单弹出前调用
case WM_INITMENUPOPUP:
{
   //查看是否文件存在和是否激活最新地图菜单
   DWORD dwFile = GetFileAttributes(szMapFile);
   BOOL bEnabled = (dwFile == 0xFFFFFFFF ? FALSE : TRUE);
   EnableMenuItem((HMENU)wParam, ID_EXIT_LASTMAP, MF_BYCOMMAND | 
(bEnabled?MF_ENABLED:MF_GRAYED));
    break;
}
      
      


如果用户可以选择该菜单,接着map对话框将再次被建立,它从文件系统自动载入图象。

总结


我曾经查阅过Web服务、工具和技术所包含的内容来帮助查看SOAP包。使用提供的例程,我们看到了在Smartphone 2002上,在例子MapPoint .NET中使用Web服务的一条途径。通过建立Web服务察觉(service-aware)客户端,我们获得了工作于混合连接环境,连接和断开连接,合计和储存数据的移动设备上的新一代应用程序的好处,并提供了新的经验。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值